diff --git a/first-network/README.md b/first-network/README.md new file mode 100644 index 0000000000..36560704bf --- /dev/null +++ b/first-network/README.md @@ -0,0 +1,4 @@ +## Build Your First Network (BYFN) + +The directions for using this are documented in the Hyperledger Fabric +["Build Your First Network"](http://hyperledger-fabric.readthedocs.io/en/latest/build_network.html) tutorial. diff --git a/first-network/base/docker-compose-base.yaml b/first-network/base/docker-compose-base.yaml new file mode 100644 index 0000000000..7a97e7186d --- /dev/null +++ b/first-network/base/docker-compose-base.yaml @@ -0,0 +1,108 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +version: '2' + +services: + + orderer.example.com: + container_name: orderer.example.com + image: hyperledger/fabric-orderer + environment: + - ORDERER_GENERAL_LOGLEVEL=debug + - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 + - ORDERER_GENERAL_GENESISMETHOD=file + - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block + - ORDERER_GENERAL_LOCALMSPID=OrdererMSP + - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp + # enabled TLS + - ORDERER_GENERAL_TLS_ENABLED=true + - ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key + - ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt + - ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt] + working_dir: /opt/gopath/src/github.com/hyperledger/fabric + command: orderer + volumes: + - ../channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block + - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp + - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls + ports: + - 7050:7050 + + peer0.org1.example.com: + container_name: peer0.org1.example.com + extends: + file: peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer0.org1.example.com + - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/fabric/tls + ports: + - 7051:7051 + - 7053:7053 + + peer1.org1.example.com: + container_name: peer1.org1.example.com + extends: + file: peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer1.org1.example.com + - CORE_PEER_ADDRESS=peer1.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls:/etc/hyperledger/fabric/tls + + ports: + - 8051:7051 + - 8053:7053 + + peer0.org2.example.com: + container_name: peer0.org2.example.com + extends: + file: peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer0.org2.example.com + - CORE_PEER_ADDRESS=peer0.org2.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org2.example.com:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org2.example.com:7051 + - CORE_PEER_LOCALMSPID=Org2MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls:/etc/hyperledger/fabric/tls + ports: + - 9051:7051 + - 9053:7053 + + peer1.org2.example.com: + container_name: peer1.org2.example.com + extends: + file: peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer1.org2.example.com + - CORE_PEER_ADDRESS=peer1.org2.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org2.example.com:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org2.example.com:7051 + - CORE_PEER_LOCALMSPID=Org2MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls:/etc/hyperledger/fabric/tls + ports: + - 10051:7051 + - 10053:7053 diff --git a/first-network/base/peer-base.yaml b/first-network/base/peer-base.yaml new file mode 100644 index 0000000000..89ed7283a4 --- /dev/null +++ b/first-network/base/peer-base.yaml @@ -0,0 +1,26 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +version: '2' +services: + peer-base: + image: hyperledger/fabric-peer + environment: + - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock + # the following setting starts chaincode containers on the same + # bridge network as the peers + # https://docs.docker.com/compose/networking/ + - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=byfn_default + #- CORE_LOGGING_LEVEL=ERROR + - CORE_LOGGING_LEVEL=DEBUG + - CORE_PEER_TLS_ENABLED=true + - CORE_PEER_GOSSIP_USELEADERELECTION=true + - CORE_PEER_GOSSIP_ORGLEADER=false + - CORE_PEER_PROFILE_ENABLED=true + - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt + - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key + - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer + command: peer node start diff --git a/first-network/byfn.sh b/first-network/byfn.sh new file mode 100755 index 0000000000..b4a43b2c44 --- /dev/null +++ b/first-network/byfn.sh @@ -0,0 +1,342 @@ +#!/bin/bash + +# This script will orchestrate a sample end-to-end execution of the Hyperledger +# Fabric network. +# +# The end-to-end verification provisions a sample Fabric network consisting of +# two organizations, each maintaining two peers, and a “solo” ordering service. +# +# This verification makes use of two fundamental tools, which are necessary to +# create a functioning transactional network with digital signature validation +# and access control: +# +# * cryptogen - generates the x509 certificates used to identify and +# authenticate the various components in the network. +# * configtxgen - generates the requisite configuration artifacts for orderer +# bootstrap and channel creation. +# +# Each tool consumes a configuration yaml file, within which we specify the topology +# of our network (cryptogen) and the location of our certificates for various +# configuration operations (configtxgen). Once the tools have been successfully run, +# we are able to launch our network. More detail on the tools and the structure of +# the network will be provided later in this document. For now, let's get going... + +# prepending $PWD/../bin to PATH to ensure we are picking up the correct binaries +# this may be commented out to resolve installed version of tools if desired +export PATH=${PWD}/../bin:${PWD}:$PATH + +# Print the usage message +function printHelp () { + echo "Usage: " + echo " byfn.sh -m up|down|restart|generate [-c ] [-t ]" + echo " byfn.sh -h|--help (print this message)" + echo " -m - one of 'up', 'down', 'restart' or 'generate'" + echo " - 'up' - bring up the network with docker-compose up" + echo " - 'down' - bring up the network with docker-compose up" + echo " - 'restart' - bring up the network with docker-compose up" + echo " - 'generate' - generate required certificates and genesis block" + echo " -c - channel name to use (defaults to \"mychannel\")" + echo " -t - CLI timeout duration in microseconds (defaults to 10000)" + echo + echo "Typically, one would first generate the required certificates and " + echo "genesis block, then bring up the network. e.g.:" + echo + echo " byfn.sh -m generate -c " + echo " byfn.sh -m up -c " + echo " byfn.sh -m down -c " + echo + echo "Taking all defaults:" + echo " byfn.sh -m generate" + echo " byfn.sh -m up" + echo " byfn.sh -m down" +} + +# Ask user for confirmation to proceed +function askProceed () { + read -p "Continue (y/n)? " ans + case "$ans" in + y|Y ) + echo "proceeding ..." + ;; + n|N ) + echo "exiting..." + exit 1 + ;; + * ) + echo "invalid response" + proceed + ;; + esac +} + +# Obtain CONTAINER_IDS and remove them +# TODO Might want to make this optional - could clear other containers +function clearContainers () { + CONTAINER_IDS=$(docker ps -aq) + if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" == " " ]; then + echo "---- No containers available for deletion ----" + else + docker rm -f $CONTAINER_IDS + fi +} + +# Delete any images that were generated as a part of this setup +# specifically the following images are often left behind: +# TODO list generated image naming patterns +function removeUnwantedImages() { + DOCKER_IMAGE_IDS=$(docker images | grep "dev\|none\|test-vp\|peer[0-9]-" | awk '{print $3}') + if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" == " " ]; then + echo "---- No images available for deletion ----" + else + docker rmi -f $DOCKER_IMAGE_IDS + fi +} + +# Generate the needed certificates, the genesis block and start the network. +function networkUp () { + CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=$CLI_TIMEOUT docker-compose -f $COMPOSE_FILE up -d 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to start network" + docker logs -f cli + exit 1 + fi + docker logs -f cli +} + +# Tear down running network +function networkDown () { + docker-compose -f $COMPOSE_FILE down + # Don't remove containers, images, etc if restarting + if [ "$MODE" != "restart" ]; then + #Cleanup the chaincode containers + clearContainers + #Cleanup images + removeUnwantedImages + # remove orderer block and other channel configuration transactions and certs + rm -rf channel-artifacts/*.block channel-artifacts/*.tx crypto-config + # remove the docker-compose yaml file that was customized to the example + rm -f docker-compose-e2e.yaml + fi +} + +# Using docker-compose-e2e-template.yaml, replace constants with private key file names +# generated by the cryptogen tool and output a docker-compose.yaml specific to this +# configuration +function replacePrivateKey () { + # sed on MacOSX does not support -i flag with a null extension. We will use + # 't' for our back-up's extension and depete it at the end of the function + ARCH=`uname -s | grep Darwin` + if [ "$ARCH" == "Darwin" ]; then + OPTS="-it" + else + OPTS="-i" + fi + + # Copy the template to the file that will be modified to add the private key + cp docker-compose-e2e-template.yaml docker-compose-e2e.yaml + + # The next steps will replace the template's contents with the + # actual values of the private key file names for the two CAs. + CURRENT_DIR=$PWD + cd crypto-config/peerOrganizations/org1.example.com/ca/ + PRIV_KEY=$(ls *_sk) + cd $CURRENT_DIR + sed $OPTS "s/CA1_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml + cd crypto-config/peerOrganizations/org2.example.com/ca/ + PRIV_KEY=$(ls *_sk) + cd $CURRENT_DIR + sed $OPTS "s/CA2_PRIVATE_KEY/${PRIV_KEY}/g" docker-compose-e2e.yaml + # If MacOSX, remove the temporary backup of the docker-compose file + if [ "$ARCH" == "Darwin" ]; then + rm docker-compose-e2e.yamlt + fi +} + +# We will use the cryptogen tool to generate the cryptographic material (x509 certs) +# for our various network entities. The certificates are based on a standard PKI +# implementation where validation is achieved by reaching a common trust anchor. +# +# Cryptogen consumes a file - ``crypto-config.yaml`` - that contains the network +# topology and allows us to generate a library of certificates for both the +# Organizations and the components that belong to those Organizations. Each +# Organization is provisioned a unique root certificate (``ca-cert``), that binds +# specific components (peers and orderers) to that Org. Transactions and communications +# within Fabric are signed by an entity's private key (``keystore``), and then verified +# by means of a public key (``signcerts``). You will notice a "count" variable within +# this file. We use this to specify the number of peers per Organization; in our +# case it's two peers per Org. The rest of this template is extremely +# self-explanatory. +# +# After we run the tool, the certs will be parked in a folder titled ``crypto-config``. + +# Generates Org certs using cryptogen tool +function generateCerts (){ + which cryptogen + if [ "$?" -ne 0 ]; then + echo "cryptogen tool not found. exiting" + exit 1 + fi + echo + echo "##########################################################" + echo "##### Generate certificates using cryptogen tool #########" + echo "##########################################################" + + cryptogen generate --config=./crypto-config.yaml + if [ "$?" -ne 0 ]; then + echo "Failed to generate certificates..." + exit 1 + fi + echo +} + +# The `configtxgen tool is used to create four artifacts: orderer **bootstrap +# block**, fabric **channel configuration transaction**, and two **anchor +# peer transactions** - one for each Peer Org. +# +# The orderer block is the genesis block for the ordering service, and the +# channel transaction file is broadcast to the orderer at channel creation +# time. The anchor peer transactions, as the name might suggest, specify each +# Org's anchor peer on this channel. +# +# Configtxgen consumes a file - ``configtx.yaml`` - that contains the definitions +# for the sample network. There are three members - one Orderer Org (``OrdererOrg``) +# and two Peer Orgs (``Org1`` & ``Org2``) each managing and maintaining two peer nodes. +# This file also specifies a consortium - ``SampleConsortium`` - consisting of our +# two Peer Orgs. Pay specific attention to the "Profiles" section at the top of +# this file. You will notice that we have two unique headers. One for the orderer genesis +# block - ``TwoOrgsOrdererGenesis`` - and one for our channel - ``TwoOrgsChannel``. +# These headers are important, as we will pass them in as arguments when we create +# our artifacts. This file also contains two additional specifications that are worth +# noting. Firstly, we specify the anchor peers for each Peer Org +# (``peer0.org1.example.com`` & ``peer0.org2.example.com``). Secondly, we point to +# the location of the MSP directory for each member, in turn allowing us to store the +# root certificates for each Org in the orderer genesis block. This is a critical +# concept. Now any network entity communicating with the ordering service can have +# its digital signature verified. +# +# This function will generate the crypto material and our four configuration +# artifacts, and subsequently output these files into the ``channel-artifacts`` +# folder. +# +# If you receive the following warning, it can be safely ignored: +# +# [bccsp] GetDefault -> WARN 001 Before using BCCSP, please call InitFactories(). Falling back to bootBCCSP. +# +# You can ignore the logs regarding intermediate certs, we are not using them in +# this crypto implementation. + +# Generate orderer genesis block, channel configuration transaction and +# anchor peer update transactions +function generateChannelArtifacts() { + which configtxgen + if [ "$?" -ne 0 ]; then + echo "configtxgen tool not found. exiting" + exit 1 + fi + + echo "##########################################################" + echo "######### Generating Orderer Genesis block ##############" + echo "##########################################################" + # Note: For some unknown reason (at least for now) the block file can't be + # named orderer.genesis.block or the orderer will fail to launch! + configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block + if [ "$?" -ne 0 ]; then + echo "Failed to generate orderer genesis block..." + exit 1 + fi + echo + echo "#################################################################" + echo "### Generating channel configuration transaction 'channel.tx' ###" + echo "#################################################################" + configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME + if [ "$?" -ne 0 ]; then + echo "Failed to generate channel configuration transaction..." + exit 1 + fi + + echo + echo "#################################################################" + echo "####### Generating anchor peer update for Org1MSP ##########" + echo "#################################################################" + configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP + if [ "$?" -ne 0 ]; then + echo "Failed to generate anchor peer update for Org1MSP..." + exit 1 + fi + + echo + echo "#################################################################" + echo "####### Generating anchor peer update for Org2MSP ##########" + echo "#################################################################" + configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate \ + ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP + if [ "$?" -ne 0 ]; then + echo "Failed to generate anchor peer update for Org2MSP..." + exit 1 + fi + echo +} + +# Obtain the OS and Architecture string that will be used to select the correct +# native binaries for your platform +OS_ARCH=$(echo "$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')-$(uname -m | sed 's/x86_64/amd64/g')" | awk '{print tolower($0)}') +# timeout duration - the duration the CLI should wait for a response from +# another container before giving up +CLI_TIMEOUT=10000 +# channel name defaults to "mychannel" +CHANNEL_NAME="mychannel" +# use this as the default docker-compose yaml definition +COMPOSE_FILE=docker-compose-cli.yaml + +# Parse commandline args +while getopts "h?m:c:t:" opt; do + case "$opt" in + h|\?) + printHelp + exit 0 + ;; + m) MODE=$OPTARG + ;; + c) CHANNEL_NAME=$OPTARG + ;; + t) CLI_TIMEOUT=$OPTARG + ;; + esac +done + +# Determine whether starting, stopping, restarting or generating for announce +if [ "$MODE" == "up" ]; then + EXPMODE="Starting" + elif [ "$MODE" == "down" ]; then + EXPMODE="Stopping" + elif [ "$MODE" == "restart" ]; then + EXPMODE="Restarting" + elif [ "$MODE" == "generate" ]; then + EXPMODE="Generating certs and genesis block for" +else + printHelp + exit 1 +fi + +# Announce what was requested +echo "${EXPMODE} with channel '${CHANNEL_NAME}' and CLI timeout of '${CLI_TIMEOUT}'" + +# ask for confirmation to proceed +askProceed + +#Create the network using docker compose +if [ "${MODE}" == "up" ]; then + networkUp + elif [ "${MODE}" == "down" ]; then ## Clear the network + networkDown +elif [ "${MODE}" == "generate" ]; then ## Generate Artifacts + generateCerts + replacePrivateKey + generateChannelArtifacts + elif [ "${MODE}" == "restart" ]; then ## Restart the network + networkDown + networkUp +else + printHelp + exit 1 +fi diff --git a/first-network/chaincode/chaincode_example02/chaincode_example02.go b/first-network/chaincode/chaincode_example02/chaincode_example02.go new file mode 100644 index 0000000000..5343806643 --- /dev/null +++ b/first-network/chaincode/chaincode_example02/chaincode_example02.go @@ -0,0 +1,199 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +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. +*/ + +package main + +//WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of +//calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has +//to be modified as well with the new ID of chaincode_example02. +//chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of +//hard-coding. + +import ( + "fmt" + "strconv" + + "github.com/hyperledger/fabric/core/chaincode/shim" + pb "github.com/hyperledger/fabric/protos/peer" +) + +// SimpleChaincode example simple Chaincode implementation +type SimpleChaincode struct { +} + +func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { + fmt.Println("ex02 Init") + _, args := stub.GetFunctionAndParameters() + var A, B string // Entities + var Aval, Bval int // Asset holdings + var err error + + if len(args) != 4 { + return shim.Error("Incorrect number of arguments. Expecting 4") + } + + // Initialize the chaincode + A = args[0] + Aval, err = strconv.Atoi(args[1]) + if err != nil { + return shim.Error("Expecting integer value for asset holding") + } + B = args[2] + Bval, err = strconv.Atoi(args[3]) + if err != nil { + return shim.Error("Expecting integer value for asset holding") + } + fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) + + // Write the state to the ledger + err = stub.PutState(A, []byte(strconv.Itoa(Aval))) + if err != nil { + return shim.Error(err.Error()) + } + + err = stub.PutState(B, []byte(strconv.Itoa(Bval))) + if err != nil { + return shim.Error(err.Error()) + } + + return shim.Success(nil) +} + +func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { + fmt.Println("ex02 Invoke") + function, args := stub.GetFunctionAndParameters() + if function == "invoke" { + // Make payment of X units from A to B + return t.invoke(stub, args) + } else if function == "delete" { + // Deletes an entity from its state + return t.delete(stub, args) + } else if function == "query" { + // the old "Query" is now implemtned in invoke + return t.query(stub, args) + } + + return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"") +} + +// Transaction makes payment of X units from A to B +func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response { + var A, B string // Entities + var Aval, Bval int // Asset holdings + var X int // Transaction value + var err error + + if len(args) != 3 { + return shim.Error("Incorrect number of arguments. Expecting 3") + } + + A = args[0] + B = args[1] + + // Get the state from the ledger + // TODO: will be nice to have a GetAllState call to ledger + Avalbytes, err := stub.GetState(A) + if err != nil { + return shim.Error("Failed to get state") + } + if Avalbytes == nil { + return shim.Error("Entity not found") + } + Aval, _ = strconv.Atoi(string(Avalbytes)) + + Bvalbytes, err := stub.GetState(B) + if err != nil { + return shim.Error("Failed to get state") + } + if Bvalbytes == nil { + return shim.Error("Entity not found") + } + Bval, _ = strconv.Atoi(string(Bvalbytes)) + + // Perform the execution + X, err = strconv.Atoi(args[2]) + if err != nil { + return shim.Error("Invalid transaction amount, expecting a integer value") + } + Aval = Aval - X + Bval = Bval + X + fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) + + // Write the state back to the ledger + err = stub.PutState(A, []byte(strconv.Itoa(Aval))) + if err != nil { + return shim.Error(err.Error()) + } + + err = stub.PutState(B, []byte(strconv.Itoa(Bval))) + if err != nil { + return shim.Error(err.Error()) + } + + return shim.Success(nil) +} + +// Deletes an entity from state +func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response { + if len(args) != 1 { + return shim.Error("Incorrect number of arguments. Expecting 1") + } + + A := args[0] + + // Delete the key from the state in ledger + err := stub.DelState(A) + if err != nil { + return shim.Error("Failed to delete state") + } + + return shim.Success(nil) +} + +// query callback representing the query of a chaincode +func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response { + var A string // Entities + var err error + + if len(args) != 1 { + return shim.Error("Incorrect number of arguments. Expecting name of the person to query") + } + + A = args[0] + + // Get the state from the ledger + Avalbytes, err := stub.GetState(A) + if err != nil { + jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}" + return shim.Error(jsonResp) + } + + if Avalbytes == nil { + jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}" + return shim.Error(jsonResp) + } + + jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}" + fmt.Printf("Query Response:%s\n", jsonResp) + return shim.Success(Avalbytes) +} + +func main() { + err := shim.Start(new(SimpleChaincode)) + if err != nil { + fmt.Printf("Error starting Simple chaincode: %s", err) + } +} diff --git a/first-network/channel-artifacts/.gitkeep b/first-network/channel-artifacts/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/first-network/configtx.yaml b/first-network/configtx.yaml new file mode 100644 index 0000000000..e60d789e82 --- /dev/null +++ b/first-network/configtx.yaml @@ -0,0 +1,149 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +################################################################################ +# +# Profile +# +# - Different configuration profiles may be encoded here to be specified +# as parameters to the configtxgen tool +# +################################################################################ +Profiles: + + TwoOrgsOrdererGenesis: + Orderer: + <<: *OrdererDefaults + Organizations: + - *OrdererOrg + Consortiums: + SampleConsortium: + Organizations: + - *Org1 + - *Org2 + TwoOrgsChannel: + Consortium: SampleConsortium + Application: + <<: *ApplicationDefaults + Organizations: + - *Org1 + - *Org2 + +################################################################################ +# +# Section: Organizations +# +# - This section defines the different organizational identities which will +# be referenced later in the configuration. +# +################################################################################ +Organizations: + + # SampleOrg defines an MSP using the sampleconfig. It should never be used + # in production but may be used as a template for other definitions + - &OrdererOrg + # DefaultOrg defines the organization which is used in the sampleconfig + # of the fabric.git development environment + Name: OrdererOrg + + # ID to load the MSP definition as + ID: OrdererMSP + + # MSPDir is the filesystem path which contains the MSP configuration + MSPDir: crypto-config/ordererOrganizations/example.com/msp + + - &Org1 + # DefaultOrg defines the organization which is used in the sampleconfig + # of the fabric.git development environment + Name: Org1MSP + + # ID to load the MSP definition as + ID: Org1MSP + + MSPDir: crypto-config/peerOrganizations/org1.example.com/msp + + AnchorPeers: + # AnchorPeers defines the location of peers which can be used + # for cross org gossip communication. Note, this value is only + # encoded in the genesis block in the Application section context + - Host: peer0.org1.example.com + Port: 7051 + + - &Org2 + # DefaultOrg defines the organization which is used in the sampleconfig + # of the fabric.git development environment + Name: Org2MSP + + # ID to load the MSP definition as + ID: Org2MSP + + MSPDir: crypto-config/peerOrganizations/org2.example.com/msp + + AnchorPeers: + # AnchorPeers defines the location of peers which can be used + # for cross org gossip communication. Note, this value is only + # encoded in the genesis block in the Application section context + - Host: peer0.org2.example.com + Port: 7051 + +################################################################################ +# +# SECTION: Orderer +# +# - This section defines the values to encode into a config transaction or +# genesis block for orderer related parameters +# +################################################################################ +Orderer: &OrdererDefaults + + # Orderer Type: The orderer implementation to start + # Available types are "solo" and "kafka" + OrdererType: solo + + Addresses: + - orderer.example.com:7050 + + # Batch Timeout: The amount of time to wait before creating a batch + BatchTimeout: 2s + + # Batch Size: Controls the number of messages batched into a block + BatchSize: + + # Max Message Count: The maximum number of messages to permit in a batch + MaxMessageCount: 10 + + # Absolute Max Bytes: The absolute maximum number of bytes allowed for + # the serialized messages in a batch. + AbsoluteMaxBytes: 99 MB + + # Preferred Max Bytes: The preferred maximum number of bytes allowed for + # the serialized messages in a batch. A message larger than the preferred + # max bytes will result in a batch larger than preferred max bytes. + PreferredMaxBytes: 512 KB + + Kafka: + # Brokers: A list of Kafka brokers to which the orderer connects + # NOTE: Use IP:port notation + Brokers: + - 127.0.0.1:9092 + + # Organizations is the list of orgs which are defined as participants on + # the orderer side of the network + Organizations: + +################################################################################ +# +# SECTION: Application +# +# - This section defines the values to encode into a config transaction or +# genesis block for application related parameters +# +################################################################################ +Application: &ApplicationDefaults + + # Organizations is the list of orgs which are defined as participants on + # the application side of the network + Organizations: diff --git a/first-network/crypto-config.yaml b/first-network/crypto-config.yaml new file mode 100644 index 0000000000..06ad8185cd --- /dev/null +++ b/first-network/crypto-config.yaml @@ -0,0 +1,81 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# --------------------------------------------------------------------------- +# "OrdererOrgs" - Definition of organizations managing orderer nodes +# --------------------------------------------------------------------------- +OrdererOrgs: + # --------------------------------------------------------------------------- + # Orderer + # --------------------------------------------------------------------------- + - Name: Orderer + Domain: example.com + # --------------------------------------------------------------------------- + # "Specs" - See PeerOrgs below for complete description + # --------------------------------------------------------------------------- + Specs: + - Hostname: orderer +# --------------------------------------------------------------------------- +# "PeerOrgs" - Definition of organizations managing peer nodes +# --------------------------------------------------------------------------- +PeerOrgs: + # --------------------------------------------------------------------------- + # Org1 + # --------------------------------------------------------------------------- + - Name: Org1 + Domain: org1.example.com + # --------------------------------------------------------------------------- + # "Specs" + # --------------------------------------------------------------------------- + # Uncomment this section to enable the explicit definition of hosts in your + # configuration. Most users will want to use Template, below + # + # Specs is an array of Spec entries. Each Spec entry consists of two fields: + # - Hostname: (Required) The desired hostname, sans the domain. + # - CommonName: (Optional) Specifies the template or explicit override for + # the CN. By default, this is the template: + # + # "{{.Hostname}}.{{.Domain}}" + # + # which obtains its values from the Spec.Hostname and + # Org.Domain, respectively. + # --------------------------------------------------------------------------- + # Specs: + # - Hostname: foo # implicitly "foo.org1.example.com" + # CommonName: foo27.org5.example.com # overrides Hostname-based FQDN set above + # - Hostname: bar + # - Hostname: baz + # --------------------------------------------------------------------------- + # "Template" + # --------------------------------------------------------------------------- + # Allows for the definition of 1 or more hosts that are created sequentially + # from a template. By default, this looks like "peer%d" from 0 to Count-1. + # You may override the number of nodes (Count), the starting index (Start) + # or the template used to construct the name (Hostname). + # + # Note: Template and Specs are not mutually exclusive. You may define both + # sections and the aggregate nodes will be created for you. Take care with + # name collisions + # --------------------------------------------------------------------------- + Template: + Count: 2 + # Start: 5 + # Hostname: {{.Prefix}}{{.Index}} # default + # --------------------------------------------------------------------------- + # "Users" + # --------------------------------------------------------------------------- + # Count: The number of user accounts _in addition_ to Admin + # --------------------------------------------------------------------------- + Users: + Count: 1 + # --------------------------------------------------------------------------- + # Org2: See "Org1" for full specification + # --------------------------------------------------------------------------- + - Name: Org2 + Domain: org2.example.com + Template: + Count: 2 + Users: + Count: 1 diff --git a/first-network/docker-compose-cli.yaml b/first-network/docker-compose-cli.yaml new file mode 100644 index 0000000000..36483348b4 --- /dev/null +++ b/first-network/docker-compose-cli.yaml @@ -0,0 +1,69 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +version: '2' + +services: + + orderer.example.com: + extends: + file: base/docker-compose-base.yaml + service: orderer.example.com + container_name: orderer.example.com + + peer0.org1.example.com: + container_name: peer0.org1.example.com + extends: + file: base/docker-compose-base.yaml + service: peer0.org1.example.com + + peer1.org1.example.com: + container_name: peer1.org1.example.com + extends: + file: base/docker-compose-base.yaml + service: peer1.org1.example.com + + peer0.org2.example.com: + container_name: peer0.org2.example.com + extends: + file: base/docker-compose-base.yaml + service: peer0.org2.example.com + + peer1.org2.example.com: + container_name: peer1.org2.example.com + extends: + file: base/docker-compose-base.yaml + service: peer1.org2.example.com + + cli: + container_name: cli + image: hyperledger/fabric-tools + tty: true + environment: + - GOPATH=/opt/gopath + - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock + - CORE_LOGGING_LEVEL=DEBUG + - CORE_PEER_ID=cli + - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + - CORE_PEER_TLS_ENABLED=true + - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt + - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key + - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt + - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer + command: /bin/bash -c './scripts/script.sh ${CHANNEL_NAME}; sleep $TIMEOUT' + volumes: + - /var/run/:/host/var/run/ + - ./chaincode/:/opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go + - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ + - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/ + - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts + depends_on: + - orderer.example.com + - peer0.org1.example.com + - peer1.org1.example.com + - peer0.org2.example.com + - peer1.org2.example.com diff --git a/first-network/docker-compose-couch.yaml b/first-network/docker-compose-couch.yaml new file mode 100644 index 0000000000..d2049b7fdd --- /dev/null +++ b/first-network/docker-compose-couch.yaml @@ -0,0 +1,67 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +version: '2' + +services: + couchdb0: + container_name: couchdb0 + image: hyperledger/fabric-couchdb + # Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service, + # for example map it to utilize Fauxton User Interface in dev environments. + ports: + - "5984:5984" + + peer0.org1.example.com: + environment: + - CORE_LEDGER_STATE_STATEDATABASE=CouchDB + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb0:5984 + depends_on: + - couchdb0 + + couchdb1: + container_name: couchdb1 + image: hyperledger/fabric-couchdb + # Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service, + # for example map it to utilize Fauxton User Interface in dev environments. + ports: + - "6984:5984" + + peer1.org1.example.com: + environment: + - CORE_LEDGER_STATE_STATEDATABASE=CouchDB + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb1:5984 + depends_on: + - couchdb1 + + couchdb2: + container_name: couchdb2 + image: hyperledger/fabric-couchdb + # Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service, + # for example map it to utilize Fauxton User Interface in dev environments. + ports: + - "7984:5984" + + peer0.org2.example.com: + environment: + - CORE_LEDGER_STATE_STATEDATABASE=CouchDB + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb2:5984 + depends_on: + - couchdb2 + + couchdb3: + container_name: couchdb3 + image: hyperledger/fabric-couchdb + # Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service, + # for example map it to utilize Fauxton User Interface in dev environments. + ports: + - "8984:5984" + + peer1.org2.example.com: + environment: + - CORE_LEDGER_STATE_STATEDATABASE=CouchDB + - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb3:5984 + depends_on: + - couchdb3 diff --git a/first-network/docker-compose-e2e-template.yaml b/first-network/docker-compose-e2e-template.yaml new file mode 100644 index 0000000000..d30d0ef57c --- /dev/null +++ b/first-network/docker-compose-e2e-template.yaml @@ -0,0 +1,67 @@ +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +version: '2' + +services: + ca0: + image: hyperledger/fabric-ca + environment: + - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server + - FABRIC_CA_SERVER_CA_NAME=ca-org1 + - FABRIC_CA_SERVER_TLS_ENABLED=true + - FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem + - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/CA1_PRIVATE_KEY + ports: + - "7054:7054" + command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/CA1_PRIVATE_KEY -b admin:adminpw -d' + volumes: + - ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config + container_name: ca_peerOrg1 + + ca1: + image: hyperledger/fabric-ca + environment: + - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server + - FABRIC_CA_SERVER_CA_NAME=ca-org2 + - FABRIC_CA_SERVER_TLS_ENABLED=true + - FABRIC_CA_SERVER_TLS_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem + - FABRIC_CA_SERVER_TLS_KEYFILE=/etc/hyperledger/fabric-ca-server-config/CA2_PRIVATE_KEY + ports: + - "8054:7054" + command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/CA2_PRIVATE_KEY -b admin:adminpw -d' + volumes: + - ./crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/fabric-ca-server-config + container_name: ca_peerOrg2 + + orderer.example.com: + extends: + file: base/docker-compose-base.yaml + service: orderer.example.com + container_name: orderer.example.com + + peer0.org1.example.com: + container_name: peer0.org1.example.com + extends: + file: base/docker-compose-base.yaml + service: peer0.org1.example.com + + peer1.org1.example.com: + container_name: peer1.org1.example.com + extends: + file: base/docker-compose-base.yaml + service: peer1.org1.example.com + + peer0.org2.example.com: + container_name: peer0.org2.example.com + extends: + file: base/docker-compose-base.yaml + service: peer0.org2.example.com + + peer1.org2.example.com: + container_name: peer1.org2.example.com + extends: + file: base/docker-compose-base.yaml + service: peer1.org2.example.com diff --git a/first-network/scripts/script.sh b/first-network/scripts/script.sh new file mode 100755 index 0000000000..43433f0ca7 --- /dev/null +++ b/first-network/scripts/script.sh @@ -0,0 +1,240 @@ +#!/bin/bash + +echo +echo " ____ _____ _ ____ _____ " +echo "/ ___| |_ _| / \ | _ \ |_ _|" +echo "\___ \ | | / _ \ | |_) | | | " +echo " ___) | | | / ___ \ | _ < | | " +echo "|____/ |_| /_/ \_\ |_| \_\ |_| " +echo +echo "Build your first network (BYFN) end-to-end test" +echo +CHANNEL_NAME="$1" +: ${CHANNEL_NAME:="mychannel"} +: ${TIMEOUT:="60"} +COUNTER=1 +MAX_RETRY=5 +ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/cacerts/ca.example.com-cert.pem + +echo "Channel name : "$CHANNEL_NAME + +# verify the result of the end-to-end test +verifyResult () { + if [ $1 -ne 0 ] ; then + echo "!!!!!!!!!!!!!!! "$2" !!!!!!!!!!!!!!!!" + echo "========= ERROR !!! FAILED to execute End-2-End Scenario ===========" + echo + exit 1 + fi +} + +setGlobals () { + + if [ $1 -eq 0 -o $1 -eq 1 ] ; then + CORE_PEER_LOCALMSPID="Org1MSP" + CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt + CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp + if [ $1 -eq 0 ]; then + CORE_PEER_ADDRESS=peer0.org1.example.com:7051 + else + CORE_PEER_ADDRESS=peer1.org1.example.com:7051 + CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp + fi + else + CORE_PEER_LOCALMSPID="Org2MSP" + CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt + CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp + if [ $1 -eq 2 ]; then + CORE_PEER_ADDRESS=peer0.org2.example.com:7051 + else + CORE_PEER_ADDRESS=peer1.org2.example.com:7051 + fi + fi + + env |grep CORE +} + +createChannel() { + setGlobals 0 + + if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then + peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx >&log.txt + else + peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt + fi + res=$? + cat log.txt + verifyResult $res "Channel creation failed" + echo "===================== Channel \"$CHANNEL_NAME\" is created successfully ===================== " + echo +} + +updateAnchorPeers() { + PEER=$1 + setGlobals $PEER + + if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then + peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx >&log.txt + else + peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA >&log.txt + fi + res=$? + cat log.txt + verifyResult $res "Anchor peer update failed" + echo "===================== Anchor peers for org \"$CORE_PEER_LOCALMSPID\" on \"$CHANNEL_NAME\" is updated successfully ===================== " + echo +} + +## Sometimes Join takes time hence RETRY atleast for 5 times +joinWithRetry () { + peer channel join -b $CHANNEL_NAME.block >&log.txt + res=$? + cat log.txt + if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then + COUNTER=` expr $COUNTER + 1` + echo "PEER$1 failed to join the channel, Retry after 2 seconds" + sleep 2 + joinWithRetry $1 + else + COUNTER=1 + fi + verifyResult $res "After $MAX_RETRY attempts, PEER$ch has failed to Join the Channel" +} + +joinChannel () { + for ch in 0 1 2 3; do + setGlobals $ch + joinWithRetry $ch + echo "===================== PEER$ch joined on the channel \"$CHANNEL_NAME\" ===================== " + sleep 2 + echo + done +} + +installChaincode () { + PEER=$1 + setGlobals $PEER + peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02 >&log.txt + res=$? + cat log.txt + verifyResult $res "Chaincode installation on remote peer PEER$PEER has Failed" + echo "===================== Chaincode is installed on remote peer PEER$PEER ===================== " + echo +} + +instantiateChaincode () { + PEER=$1 + setGlobals $PEER + # while 'peer chaincode' command can get the orderer endpoint from the peer (if join was successful), + # lets supply it directly as we know it using the "-o" option + if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then + peer chaincode instantiate -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')" >&log.txt + else + peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR ('Org1MSP.member','Org2MSP.member')" >&log.txt + fi + res=$? + cat log.txt + verifyResult $res "Chaincode instantiation on PEER$PEER on channel '$CHANNEL_NAME' failed" + echo "===================== Chaincode Instantiation on PEER$PEER on channel '$CHANNEL_NAME' is successful ===================== " + echo +} + +chaincodeQuery () { + PEER=$1 + echo "===================== Querying on PEER$PEER on channel '$CHANNEL_NAME'... ===================== " + setGlobals $PEER + local rc=1 + local starttime=$(date +%s) + + # continue to poll + # we either get a successful response, or reach TIMEOUT + while test "$(($(date +%s)-starttime))" -lt "$TIMEOUT" -a $rc -ne 0 + do + sleep 3 + echo "Attempting to Query PEER$PEER ...$(($(date +%s)-starttime)) secs" + peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' >&log.txt + test $? -eq 0 && VALUE=$(cat log.txt | awk '/Query Result/ {print $NF}') + test "$VALUE" = "$2" && let rc=0 + done + echo + cat log.txt + if test $rc -eq 0 ; then + echo "===================== Query on PEER$PEER on channel '$CHANNEL_NAME' is successful ===================== " + else + echo "!!!!!!!!!!!!!!! Query result on PEER$PEER is INVALID !!!!!!!!!!!!!!!!" + echo "================== ERROR !!! FAILED to execute End-2-End Scenario ==================" + echo + exit 1 + fi +} + +chaincodeInvoke () { + PEER=$1 + setGlobals $PEER + # while 'peer chaincode' command can get the orderer endpoint from the peer (if join was successful), + # lets supply it directly as we know it using the "-o" option + if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then + peer chaincode invoke -o orderer.example.com:7050 -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' >&log.txt + else + peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' >&log.txt + fi + res=$? + cat log.txt + verifyResult $res "Invoke execution on PEER$PEER failed " + echo "===================== Invoke transaction on PEER$PEER on channel '$CHANNEL_NAME' is successful ===================== " + echo +} + +## Create channel +echo "Creating channel..." +createChannel + +## Join all the peers to the channel +echo "Having all peers join the channel..." +joinChannel + +## Set the anchor peers for each org in the channel +echo "Updating anchor peers for org1..." +updateAnchorPeers 0 +echo "Updating anchor peers for org2..." +updateAnchorPeers 2 + +## Install chaincode on Peer0/Org1 and Peer2/Org2 +echo "Installing chaincode on org1/peer0..." +installChaincode 0 +echo "Install chaincode on org2/peer2..." +installChaincode 2 + +#Instantiate chaincode on Peer2/Org2 +echo "Instantiating chaincode on org2/peer2..." +instantiateChaincode 2 + +#Query on chaincode on Peer0/Org1 +echo "Querying chaincode on org1/peer0..." +chaincodeQuery 0 100 + +#Invoke on chaincode on Peer0/Org1 +echo "Sending invoke transaction on org1/peer0..." +chaincodeInvoke 0 + +## Install chaincode on Peer3/Org2 +echo "Installing chaincode on org2/peer3..." +installChaincode 3 + +#Query on chaincode on Peer3/Org2, check if the result is 90 +echo "Querying chaincode on org2/peer3..." +chaincodeQuery 3 90 + +echo +echo "========= All GOOD, BYFN execution completed =========== " +echo + +echo +echo " _____ _ _ ____ " +echo "| ____| | \ | | | _ \ " +echo "| _| | \| | | | | | " +echo "| |___ | |\ | | |_| | " +echo "|_____| |_| \_| |____/ " +echo + +exit 0