From c7c4bce012d624836f18511ebaa7ec37c0d2c77a Mon Sep 17 00:00:00 2001 From: lmuswere Date: Mon, 6 Jan 2020 09:25:23 +0000 Subject: [PATCH] Integration test for benchmark generator Signed-off-by: lmuswere --- .travis.yml | 2 + .travis/benchmark-integration-test-direct.sh | 1 + .../benchmark-integration-test-verdaccio.sh | 1 + packages/caliper-cli/caliper.js | 0 .../generator_tests/fabric/.gitignore | 4 + .../generator_tests/fabric/.yo-rc.json | 13 + .../generator_tests/fabric/caliper.yaml | 47 ++ .../generator_tests/fabric/config/.gitignore | 5 + .../fabric/config/configtx.yaml | 92 ++++ .../fabric/config/crypto-config.yaml | 36 ++ .../generator_tests/fabric/config/generate.sh | 20 + .../fabric/docker-compose.yaml | 262 +++++++++ .../fabric/myWorkspace/networkconfig.yaml | 143 +++++ .../generator_tests/fabric/run.sh | 78 +++ .../fabric/src/marbles/node/marbles.js | 509 ++++++++++++++++++ .../statedb/couchdb/indexes/indexOwner.json | 1 + .../fabric/src/marbles/node/package.json | 17 + .../generator_tests/run.sh | 21 + .../caliper-tests-integration/package.json | 9 +- .../caliper-tests-integration/run-tests.sh | 2 +- 20 files changed, 1260 insertions(+), 3 deletions(-) mode change 100644 => 100755 packages/caliper-cli/caliper.js create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/.gitignore create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/.yo-rc.json create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/caliper.yaml create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/config/.gitignore create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/config/configtx.yaml create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/config/crypto-config.yaml create mode 100755 packages/caliper-tests-integration/generator_tests/fabric/config/generate.sh create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/docker-compose.yaml create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/myWorkspace/networkconfig.yaml create mode 100755 packages/caliper-tests-integration/generator_tests/fabric/run.sh create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/marbles.js create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/metadata/statedb/couchdb/indexes/indexOwner.json create mode 100644 packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/package.json create mode 100755 packages/caliper-tests-integration/generator_tests/run.sh diff --git a/.travis.yml b/.travis.yml index 52e0460d8..0d74caef9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,6 +42,8 @@ jobs: script: BENCHMARK=besu ./.travis/benchmark-integration-test-direct.sh - name: FISCO-BCOS integration test script: BENCHMARK=fisco-bcos ./.travis/benchmark-integration-test-direct.sh + - name: Generator integration test + script: BENCHMARK=generator ./.travis/benchmark-integration-test-direct.sh # publish npm packages - stage: npm publish diff --git a/.travis/benchmark-integration-test-direct.sh b/.travis/benchmark-integration-test-direct.sh index 0b1c00c54..f3196e411 100755 --- a/.travis/benchmark-integration-test-direct.sh +++ b/.travis/benchmark-integration-test-direct.sh @@ -23,6 +23,7 @@ npm i && npm run repoclean -- --yes && npm run bootstrap # Call CLI directly # The CWD will be in one of the caliper-tests-integration/*_tests directories export CALL_METHOD="node ../../caliper-cli/caliper.js" +export GENERATOR_METHOD="yo ../../../caliper-generator/generator-caliper/generators/benchmark/index.js" echo "---- Running Integration test for adaptor ${BENCHMARK}" cd ./packages/caliper-tests-integration/ diff --git a/.travis/benchmark-integration-test-verdaccio.sh b/.travis/benchmark-integration-test-verdaccio.sh index 53f58af5d..527851804 100755 --- a/.travis/benchmark-integration-test-verdaccio.sh +++ b/.travis/benchmark-integration-test-verdaccio.sh @@ -22,6 +22,7 @@ npm i && npm run repoclean -- --yes && npm run bootstrap # Call CLI through the local binary export CALL_METHOD="npx caliper" +export GENERATOR_METHOD="yo caliper:benchmark" echo "---- Publishing packages locally" cd ./packages/caliper-tests-integration/ diff --git a/packages/caliper-cli/caliper.js b/packages/caliper-cli/caliper.js old mode 100644 new mode 100755 diff --git a/packages/caliper-tests-integration/generator_tests/fabric/.gitignore b/packages/caliper-tests-integration/generator_tests/fabric/.gitignore new file mode 100644 index 000000000..48cfc5f68 --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/.gitignore @@ -0,0 +1,4 @@ +**/*.log +report.html + +**/myWorkspace/benchmarks diff --git a/packages/caliper-tests-integration/generator_tests/fabric/.yo-rc.json b/packages/caliper-tests-integration/generator_tests/fabric/.yo-rc.json new file mode 100644 index 000000000..bdb41d7fc --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/.yo-rc.json @@ -0,0 +1,13 @@ +{ + "generator-caliper-benchmarks": { + "promptValues": { + "subgenerator": "benchmark" + }, + "workspace": {} + }, + "generator-caliper": { + "promptValues": { + "subgenerator": "benchmark" + } + } +} \ No newline at end of file diff --git a/packages/caliper-tests-integration/generator_tests/fabric/caliper.yaml b/packages/caliper-tests-integration/generator_tests/fabric/caliper.yaml new file mode 100644 index 000000000..bcd0e9228 --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/caliper.yaml @@ -0,0 +1,47 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +caliper: + benchconfig: benchconfig.yaml + networkconfig: networkconfig.yaml + report: + path: ../report.html + logging: + template: '%timestamp%%level%%module%%message%%metadata%' + formats: + timestamp: 'YYYY.MM.DD-HH:mm:ss.SSS ZZ' + label: false + json: false + pad: true + align: false + attributeformat: + level: ' %attribute%' + module: ' [%attribute%] ' + metadata: ' (%attribute%)' + colorize: + all: true + colors: + info: green + error: red + warn: yellow + debug: grey + targets: + console: + target: console + enabled: true + options: + level: debug + file: + target: file + enabled: false diff --git a/packages/caliper-tests-integration/generator_tests/fabric/config/.gitignore b/packages/caliper-tests-integration/generator_tests/fabric/config/.gitignore new file mode 100644 index 000000000..2bc6f264d --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/config/.gitignore @@ -0,0 +1,5 @@ +bin/* +config/* +crypto-config/* +genesis.block +mychannel.tx diff --git a/packages/caliper-tests-integration/generator_tests/fabric/config/configtx.yaml b/packages/caliper-tests-integration/generator_tests/fabric/config/configtx.yaml new file mode 100644 index 000000000..8ee584f0c --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/config/configtx.yaml @@ -0,0 +1,92 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +--- + +Organizations: +- &OrdererOrg + Name: OrdererMSP + ID: OrdererMSP + MSPDir: crypto-config/ordererOrganizations/example.com/msp + AdminPrincipal: Role.MEMBER + +- &Org1 + Name: Org1MSP + ID: Org1MSP + MSPDir: crypto-config/peerOrganizations/org1.example.com/msp + AdminPrincipal: Role.ADMIN + AnchorPeers: + - Host: peer0.org1.example.com + Port: 7051 + +- &Org2 + Name: Org2MSP + ID: Org2MSP + MSPDir: crypto-config/peerOrganizations/org2.example.com/msp + AdminPrincipal: Role.ADMIN + AnchorPeers: + - Host: peer0.org2.example.com + Port: 7051 + +Orderer: &OrdererDefaults + OrdererType: etcdraft + Addresses: + - orderer0.example.com:7050 + - orderer1.example.com:7050 + + BatchTimeout: 500ms + BatchSize: + MaxMessageCount: 50 + AbsoluteMaxBytes: 1 MB + PreferredMaxBytes: 1 MB + + MaxChannels: 0 + EtcdRaft: + Consenters: + - Host: orderer0.example.com + Port: 7050 + ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer0.example.com/tls/server.crt + ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer0.example.com/tls/server.crt + - Host: orderer1.example.com + Port: 7050 + ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.crt + ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.crt + + Organizations: + +Application: &ApplicationDefaults + Organizations: + +Profiles: + OrdererGenesis: + Orderer: + <<: *OrdererDefaults + Organizations: + - *OrdererOrg + Consortiums: + SampleConsortium: + Organizations: + - *Org1 + - *Org2 + SampleConsortium2: + Organizations: + - *Org1 + - *Org2 + ChannelConfig: + Consortium: SampleConsortium + Application: + <<: *ApplicationDefaults + Organizations: + - *Org1 + - *Org2 diff --git a/packages/caliper-tests-integration/generator_tests/fabric/config/crypto-config.yaml b/packages/caliper-tests-integration/generator_tests/fabric/config/crypto-config.yaml new file mode 100644 index 000000000..d66bf601d --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/config/crypto-config.yaml @@ -0,0 +1,36 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +OrdererOrgs: +- Name: Orderer + Domain: example.com + + Template: + Count: 2 + +PeerOrgs: +- Name: Org1 + Domain: org1.example.com + Template: + Count: 1 + Users: + Count: 1 + +- Name: Org2 + Domain: org2.example.com + Template: + Count: 1 + Users: + Count: 1 diff --git a/packages/caliper-tests-integration/generator_tests/fabric/config/generate.sh b/packages/caliper-tests-integration/generator_tests/fabric/config/generate.sh new file mode 100755 index 000000000..2a2e3f4cf --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/config/generate.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# if the binaries are not available, download them +if [[ ! -d "bin" ]]; then + curl -sSL http://bit.ly/2ysbOFE | bash -s -- 1.4.1 1.4.1 0.4.15 -ds +fi + +rm -rf ./crypto-config/ +rm -f ./genesis.block +rm -f ./mychannel.tx + +./bin/cryptogen generate --config=./crypto-config.yaml +./bin/configtxgen -profile OrdererGenesis -outputBlock genesis.block -channelID syschannel +./bin/configtxgen -profile ChannelConfig -outputCreateChannelTx mychannel.tx -channelID mychannel + +# Rename the key files we use to be key.pem instead of a uuid +for KEY in $(find crypto-config -type f -name "*_sk"); do + KEY_DIR=$(dirname ${KEY}) + mv ${KEY} ${KEY_DIR}/key.pem +done diff --git a/packages/caliper-tests-integration/generator_tests/fabric/docker-compose.yaml b/packages/caliper-tests-integration/generator_tests/fabric/docker-compose.yaml new file mode 100644 index 000000000..2e8a8595f --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/docker-compose.yaml @@ -0,0 +1,262 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: '3' + +volumes: + prometheus_data: {} + +services: + +####### +# CAs # +####### + + ca.org1.example.com: + image: hyperledger/fabric-ca:1.4.1 + environment: + - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server + - FABRIC_CA_SERVER_CA_NAME=ca.org1.example.com + - FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem + - FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/key.pem + # TLS + - FABRIC_CA_SERVER_TLS_ENABLED=true + - FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-tls/tlsca.org1.example.com-cert.pem + - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-tls/key.pem + ports: + - "7054:7054" + command: sh -c 'fabric-ca-server start -b admin:adminpw -d' + volumes: + - ./config/crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config + - ./config/crypto-config/peerOrganizations/org1.example.com/tlsca/:/etc/hyperledger/fabric-ca-server-tls + container_name: ca.org1.example.com + + ca.org2.example.com: + image: hyperledger/fabric-ca:1.4.1 + environment: + - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server + - FABRIC_CA_SERVER_CA_NAME=ca.org2.example.com + - FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem + - FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/key.pem + # TLS + - FABRIC_CA_SERVER_TLS_ENABLED=true + - FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-tls/tlsca.org2.example.com-cert.pem + - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-tls/key.pem + ports: + - "8054:7054" + command: sh -c 'fabric-ca-server start -b admin:adminpw -d' + volumes: + - ./config/crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/fabric-ca-server-config + - ./config/crypto-config/peerOrganizations/org2.example.com/tlsca/:/etc/hyperledger/fabric-ca-server-tls + container_name: ca.org2.example.com + +############ +# ORDERERS # +############ + + orderer0.example.com: + container_name: orderer0.example.com + image: hyperledger/fabric-orderer:1.4.1 + environment: + - FABRIC_LOGGING_SPEC=info + - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 + - ORDERER_GENERAL_GENESISMETHOD=file + - ORDERER_GENERAL_GENESISFILE=/etc/hyperledger/configtx/genesis.block + - ORDERER_GENERAL_LOCALMSPID=OrdererMSP + - ORDERER_GENERAL_LOCALMSPDIR=/etc/hyperledger/msp/orderer/msp + # TLS + - ORDERER_GENERAL_TLS_ENABLED=true + - ORDERER_GENERAL_TLS_PRIVATEKEY=/etc/hyperledger/msp/orderer/tls/server.key + - ORDERER_GENERAL_TLS_CERTIFICATE=/etc/hyperledger/msp/orderer/tls/server.crt + - ORDERER_GENERAL_TLS_ROOTCAS=[/etc/hyperledger/msp/orderer/tls/ca.crt] + # Mutual TLS + - ORDERER_GENERAL_TLS_CLIENTAUTHREQUIRED=true + - ORDERER_GENERAL_TLS_CLIENTROOTCAS=[/etc/hyperledger/msp/caOrg1/ca.org1.example.com-cert.pem, /etc/hyperledger/msp/caOrg2/ca.org2.example.com-cert.pem, /etc/hyperledger/msp/caOrderer/ca.example.com-cert.pem] + # Raft TLS + - ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=/etc/hyperledger/msp/orderer/tls/server.crt + - ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=/etc/hyperledger/msp/orderer/tls/server.key + # setting up metrics + - ORDERER_OPERATIONS_LISTENADDRESS=0.0.0.0:9000 + - ORDERER_OPERATIONS_TLS_ENABLED=false + - ORDERER_METRICS_ENABLE=true + - ORDERER_METRICS_PROVIDER=prometheus + working_dir: /opt/gopath/src/github.com/hyperledger/fabric + command: orderer + ports: + - 7050:7050 + volumes: + - ./config/:/etc/hyperledger/configtx + - ./config/crypto-config/ordererOrganizations/example.com/orderers/orderer0.example.com/:/etc/hyperledger/msp/orderer + - ./config/crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/msp/caOrg1 + - ./config/crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/msp/caOrg2 + - ./config/crypto-config/ordererOrganizations/example.com/ca/:/etc/hyperledger/msp/caOrderer + depends_on: + - ca.org1.example.com + - ca.org2.example.com + + orderer1.example.com: + container_name: orderer1.example.com + image: hyperledger/fabric-orderer:1.4.1 + environment: + - FABRIC_LOGGING_SPEC=info + - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 + - ORDERER_GENERAL_GENESISMETHOD=file + - ORDERER_GENERAL_GENESISFILE=/etc/hyperledger/configtx/genesis.block + - ORDERER_GENERAL_LOCALMSPID=OrdererMSP + - ORDERER_GENERAL_LOCALMSPDIR=/etc/hyperledger/msp/orderer/msp + # TLS + - ORDERER_GENERAL_TLS_ENABLED=true + - ORDERER_GENERAL_TLS_PRIVATEKEY=/etc/hyperledger/msp/orderer/tls/server.key + - ORDERER_GENERAL_TLS_CERTIFICATE=/etc/hyperledger/msp/orderer/tls/server.crt + - ORDERER_GENERAL_TLS_ROOTCAS=[/etc/hyperledger/msp/orderer/tls/ca.crt] + # Mutual TLS + - ORDERER_GENERAL_TLS_CLIENTAUTHREQUIRED=true + - ORDERER_GENERAL_TLS_CLIENTROOTCAS=[/etc/hyperledger/msp/caOrg1/ca.org1.example.com-cert.pem, /etc/hyperledger/msp/caOrg2/ca.org2.example.com-cert.pem, /etc/hyperledger/msp/caOrderer/ca.example.com-cert.pem] + # Raft TLS + - ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=/etc/hyperledger/msp/orderer/tls/server.crt + - ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=/etc/hyperledger/msp/orderer/tls/server.key + # setting up metrics + - ORDERER_OPERATIONS_LISTENADDRESS=0.0.0.0:9000 + - ORDERER_OPERATIONS_TLS_ENABLED=false + - ORDERER_METRICS_ENABLE=true + - ORDERER_METRICS_PROVIDER=prometheus + working_dir: /opt/gopath/src/github.com/hyperledger/fabric + command: orderer + ports: + - 8050:7050 + volumes: + - ./config/:/etc/hyperledger/configtx + - ./config/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/:/etc/hyperledger/msp/orderer + - ./config/crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/msp/caOrg1 + - ./config/crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/msp/caOrg2 + - ./config/crypto-config/ordererOrganizations/example.com/ca/:/etc/hyperledger/msp/caOrderer + depends_on: + - ca.org1.example.com + - ca.org2.example.com + +######### +# PEERS # +######### + + peer0.org1.example.com: + container_name: peer0.org1.example.com + image: hyperledger/fabric-peer:1.4.1 + environment: + - FABRIC_LOGGING_SPEC=info + - CORE_CHAINCODE_LOGGING_LEVEL=INFO + - CORE_CHAINCODE_LOGGING_SHIM=INFO + - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock + - CORE_PEER_ID=peer0.org1.example.com + - CORE_PEER_ENDORSER_ENABLED=true + - CORE_PEER_LOCALMSPID=Org1MSP + - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/peer/msp/ + - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 + - CORE_PEER_GOSSIP_USELEADERELECTION=true + - CORE_PEER_GOSSIP_ORGLEADER=false + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051 + - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=caliper_default + # CouchDB + - CORE_LEDGER_STATE_STATEDATABASE=CouchDB + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb.peer0.org1.example.com:5984 + # TLS + - CORE_PEER_TLS_ENABLED=true + - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/msp/peer/tls/server.key + - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/msp/peer/tls/server.crt + - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/msp/peer/tls/ca.crt + # Mutual TLS + - CORE_PEER_TLS_CLIENTAUTHREQUIRED=true + - CORE_PEER_TLS_CLIENTROOTCAS_FILES=/etc/hyperledger/msp/caOrg1/ca.org1.example.com-cert.pem /etc/hyperledger/msp/caOrg2/ca.org2.example.com-cert.pem /etc/hyperledger/msp/caOrderer/ca.example.com-cert.pem + # setting up metrics + - CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9000 + - CORE_OPERATIONS_TLS_ENABLED=false + - CORE_METRICS_ENABLE=true + - CORE_METRICS_PROVIDER=prometheus + working_dir: /opt/gopath/src/github.com/hyperledger/fabric + command: peer node start + ports: + - 7051:7051 + volumes: + - /var/run/:/host/var/run/ + - ./config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/:/etc/hyperledger/msp/peer + - ./config/crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/msp/caOrg1 + - ./config/crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/msp/caOrg2 + - ./config/crypto-config/ordererOrganizations/example.com/ca/:/etc/hyperledger/msp/caOrderer + depends_on: + - orderer0.example.com + - orderer1.example.com + - couchdb.peer0.org1.example.com + + couchdb.peer0.org1.example.com: + container_name: couchdb.peer0.org1.example.com + image: hyperledger/fabric-couchdb:0.4.14 + ports: + - 5984:5984 + environment: + DB_URL: http://localhost:5984/member_db + + peer0.org2.example.com: + container_name: peer0.org2.example.com + image: hyperledger/fabric-peer:1.4.1 + environment: + - FABRIC_LOGGING_SPEC=info + - CORE_CHAINCODE_LOGGING_LEVEL=INFO + - CORE_CHAINCODE_LOGGING_SHIM=INFO + - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock + - CORE_PEER_ID=peer0.org2.example.com + - CORE_PEER_ENDORSER_ENABLED=true + - CORE_PEER_LOCALMSPID=Org2MSP + - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/peer/msp/ + - CORE_PEER_ADDRESS=peer0.org2.example.com:7051 + - CORE_PEER_GOSSIP_USELEADERELECTION=true + - CORE_PEER_GOSSIP_ORGLEADER=false + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org2.example.com:7051 + - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=caliper_default + # CouchDB + - CORE_LEDGER_STATE_STATEDATABASE=CouchDB + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb.peer0.org2.example.com:5984 + # TLS + - CORE_PEER_TLS_ENABLED=true + - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/msp/peer/tls/server.key + - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/msp/peer/tls/server.crt + - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/msp/peer/tls/ca.crt + # Mutual TLS + - CORE_PEER_TLS_CLIENTAUTHREQUIRED=true + - CORE_PEER_TLS_CLIENTROOTCAS_FILES=/etc/hyperledger/msp/caOrg1/ca.org1.example.com-cert.pem /etc/hyperledger/msp/caOrg2/ca.org2.example.com-cert.pem /etc/hyperledger/msp/caOrderer/ca.example.com-cert.pem + # setting up metrics + - CORE_OPERATIONS_LISTENADDRESS=0.0.0.0:9000 + - CORE_OPERATIONS_TLS_ENABLED=false + - CORE_METRICS_ENABLE=true + - CORE_METRICS_PROVIDER=prometheus + working_dir: /opt/gopath/src/github.com/hyperledger/fabric + command: peer node start + ports: + - 8051:7051 + volumes: + - /var/run/:/host/var/run/ + - ./config/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/:/etc/hyperledger/msp/peer + - ./config/crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/msp/caOrg1 + - ./config/crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/msp/caOrg2 + - ./config/crypto-config/ordererOrganizations/example.com/ca/:/etc/hyperledger/msp/caOrderer + depends_on: + - orderer0.example.com + - orderer1.example.com + - couchdb.peer0.org2.example.com + + couchdb.peer0.org2.example.com: + container_name: couchdb.peer0.org2.example.com + image: hyperledger/fabric-couchdb:0.4.14 + ports: + - 6984:5984 + environment: + DB_URL: http://localhost:5984/member_db diff --git a/packages/caliper-tests-integration/generator_tests/fabric/myWorkspace/networkconfig.yaml b/packages/caliper-tests-integration/generator_tests/fabric/myWorkspace/networkconfig.yaml new file mode 100644 index 000000000..10ab49c8e --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/myWorkspace/networkconfig.yaml @@ -0,0 +1,143 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Fabric +version: "1.0" +mutual-tls: true + +caliper: + blockchain: fabric + command: + start: docker-compose -p caliper up -d;rm -rf /tmp/hfc-*;sleep 5s + end: docker-compose -p caliper down;(test -z \"$(docker ps -aq)\") || docker rm $(docker ps -aq);(test -z \"$(docker images dev* -q)\") || docker rmi $(docker images dev* -q);rm -rf /tmp/hfc-* + +clients: + client0.org1.example.com: + client: + organization: Org1 + credentialStore: + path: /tmp/hfc-kvs/org1 + cryptoStore: + path: /tmp/hfc-cvs/org1 + clientPrivateKey: + path: ../config/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/key.pem + clientSignedCert: + path: ../config/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem + + client0.org2.example.com: + client: + organization: Org2 + credentialStore: + path: /tmp/hfc-kvs/org2 + cryptoStore: + path: /tmp/hfc-cvs/org2 + clientPrivateKey: + path: ../config/crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/keystore/key.pem + clientSignedCert: + path: ../config/crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/signcerts/User1@org2.example.com-cert.pem + +channels: + mychannel: + created: false + configBinary: ../config/mychannel.tx + orderers: + - orderer0.example.com + - orderer1.example.com + peers: + peer0.org1.example.com: + eventSource: true + peer0.org2.example.com: + eventSource: true + chaincodes: + - id: marbles + contractID: mymarbles + version: v0 + language: node + path: ../src/marbles/node + metadataPath: ../src/marbles/node/metadata + +organizations: + Org1: + mspid: Org1MSP + peers: + - peer0.org1.example.com + certificateAuthorities: + - ca.org1.example.com + adminPrivateKey: + path: ../config/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/key.pem + signedCert: + path: ../config/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem + + Org2: + mspid: Org2MSP + peers: + - peer0.org2.example.com + certificateAuthorities: + - ca.org2.example.com + adminPrivateKey: + path: ../config/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore/key.pem + signedCert: + path: ../config/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts/Admin@org2.example.com-cert.pem + +orderers: + orderer0.example.com: + url: grpcs://localhost:7050 + grpcOptions: + ssl-target-name-override: orderer0.example.com + tlsCACerts: + path: ../config/crypto-config/ordererOrganizations/example.com/orderers/orderer0.example.com/msp/tlscacerts/tlsca.example.com-cert.pem + orderer1.example.com: + url: grpcs://localhost:8050 + grpcOptions: + ssl-target-name-override: orderer1.example.com + tlsCACerts: + path: ../config/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/msp/tlscacerts/tlsca.example.com-cert.pem + +peers: + peer0.org1.example.com: + url: grpcs://localhost:7051 + grpcOptions: + ssl-target-name-override: peer0.org1.example.com + grpc.keepalive_time_ms: 600000 + tlsCACerts: + path: ../config/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/tlscacerts/tlsca.org1.example.com-cert.pem + + peer0.org2.example.com: + url: grpcs://localhost:8051 + grpcOptions: + ssl-target-name-override: peer0.org2.example.com + grpc.keepalive_time_ms: 600000 + tlsCACerts: + path: ../config/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp/tlscacerts/tlsca.org2.example.com-cert.pem + +certificateAuthorities: + ca.org1.example.com: + url: https://localhost:7054 + httpOptions: + verify: false + tlsCACerts: + path: ../config/crypto-config/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem + registrar: + - enrollId: admin + enrollSecret: adminpw + + ca.org2.example.com: + url: https://localhost:8054 + httpOptions: + verify: false + tlsCACerts: + path: ../config/crypto-config/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem + registrar: + - enrollId: admin + enrollSecret: adminpw diff --git a/packages/caliper-tests-integration/generator_tests/fabric/run.sh b/packages/caliper-tests-integration/generator_tests/fabric/run.sh new file mode 100755 index 000000000..59ee83020 --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/run.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Print all commands. +set -v + +# Grab the parent (generator_tests) directory. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "${DIR}" + +# change default settings (add config paths too) +export CALIPER_PROJECTCONFIG=../caliper.yaml + +dispose () { + ${CALL_METHOD} benchmark run --caliper-workspace 'fabric/myWorkspace' --caliper-flow-only-end +} + +# Install yo +npm install --global yo + +# generate the crypto materials +cd ./config +./generate.sh + +# back to this dir +cd ${DIR} + +# Run benchmark generator using generator defaults +${GENERATOR_METHOD} -- --workspace 'myWorkspace' --chaincodeId 'mymarbles' --version 'v0' --chaincodeFunction 'queryMarblesByOwner' --chaincodeArguments '["Alice"]' --clients 'marbles' --benchmarkName 'A name for the marbles benchmark' --benchmarkDescription 'A description for the marbles benchmark' --label 'A label for the round' --rateController 'fixed-rate' --txType 'txDuration' --txDuration 'marbles' +# start network and run benchmark test +cd ../ +${CALL_METHOD} benchmark run --caliper-workspace 'fabric/myWorkspace' --caliper-networkconfig 'networkconfig.yaml' --caliper-benchconfig 'benchmarks/config.yaml' --caliper-flow-skip-end +rc=$? +if [[ ${rc} != 0 ]]; then + echo "Failed start network"; + rm -r fabric/myWorkspace/benchmarks + dispose; + exit ${rc}; +fi + +cd ${DIR} +# Run benchmark generator not using generator defaults +rm -r myWorkspace/benchmarks +${GENERATOR_METHOD} -- --workspace 'myWorkspace' --chaincodeId 'mymarbles' --version 'v0' --chaincodeFunction 'queryMarblesByOwner' --chaincodeArguments '["Alice"]' --clients 3 --benchmarkName 'A name for the marbles benchmark' --benchmarkDescription 'A description for the marbles benchmark' --label 'A label for the round' --rateController 'fixed-rate' --txType 'txDuration' --txDuration 30 + +# Run benchmark test +cd ../ +${CALL_METHOD} benchmark run --caliper-workspace 'fabric/myWorkspace' --caliper-networkconfig 'networkconfig.yaml' --caliper-benchconfig 'benchmarks/config.yaml' --caliper-flow-only-test +rc=$? +if [[ ${rc} != 0 ]]; then + echo "Failed run benchmark"; + rm -r fabric/myWorkspace/benchmarks + dispose; + exit ${rc}; +fi + +# dispose network +${CALL_METHOD} benchmark run --caliper-workspace 'fabric/myWorkspace' --caliper-networkconfig 'networkconfig.yaml' --caliper-benchconfig 'benchmarks/config.yaml' --caliper-flow-only-end +rc=$? +if [[ ${rc} != 0 ]]; then + echo "Failed end network"; + rm -r fabric/myWorkspace/benchmarks + exit ${rc}; +fi +cd ${DIR} +rm -r myWorkspace/benchmarks diff --git a/packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/marbles.js b/packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/marbles.js new file mode 100644 index 000000000..f36228a19 --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/marbles.js @@ -0,0 +1,509 @@ +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/* +* NOTE: This implementation is a derivative work of the following: +* https://github.com/hyperledger/fabric-samples/blob/release-1.1/chaincode/marbles02/node/marbles_chaincode.js +* The modifications include: bug fixes and refactoring for eslint compliance. +*/ + +/* eslint-disable no-console */ + +'use strict'; +const shim = require('fabric-shim'); +const util = require('util'); + +/** + * Marble asset management chaincode written in node.js, implementing {@link ChaincodeInterface}. + * @type {SimpleChaincode} + * @extends {ChaincodeInterface} + */ +let Chaincode = class { + /** + * Called during chaincode instantiate and upgrade. This method can be used + * to initialize asset states. + * @async + * @param {ChaincodeStub} stub The chaincode stub is implemented by the fabric-shim + * library and passed to the {@link ChaincodeInterface} calls by the Hyperledger Fabric platform. The stub + * encapsulates the APIs between the chaincode implementation and the Fabric peer. + * @return {Promise} Returns a promise of a response indicating the result of the invocation. + */ + async Init(stub) { + const marbles = [ + { + name: 'marble1', + color: 'Green', + size: '5', + owner: 'Alice' + }, + { + name: 'marble2', + color: 'Yellow', + size: '15', + owner: 'Bob' + }, + { + name: 'marble3', + color: 'Blue', + size: '10', + owner: 'Tom' + } + ] + for (let i=0; i < marbles.length; i++) { + marbles[i].docType = 'marble'; + await stub.putState(marbles[i].name, Buffer.from(JSON.stringify(marbles[i]))); + console.info('Added <--> ', marbles[i]); + } + console.info('=========== Instantiated Marbles Chaincode ==========='); + return shim.success(); + } + + /** + * Called throughout the life time of the chaincode to carry out business + * transaction logic and effect the asset states. + * The provided functions are the following: initMarble, delete, transferMarble, readMarble, getMarblesByRange, + * transferMarblesBasedOnColor, queryMarblesByOwner, queryMarbles, getHistoryForMarble. + * @async + * @param {ChaincodeStub} stub The chaincode stub is implemented by the fabric-shim + * library and passed to the {@link ChaincodeInterface} calls by the Hyperledger Fabric platform. The stub + * encapsulates the APIs between the chaincode implementation and the Fabric peer. + * @return {Promise} Returns a promise of a response indicating the result of the invocation. + */ + async Invoke(stub) { + console.info('Transaction ID: ' + stub.getTxID()); + console.info(util.format('Args: %j', stub.getArgs())); + + let ret = stub.getFunctionAndParameters(); + console.info(ret); + + let method = this[ret.fcn]; + if (!method) { + console.log('no function of name:' + ret.fcn + ' found'); + throw new Error('Received unknown function ' + ret.fcn + ' invocation'); + } + try { + let payload = await method(stub, ret.params, this); + return shim.success(payload); + } catch (err) { + console.log(err); + return shim.error(err); + } + } + + /** + * Creates a new marble with the given attributes. + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String[]} args The arguments of the function. Index 0: marble name. Index 1: marble color. + * Index 2: marble size. Index 3: marble owner. + */ + async initMarble(stub, args) { + if (args.length !== 4) { + throw new Error('Incorrect number of arguments. Expecting 4'); + } + // ==== Input sanitation ==== + console.info('--- start init marble ---'); + if (args[0].length <= 0) { + throw new Error('1st argument must be a non-empty string'); + } + if (args[1].length <= 0) { + throw new Error('2nd argument must be a non-empty string'); + } + if (args[2].length <= 0) { + throw new Error('3rd argument must be a non-empty string'); + } + if (args[3].length <= 0) { + throw new Error('4th argument must be a non-empty string'); + } + let marbleName = args[0]; + let color = args[1].toLowerCase(); + let owner = args[3].toLowerCase(); + let size = parseInt(args[2]); + if (isNaN(size)) { + throw new Error('3rd argument must be a numeric string'); + } + + // ==== Check if marble already exists ==== + let marbleState = await stub.getState(marbleName); + if (marbleState.toString()) { + throw new Error('This marble already exists: ' + marbleName); + } + + // ==== Create marble object and marshal to JSON ==== + let marble = {}; + marble.docType = 'marble'; + marble.name = marbleName; + marble.color = color; + marble.size = size; + marble.owner = owner; + + // === Save marble to state === + await stub.putState(marbleName, Buffer.from(JSON.stringify(marble))); + let indexName = 'color~name'; + let colorNameIndexKey = await stub.createCompositeKey(indexName, [marble.color, marble.name]); + console.info(colorNameIndexKey); + // Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble. + // Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value + await stub.putState(colorNameIndexKey, Buffer.from('\u0000')); + // ==== Marble saved and indexed. Return success ==== + console.info('- end init marble'); + } + + /** + * Retrieves the information about a marble. + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String[]} args The arguments of the function. Index 0: marble name. + * @return {Promise} The byte representation of the marble. + */ + async readMarble(stub, args) { + if (args.length !== 1) { + throw new Error('Incorrect number of arguments. Expecting name of the marble to query'); + } + + let name = args[0]; + if (!name) { + throw new Error(' marble name must not be empty'); + } + let marbleAsBytes = await stub.getState(name); //get the marble from chaincode state + if (!marbleAsBytes.toString()) { + let jsonResp = {}; + jsonResp.Error = 'Marble does not exist: ' + name; + throw new Error(JSON.stringify(jsonResp)); + } + console.info('======================================='); + console.log(marbleAsBytes.toString()); + console.info('======================================='); + return marbleAsBytes; + } + + /** + * Deletes the given marble. + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String[]} args The arguments of the function. Index 0: marble name. + */ + async delete(stub, args) { + if (args.length !== 1) { + throw new Error('Incorrect number of arguments. Expecting name of the marble to delete'); + } + let marbleName = args[0]; + if (!marbleName) { + throw new Error('marble name must not be empty'); + } + // to maintain the color~name index, we need to read the marble first and get its color + let valAsBytes = await stub.getState(marbleName); //get the marble from chaincode state + let jsonResp = {}; + if (!valAsBytes) { + jsonResp.error = 'marble does not exist: ' + marbleName; + throw new Error(jsonResp); + } + let marbleJSON = {}; + try { + marbleJSON = JSON.parse(valAsBytes.toString()); + } catch (err) { + jsonResp = {}; + jsonResp.error = 'Failed to decode JSON of: ' + marbleName; + throw new Error(jsonResp); + } + + await stub.deleteState(marbleName); //remove the marble from chaincode state + + // delete the index + let indexName = 'color~name'; + let colorNameIndexKey = stub.createCompositeKey(indexName, [marbleJSON.color, marbleJSON.name]); + if (!colorNameIndexKey) { + throw new Error(' Failed to create the createCompositeKey'); + } + // Delete index entry to state. + await stub.deleteState(colorNameIndexKey); + } + + // =========================================================== + // transfer a marble by setting a new owner name on the marble + // =========================================================== + /** + * Transfers the given marble to a new owner. + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String[]} args The arguments of the function. Index 0: marble name. Index 1: the new owner. + */ + async transferMarble(stub, args) { + if (args.length !== 2) { + throw new Error('Incorrect number of arguments. Expecting marble name and owner'); + } + + let marbleName = args[0]; + let newOwner = args[1].toLowerCase(); + console.info('- start transferMarble ', marbleName, newOwner); + + let marbleAsBytes = await stub.getState(marbleName); + if (!marbleAsBytes || !marbleAsBytes.toString()) { + throw new Error('marble does not exist'); + } + let marbleToTransfer = {}; + try { + marbleToTransfer = JSON.parse(marbleAsBytes.toString()); //unmarshal + } catch (err) { + let jsonResp = {}; + jsonResp.error = 'Failed to decode JSON of: ' + marbleName; + throw new Error(jsonResp); + } + console.info(marbleToTransfer); + marbleToTransfer.owner = newOwner; //change the owner + + let marbleJSONasBytes = Buffer.from(JSON.stringify(marbleToTransfer)); + await stub.putState(marbleName, marbleJSONasBytes); //rewrite the marble + + console.info('- end transferMarble (success)'); + } + + /** + * Performs a range query based on the start and end keys provided. + * + * Read-only function results are not typically submitted to ordering. If the read-only + * results are submitted to ordering, or if the query is used in an update transaction + * and submitted to ordering, then the committing peers will re-execute to guarantee that + * result sets are stable between endorsement time and commit time. The transaction is + * invalidated by the committing peers if the result set has changed between endorsement + * time and commit time. + * Therefore, range queries are a safe option for performing update transactions based on query results. + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String[]} args The arguments of the function. Index 0: start key. Index 1: end key. + * @param {Chaincode} thisObject The chaincode object context. + * @return {Promise} The marbles in the given range. + */ + async getMarblesByRange(stub, args, thisObject) { + + if (args.length !== 2) { + throw new Error('Incorrect number of arguments. Expecting 2'); + } + + let startKey = args[0]; + let endKey = args[1]; + + let resultsIterator = await stub.getStateByRange(startKey, endKey); + let results = await thisObject.getAllResults(resultsIterator, false); + + return Buffer.from(JSON.stringify(results)); + } + + /** + * Transfers marbles of a given color to a certain new owner. + * + * Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'. + * Committing peers will re-execute range queries to guarantee that result sets are stable + * between endorsement time and commit time. The transaction is invalidated by the + * committing peers if the result set has changed between endorsement time and commit time. + * Therefore, range queries are a safe option for performing update transactions based on query results. + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String[]} args The arguments of the function. Index 0: marble color. Index 1: new owner. + * @param {Chaincode} thisObject The chaincode object context. + */ + async transferMarblesBasedOnColor(stub, args, thisObject) { + if (args.length !== 2) { + throw new Error('Incorrect number of arguments. Expecting color and owner'); + } + + let color = args[0]; + let newOwner = args[1].toLowerCase(); + console.info('- start transferMarblesBasedOnColor ', color, newOwner); + + // Query the color~name index by color + // This will execute a key range query on all keys starting with 'color' + let coloredMarbleResultsIterator = await stub.getStateByPartialCompositeKey('color~name', [color]); + + let hasNext = true; + // Iterate through result set and for each marble found, transfer to newOwner + while (hasNext) { + let responseRange; + try { + responseRange = await coloredMarbleResultsIterator.next(); + } catch (err) { + hasNext = false; + continue; + } + + if (!responseRange || !responseRange.value || !responseRange.value.key) { + return; + } + console.log(responseRange.value.key); + + // let value = res.value.value.toString('utf8'); + let objectType; + let attributes; + ({ + objectType, + attributes + } = await stub.splitCompositeKey(responseRange.value.key)); + + let returnedColor = attributes[0]; + let returnedMarbleName = attributes[1]; + console.info(util.format('- found a marble from index:%s color:%s name:%s\n', objectType, returnedColor, returnedMarbleName)); + + // Now call the transfer function for the found marble. + // Re-use the same function that is used to transfer individual marbles + await thisObject.transferMarble(stub, [returnedMarbleName, newOwner]); + } + + let responsePayload = util.format('Transferred %s marbles to %s', color, newOwner); + console.info('- end transferMarblesBasedOnColor: ' + responsePayload); + } + + /** + * Queries for marbles based on a passed in owner. + * This is an example of a parameterized query where the query logic is baked into the chaincode, + * and accepting a single query parameter (owner). + * Only available on state databases that support rich query (e.g. CouchDB) + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String[]} args The arguments of the function. Index 0: marble owner. + * @param {Chaincode} thisObject The chaincode object context. + * @return {Promise} The marbles of the specified owner. + */ + async queryMarblesByOwner(stub, args, thisObject) { + if (args.length !== 1) { + throw new Error('Incorrect number of arguments. Expecting owner name.'); + } + + let owner = args[0].toLowerCase(); + let queryString = {}; + queryString.selector = {}; + queryString.selector.docType = 'marble'; + queryString.selector.owner = owner; + return await thisObject.getQueryResultForQueryString(stub, JSON.stringify(queryString), thisObject); //shim.success(queryResults); + } + + /** + * Uses a query string to perform a query for marbles. + * Query string matching state database syntax is passed in and executed as is. + * Supports ad hoc queries that can be defined at runtime by the client. + * If this is not desired, follow the queryMarblesForOwner example for parameterized queries. + * Only available on state databases that support rich query (e.g. CouchDB) + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String[]} args The arguments of the function. Index 0: query string. + * @param {Chaincode} thisObject The chaincode object context. + * @return {Promise} The results of the specified query. + */ + async queryMarbles(stub, args, thisObject) { + if (args.length !== 1) { + throw new Error('Incorrect number of arguments. Expecting queryString'); + } + let queryString = args[0]; + if (!queryString) { + throw new Error('queryString must not be empty'); + } + + return await thisObject.getQueryResultForQueryString(stub, queryString, thisObject); + } + + /** + * Gets the results of a specified iterator. + * @async + * @param {Object} iterator The iterator to use. + * @param {Boolean} isHistory Specifies whether the iterator returns history entries or not. + * @return {Promise} The array of results in JSON format. + */ + async getAllResults(iterator, isHistory) { + let allResults = []; + let hasNext = true; + while (hasNext) { + let res; + try { + res = await iterator.next(); + } catch (err) { + hasNext = false; + continue; + } + + if (res.value && res.value.value.toString()) { + let jsonRes = {}; + console.log(res.value.value.toString('utf8')); + + if (isHistory && isHistory === true) { + jsonRes.TxId = res.value.tx_id; + jsonRes.Timestamp = res.value.timestamp; + jsonRes.IsDelete = res.value.is_delete.toString(); + try { + jsonRes.Value = JSON.parse(res.value.value.toString('utf8')); + } catch (err) { + console.log(err); + jsonRes.Value = res.value.value.toString('utf8'); + } + } else { + jsonRes.Key = res.value.key; + try { + jsonRes.Record = JSON.parse(res.value.value.toString('utf8')); + } catch (err) { + console.log(err); + jsonRes.Record = res.value.value.toString('utf8'); + } + } + allResults.push(jsonRes); + } + if (res.done) { + console.log('end of data'); + await iterator.close(); + console.info(allResults); + return allResults; + } + } + } + + /** + * Executes the provided query string. + * Result set is built and returned as a byte array containing the JSON results. + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String} queryString The query string to execute. + * @param {Chaincode} thisObject The chaincode object context. + * @return {Promise} The results of the specified query. + */ + async getQueryResultForQueryString(stub, queryString, thisObject) { + + console.info('- getQueryResultForQueryString queryString:\n' + queryString); + let resultsIterator = await stub.getQueryResult(queryString); + + let results = await thisObject.getAllResults(resultsIterator, false); + + return Buffer.from(JSON.stringify(results)); + } + + /** + * Retrieves the history for a marble. + * @async + * @param {ChaincodeStub} stub The chaincode stub. + * @param {String[]} args The arguments of the function. Index 0: marble name. + * @param {Chaincode} thisObject The chaincode object context. + * @return {Promise} The history entries of the specified marble. + */ + async getHistoryForMarble(stub, args, thisObject) { + + if (args.length !== 1) { + throw new Error('Incorrect number of arguments. Expecting 1'); + } + let marbleName = args[0]; + console.info('- start getHistoryForMarble: %s\n', marbleName); + + let resultsIterator = await stub.getHistoryForKey(marbleName); + let results = await thisObject.getAllResults(resultsIterator, true); + + return Buffer.from(JSON.stringify(results)); + } +}; + +shim.start(new Chaincode()); diff --git a/packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/metadata/statedb/couchdb/indexes/indexOwner.json b/packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/metadata/statedb/couchdb/indexes/indexOwner.json new file mode 100644 index 000000000..305f09044 --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/metadata/statedb/couchdb/indexes/indexOwner.json @@ -0,0 +1 @@ +{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} diff --git a/packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/package.json b/packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/package.json new file mode 100644 index 000000000..724fea100 --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/fabric/src/marbles/node/package.json @@ -0,0 +1,17 @@ +{ + "name": "marbles", + "version": "1.0.0", + "description": "marbles chaincode implemented in node.js", + "engines": { + "node": ">=8.4.0", + "npm": ">=5.3.0" + }, + "scripts": { + "start": "node marbles.js" + }, + "engine-strict": true, + "license": "Apache-2.0", + "dependencies": { + "fabric-shim": "~1.4.0" + } +} diff --git a/packages/caliper-tests-integration/generator_tests/run.sh b/packages/caliper-tests-integration/generator_tests/run.sh new file mode 100755 index 000000000..ca87f0507 --- /dev/null +++ b/packages/caliper-tests-integration/generator_tests/run.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Exit on first error, print all commands. +set -e +set -o pipefail + +# Generator tests for Hyperledger Fabric +generator_tests/fabric/run.sh diff --git a/packages/caliper-tests-integration/package.json b/packages/caliper-tests-integration/package.json index 4a999b6ae..fbf456a32 100644 --- a/packages/caliper-tests-integration/package.json +++ b/packages/caliper-tests-integration/package.json @@ -40,7 +40,9 @@ "@hyperledger/caliper-fabric": "0.3.0-unstable", "@hyperledger/caliper-fisco-bcos": "0.3.0-unstable", "@hyperledger/caliper-iroha": "0.3.0-unstable", - "@hyperledger/caliper-sawtooth": "0.3.0-unstable" + "@hyperledger/caliper-sawtooth": "0.3.0-unstable", + "yeoman-generator": "4.1.0", + "yosay": "^2.0.1" }, "license-check-and-add-config": { "folder": ".", @@ -72,7 +74,10 @@ "fisco-bcos_tests/config/node1", "fisco-bcos_tests/config/node2", "fisco-bcos_tests/config/node3", - "fisco-bcos_tests/config/sdk" + "fisco-bcos_tests/config/sdk", + "generator_tests/fabric/.gitignore", + "generator_tests/fabric/config/.gitignore" + ], "file_type_method": "EXCLUDE", "file_types": [ diff --git a/packages/caliper-tests-integration/run-tests.sh b/packages/caliper-tests-integration/run-tests.sh index 1de506c25..8106f22b0 100755 --- a/packages/caliper-tests-integration/run-tests.sh +++ b/packages/caliper-tests-integration/run-tests.sh @@ -19,7 +19,7 @@ set -o pipefail # Barf if we don't recognize this test adaptor. if [[ "${BENCHMARK}" = "" ]]; then - echo You must set BENCHMARK to one of the desired test adaptors 'besu|ethereum|fabric|fisco-bcos|sawtooth' + echo You must set BENCHMARK to one of the desired test adaptors 'besu|ethereum|fabric|fisco-bcos|generator|sawtooth' echo For example: echo export BENCHMARK=fabric exit 1