A new feature introduced in Hyperledger Fabric 2.0 is the ability to use external chain code launchers, which are ideal for using kubenetes or dowcker swarm to manage node and chain code containers in a unified manner.In this article, we will learn how to deploy chain codes in a K8s cluster using an external chain code launcher.
1. Prerequisites for Fabric External Chain Code Experiment
Hyperledger Fabric Block Chain Development Tutorial: Fabric Node.js Development Details | Fabric Java Development Details | Fabric Golang Development Details
Here are some prerequisites for our experiment
- A kubenetes cluster where you can use minikube or a single-node kubeadmin.In this article we use kubeadmin
- hyperledger fabric 2.0.1 docker mirror
- hyperledger fabric 2.0.1 precompiler.We need tools to create cryptographic data and channel transaction profiles.From Here Download.
- from Here Download the code for this article
2. Install precompiler
Install the precompiler using the following commands:
wget https://github.com/hyperledger/fabric/releases/download/v2.0.1/hyperledger-fabric-linux-amd64-2.0.1.tar.gz tar -xzf hyperledger-fabric-linux-amd64-2.0.1.tar.gz# Move to the bin path mv bin/* /bin# Check that you have successfully installed the tools by executing configtxgen --version# Should print the following output: # configtxgen: # Version: 2.0.1 # Commit SHA: 1cfa5da98 # Go version: go1.13.4 # OS/Arch: linux/amd64
3. Start Hyperledger Fabric Network
Once we have started the kubernetes cluster, we can start the Fabric network.But first we need to generate the basic cryptographic data and the web creation block.There are some modifications in configtx.yaml to accommodate the new ones in fabric 2.0 lifecycle chaincode Command, these modifications are necessary to use the external chain code launcher.
Fabric block chain network contains three RAFT sorting service nodes, two institutions org1 and org2 each contain one peer node and one CA.This information is already encoded in the configtx.yaml and crypto-config.yaml files and does not need to be modified.
With the configuration file, we can now generate cryptographic data and Genesis blocks for the fabricblock chain network.We use the fabricOps.sh script:
$ ./fabricOps.sh start
The above command generates all cryptographic data, such as certificates for nodes and CA s, and the creation block of the channel.
Now you need to create a new k8s namespace for the Hyperledger Fabric workload with the following commands:
$ kubectl create ns hyperledger
Then create a folder to hold persistent data for the Hyperledger Fabric workload:
$ mkdir /home/storage
Now you can deploy the Fabric sorting node:
$ kubectl create -f orderer-service/
Use the following command to check if the Fabric network is up properly:
$ kubectl get pods -n hyperledger ### Should print a similar output NAME READY STATUS RESTARTS AGE orderer0-58666b6bd7-pflf7 1/1 Running 0 5m47s orderer1-c4fd65c7d-c27ll 1/1 Running 0 5m47s orderer2-557cb7865-wlcmh 1/1 Running 0 5m47s
Next, create the workload for org1, which mainly includes the CA and peer nodes of the deployment agency:
$ kubectl create -f org1/
Also check whether the Fabric network node is started with the following commands:
$ kubectl get pods -n hyperledger ### Should print a similar output NAME READY STATUS RESTARTS AGE ca-org1-84945b8c7b-9px4s 1/1 Running 0 19m cli-org1-bc9f895f6-zmmdc 1/1 Running 0 2m56s orderer0-58666b6bd7-pflf7 1/1 Running 0 79m orderer1-c4fd65c7d-c27ll 1/1 Running 0 79m orderer2-557cb7865-wlcmh 1/1 Running 0 79m peer0-org1-798b974467-vv4zz 1/1 Running 0 19m
Repeat the steps above to load the org2 workload:
$ kubectl create -f org2/
Check the Fabric network:
$ kubectl get pods -n hyperledger ### Should print a similar output NAME READY STATUS RESTARTS AGE ca-org1-84945b8c7b-9px4s 1/1 Running 0 71m ca-org2-7454f69c48-q8lft 1/1 Running 0 2m20s cli-org1-bc9f895f6-zmmdc 1/1 Running 0 55m cli-org2-7779cc8788-8q4ns 1/1 Running 0 2m20s orderer0-58666b6bd7-pflf7 1/1 Running 0 131m orderer1-c4fd65c7d-c27ll 1/1 Running 0 131m orderer2-557cb7865-wlcmh 1/1 Running 0 131m peer0-org1-798b974467-vv4zz 1/1 Running 0 71m peer0-org2-5849c55fcd-mbn5h 1/1 Running 0 2m19s
3. Set up channels for Fabric networks
Once the workload on the Fabric network is deployed, we can start creating channels and adding peer nodes to them.cli into org1:
$ kubectl exec -it cli_org1_pod_name sh -n hyperledger
Within this pod, execute the following command:
$ peer channel create -o orderer0:7050 -c mychannel -f ./scripts/channel-artifacts/channel.tx --tls true --cafile $ORDERER_CA ### Should print a similar output 2020-03-06 11:54:57.582 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized 2020-03-06 11:54:58.903 UTC [cli.common] readBlock -> INFO 002 Received block: 0
Now that the mychannel channel channel has been created, add the peer node of org1 to the channel:
$ peer channel join -b mychannel.block ### Should print a similar output 2020-03-06 12:01:41.608 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized 2020-03-06 12:01:41.688 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
The same operation adds the peer node of org2 to the channel.However, since org1 has created channels, we need to extract the channel creation block from the sorting service.First enter the pod:
$ kubectl exec -it cli_org2_pod_name sh -n hyperledger
Then do the following within the pod:
$ peer channel fetch 0 mychannel.block -c mychannel -o orderer0:7050 --tls --cafile $ORDERER_CA ### Should print a similar output 2020-03-06 12:18:14.880 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized 2020-03-06 12:18:14.895 UTC [cli.common] readBlock -> INFO 002 Received block: 0
Then join the channel:
$ peer channel join -b mychannel.block ### Should print a similar output 2020-03-06 12:20:41.475 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized 2020-03-06 12:20:41.561 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
Check if the current node has joined the channel with the following command:
$ peer channel list ### Should print a similar output 2020-03-06 12:22:41.102 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized Channels peers has joined: mychannel
4. Install external chain code on Fabric node
Now let's get to the really interesting part.We will deploy marbles chain codes as external chain codes.You can do it in Fabric-samples The original chain code was found in, but in order to deploy it as an external chain code, we need to make some modifications in the imports and init functions. To declare it as an external chain code server.
The new version of the Hipperledger Fabric also includes a new chain code processing process to support the external installation and startup of chain codes.In order to use the external chain code feature, we also need to use this new process.
The first thing to do is configure the peer node to handle external chain codes.External chain code builder is based on buildpack Implementation, so we create three scripts: detect, build, and release.These three scripts must be defined within the peer container.You can Find these scripts in the buildpack/bin directory.
The chain code builder is defined in builders-config.yaml in the org1 directory, where the Custom Builder configuration is:
# List of directories to treat as external builders and launchers for # chaincode. The external builder detection processing will iterate over the # builders in the order specified below. externalBuilders: - name: external-builder path: /builders/external environmentWhitelist: - GOPROXY
kubernetes uses environment variables to configure peer nodes, which override the defaults in core.yaml.
Fabric 2.0 packages and installs chain codes differently from previous versions.Because we want to use the external chain code feature, the chain code does not need to be compiled and installed in the peer's pod, but in another pod.The only information needed in the peer process is the information used to connect the external chain code process.
To do this, we need some preparation for packaging chain codes.A connection.json file is required that contains information about connecting to an external chain code server, such as address, TLS certificate, connection timeout configuration, and so on.
{ "address": "chaincode-marbles-org1.hyperledger:7052", "dial_timeout": "10s", "tls_required": false, "client_auth_required": false, "client_key": "-----BEGIN EC PRIVATE KEY----- ... -----END EC PRIVATE KEY-----", "client_cert": "-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----", "root_cert": "-----BEGIN CERTIFICATE---- ... -----END CERTIFICATE-----" }
This file needs to be packaged into a tar file, code.tar.gz, and compressed using the following commands:
$ cd chaincode/packaging $ tar cfz code.tar.gz connection.json
Once we have the code.tar.gz file, we need to repackage it with another file, metadata.json.Chain code type, path, label and so on are included in metadata.json:
{"path":"","type":"external","label":"marbles"}
Package using the following commands:
$ tar cfz marbles-org1.tgz code.tar.gz metadata.json
Once we have the packaged file above, we can use the new one lifecycle chaincode install Command to install chain code:
$ peer lifecycle chaincode install marbles-org1.tgz ### Should print a similar output 2020-03-07 14:33:18.120 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nGdmarbles:e001937433673b11673d660d142c722fc372905db87f88d2448eee42c9c63064\022\006degree" > 2020-03-07 14:33:18.126 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: marbles:e001937433673b11673d660d142c722fc372905db87f88d2448eee42c9c63064
Write down the chain code package identifier above, which we will use next, but you can also query the chain code package identifier at any time with the following command:
$ peer lifecycle chaincode queryinstalled ### Should print a similar output Installed chaincodes on peer: Package ID: marbles:030eec59c7d74fbb4e9fd57bbd50bb904a715ffb9de8fea85b6a6d4b8ca9ea12, Label: marbles
Now let's repeat the steps above for org2, but since we want to use another pod to provide chain code services for the peer nodes of org2, So we need to modify the address configuration in connection.json:
"address": "chaincode-marbles-org2.hyperledger:7052",
Then repeat the previous steps to execute the following command in the cli pod of org2:
$ rm -f code.tar.gz $ tar cfz code.tar.gz connection.json $ tar cfz marbles-org2.tgz code.tar.gz metadata.json $ peer lifecycle chaincode install marbles-org2.tgz ### Should print a similar output 2020-03-07 15:10:15.093 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nGmarbles:c422c797444e4ee25a92a8eaf97765288a8d68f9c29cedf1e0cd82e4aa2c8a5b\022\006degree" > 2020-03-07 15:10:15.093 UTC [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: marbles:c422c797444e4ee25a92a8eaf97765288a8d68f9c29cedf1e0cd82e4aa2c8a5b
It also records the identifier of the chain code package, which should be different from org1.
What happens inside the peer when we install the chain code?If an external builder or launcher is defined, then the built-in chain code building process is not performed.Since we have defined an external builder, the external build scripts will be executed in the following order:
The detect script detects whether the chain code to be installed is configured as an external type in metadata.json.If the script fails, peer will assume that the external builder does not need to build chain codes and try other external builders until the built-in docker build process is used when no external builder is finally available.
#!/bin/sh # The bin/detect script is responsible for determining whether or not a buildpack # should be used to build a chaincode package and launch it. # # The peer invokes detect with two arguments: # bin/detect CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR # # When detect is invoked, CHAINCODE_SOURCE_DIR contains the chaincode source and # CHAINCODE_METADATA_DIR contains the metadata.json file from the chaincode package installed to the peer. # The CHAINCODE_SOURCE_DIR and CHAINCODE_METADATA_DIR should be treated as read only inputs. # If the buildpack should be applied to the chaincode source package, detect must return an exit code of 0; # any other exit code will indicate that the buildpack should not be applied. CHAINCODE_METADATA_DIR="$2" set -euo pipefail # use jq to extract the chaincode type from metadata.json and exit with # success if the chaincode type is golang if [ "$(cat "$CHAINCODE_METADATA_DIR/metadata.json" | sed -e 's/[{}]/''/g' | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'type'\042/){print $(i+1)}}}' | tr -d '"')" = "external" ]; then exit 0 fi exit 1
If the detect script succeeds, the build script is called.If chain codes are built inside a peer, the script should generate binaries.However, since we want to be an external service, simply copy the connection.json file to the build output directory.
#!/bin/sh # The bin/build script is responsible for building, compiling, or transforming the contents # of a chaincode package into artifacts that can be used by release and run. # # The peer invokes build with three arguments: # bin/build CHAINCODE_SOURCE_DIR CHAINCODE_METADATA_DIR BUILD_OUTPUT_DIR # # When build is invoked, CHAINCODE_SOURCE_DIR contains the chaincode source and # CHAINCODE_METADATA_DIR contains the metadata.json file from the chaincode package installed to the peer. # BUILD_OUTPUT_DIR is the directory where build must place artifacts needed by release and run. # The build script should treat the input directories CHAINCODE_SOURCE_DIR and # CHAINCODE_METADATA_DIR as read only, but the BUILD_OUTPUT_DIR is writeable. CHAINCODE_SOURCE_DIR="$1" CHAINCODE_METADATA_DIR="$2" BUILD_OUTPUT_DIR="$3" set -euo pipefail #external chaincodes expect connection.json file in the chaincode package if [ ! -f "$CHAINCODE_SOURCE_DIR/connection.json" ]; then >&2 echo "$CHAINCODE_SOURCE_DIR/connection.json not found" exit 1 fi #simply copy the endpoint information to specified output location cp $CHAINCODE_SOURCE_DIR/connection.json $BUILD_OUTPUT_DIR/connection.json if [ -d "$CHAINCODE_SOURCE_DIR/metadata" ]; then cp -a $CHAINCODE_SOURCE_DIR/metadata $BUILD_OUTPUT_DIR/metadata fi exit 0
Finally, once the build script is executed, the release script is called.This script is responsible for providing the connection.json file to the peer node, and it only needs to be placed in the release output directory.So peer now knows how to call chain codes.
#!/bin/sh # The bin/release script is responsible for providing chaincode metadata to the peer. # bin/release is optional. If it is not provided, this step is skipped. # # The peer invokes release with two arguments: # bin/release BUILD_OUTPUT_DIR RELEASE_OUTPUT_DIR # # When release is invoked, BUILD_OUTPUT_DIR contains the artifacts # populated by the build program and should be treated as read only input. # RELEASE_OUTPUT_DIR is the directory where release must place artifacts to be consumed by the peer. set -euo pipefail BUILD_OUTPUT_DIR="$1" RELEASE_OUTPUT_DIR="$2" # copy indexes from metadata/* to the output directory # if [ -d "$BUILD_OUTPUT_DIR/metadata" ] ; then # cp -a "$BUILD_OUTPUT_DIR/metadata/"* "$RELEASE_OUTPUT_DIR/" # fi #external chaincodes expect artifacts to be placed under "$RELEASE_OUTPUT_DIR"/chaincode/server if [ -f $BUILD_OUTPUT_DIR/connection.json ]; then mkdir -p "$RELEASE_OUTPUT_DIR"/chaincode/server cp $BUILD_OUTPUT_DIR/connection.json "$RELEASE_OUTPUT_DIR"/chaincode/server #if tls_required is true, copy TLS files (using above example, the fully qualified path for these fils would be "$RELEASE_OUTPUT_DIR"/chaincode/server/tls) exit 0 fi exit 1
5. Construction and Deployment of Fabric External Chain Code
Once we have chain codes installed on peer nodes, they can be built and deployed on our kubernetes cluster.Let's take a look at the necessary changes.
First you need to import some necessary modules:
import ( "bytes" "encoding/json" "fmt" "strconv" "strings" "time" "os" "github.com/hyperledger/fabric-chaincode-go/shim" pb "github.com/hyperledger/fabric-protos-go/peer" )
One thing to point out is that you need to define the go.mod file before building code.The latest version of the shim module includes support for external chain code features - only the go chain code development kit currently supports external chain code.
module github.com/marbles go 1.12 require ( github.com/hyperledger/fabric-chaincode-go v0.0.0-20200128192331-2d899240a7ed github.com/hyperledger/fabric-protos-go v0.0.0-20200124220212-e9cfc186ba7b golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect golang.org/x/text v0.3.2 // indirect google.golang.org/genproto v0.0.0-20200218151345-dad8c97a84f5 // indirect )
Other changes include modifying the chaining code server's listening address, port, and so on.Because we want to use kubernetes'yaml description file to modify these configurations, we use the os module to get these values:
func main() { server := &shim.ChaincodeServer{ CCID: os.Getenv("CHAINCODE_CCID"), Address: os.Getenv("CHAINCODE_ADDRESS"), CC: new(SimpleChaincode), TLSProps: shim.TLSProperties{ Disabled: true, }, } // Start the chaincode external server err := server.Start() if err != nil { fmt.Printf("Error starting Marbles02 chaincode: %s", err) } }
Now you can use the following Dockerfile to build a chain code image:
# This image is a microservice in golang for the Degree chaincode FROM golang:1.13.8-alpine AS build COPY ./ /go/src/github.com/marbles WORKDIR /go/src/github.com/marbles # Build application RUN go build -o chaincode -v . # Production ready image # Pass the binary to the prod image FROM alpine:3.11 as prod COPY --from=build /go/src/github.com/marbles/chaincode /app/chaincode USER 1000 WORKDIR /app CMD ./chaincode
Chain codes are constructed using an alpine image of golang.Perform the following command to create a chain code image:
$ docker build -t chaincode/marbles:1.0 .
If everything goes well, you can deploy it.Modify the CHAINCODE_CCID variable in the chain code deployment files org1-chaincode-deployment.yaml and org2-chaincode-deployment.yaml for the chain code you need to install.
#---------------- Chaincode Deployment --------------------- apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1 kind: Deployment metadata: name: chaincode-marbles-org1 namespace: hyperledger labels: app: chaincode-marbles-org1 spec: selector: matchLabels: app: chaincode-marbles-org1 strategy: type: Recreate template: metadata: labels: app: chaincode-marbles-org1 spec: containers: - image: chaincode/marbles:1.0 name: chaincode-marbles-org1 imagePullPolicy: IfNotPresent env: - name: CHAINCODE_CCID value: "marbles:d8140fbc1a0903bd88611a96c5b0077a2fdeef00a95c05bfe52e207f5f9ab79d" - name: CHAINCODE_ADDRESS value: "0.0.0.0:7052" ports: - containerPort: 7052
Then deploy:
$ kubectl create -f chaincode/k8s
Now the fabric s network should look like this:
$ kubectl get pods -n hyperledgerNAME READY STATUS RESTARTS AGE ca-org1-84945b8c7b-tx59g 1/1 Running 0 19h ca-org2-7454f69c48-nfzsq 1/1 Running 0 19h chaincode-marbles-org1-6fc8858855-wdz7z 1/1 Running 0 20m chaincode-marbles-org2-77bf56fdfb-6cdfm 1/1 Running 0 14m cli-org1-589944999c-cvgbx 1/1 Running 0 19h cli-org2-656cf8dd7c-kcxd7 1/1 Running 0 19h orderer0-5844bd9bcc-6td8c 1/1 Running 0 46h orderer1-75d8df99cd-6vbjl 1/1 Running 0 46h orderer2-795cf7c4c-6lsdd 1/1 Running 0 46h peer0-org1-5bc579d766-kq2qd 1/1 Running 0 19h peer0-org2-77f58c87fd-sczp8 1/1 Running 0 19h
Now we need to approve the chain code for each agency.This is a new feature of the chain code life cycle process, and each agency needs to agree to the definition of the new chain code.We will approve the marble chain code definition for org1.Execute the following command within the cli pod of org1, remember to modify CHAINCODE_CCID:
$ peer lifecycle chaincode approveformyorg --channelID mychannel --name marbles --version 1.0 --init-required --package-id marbles:e001937433673b11673d660d142c722fc372905db87f88d2448eee42c9c63064 --sequence 1 -o orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')" ### Should print a similar output 2020-03-08 10:02:46.192 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [4d81ea5fd494e9717a0c860812d2b06bc62e4fc6c4b85fa6c3a916eee2c78e85] committed with status (VALID)
You can check the approval status throughout the network with the following commands:
$ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name marbles --version 1.0 --init-require d --sequence 1 -o -orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')" ### Should print a similar output Chaincode definition for chaincode 'marbles', version '1.0', sequence '1' on channel 'mychannel' approval status by org: org1MSP: true org2MSP: false
Now let's approve the chain code definition for org2.Execute the following command in the cli pod of org2, remember to modify CHAINCODE_CCID:
$ peer lifecycle chaincode approveformyorg --channelID mychannel --name marbles --version 1.0 --init-required --package-id marbles:25a9f6fe26161d29af928228ca1db0c41892e26e46335c84952336ee26d1fd93 --sequence 1 -o orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')" ### Should print a similar output 2020-03-08 10:26:43.992 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [74a89f3c93c10f14c626bd4d6cb654b37889908c9e6f7b983d2cad79f1e82267] committed with status (VALID)
Check chain code submission status again:
$ peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name marbles --version 1.0 --init-required --sequence 1 -o orderer0:7050 --tls --cafile $ORDERER_CA --signature-policy "AND ('org1MSP.peer','org2MSP.peer')" ### Should print a similar output Chaincode definition for chaincode 'marbles', version '1.0', sequence '1' on channel 'mychannel' approval status by org: org1MSP: true org2MSP: true
Now that we have the approval of all agencies, let's submit the chain code definition on the channel.You can do the following on any peer:
$ peer lifecycle chaincode commit -o orderer0:7050 --channelID mychannel --name marbles --version 1.0 --sequence 1 --init-required --tls true --cafile $ORDERER_CA --peerAddresses peer0-org1:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1/peers/peer0-org1/tls/ca.crt --peerAddresses peer0-org2:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2/peers/peer0-org2/tls/ca.crt --signature-policy "AND ('org1MSP.peer','org2MSP.peer')" ### Should print a similar output 2020-03-08 14:13:49.516 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [568cb81f821698025bbc61f4c6cd3b4baf1aea632e1e1a8cfdf3ec3902d1c6bd] committed with status (VALID) at peer0-org1:7051 2020-03-08 14:13:49.533 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [568cb81f821698025bbc61f4c6cd3b4baf1aea632e1e1a8cfdf3ec3902d1c6bd] committed with status (VALID) at peer0-org2:7051
Okay, now the chain code is ready to be queried and invoked!
6. Testing Fabric External Chain Code
We can test chain code queries and transaction calls from cli pod.First create some gems:
$ peer chaincode invoke -o orderer0:7050 --tls true --cafile $ORDERER_CA -C mychannel -n marbles --peerAddresses peer0-org1:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1/peers/peer0-org1/tls/ca.crt --peerAddresses peer 0-org2:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2/peers/peer0-org2/tls/ca.crt -c '{"Args":["initMarble ","marble1","blue","35","tom"]}' --waitForEvent ### Should print a similar output 2020-03-08 14:23:03.569 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [83aeeaac47cf6302bc139addc4aa38116a40eaff788846d87cc815d2e1318f44] committed with status (VALID) at peer0-org2:7051 2020-03-08 14:23:03.575 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [83aeeaac47cf6302bc139addc4aa38116a40eaff788846d87cc815d2e1318f44] committed with status (VALID) at peer0-org1:7051 2020-03-08 14:23:03.576 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 003 Chaincode invoke successful. result: status:200
Create another gem:
$ peer chaincode invoke -o orderer0:7050 --tls true --cafile $ORDERER_CA -C mychannel -n marbles --peerAddresses peer0-org1:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1/peers/peer0-org1/tls/ca.crt --peerAddresses peer0-org2:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2/peers/peer0-org2/tls/ca.crt -c '{"Args":["initMarble","marble2","red","50","tom"]}' --waitForEvent ### Should print a similar output 2020-03-08 14:23:40.404 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [8391f9f8ea84887a56f99e4dc4501eaa6696cd7bd6c524e4868bd6cfd5b85e78] committed with status (VALID) at peer0-org2:7051 2020-03-08 14:23:40.434 UTC [chaincodeCmd] ClientWait -> INFO 002 txid [8391f9f8ea84887a56f99e4dc4501eaa6696cd7bd6c524e4868bd6cfd5b85e78] committed with status (VALID) at peer0-org1:7051 2020-03-08 14:23:40.434 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 003 Chaincode invoke successful. result: status:200
Query Gem Information:
$ peer chaincode query -C mychannel -n marbles -c '{"Args":["readMarble","marble1"]}' {"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}
You can also execute the following command to query the chain code log:
$ kubectl logs chaincode_pod_name -n hyperledger ### Should print a similar output invoke is running initMarble - start init marble - end init marble invoke is running initMarble - start init marble - end init marble invoke is running readMarble
Original Link: Hyperledger Fabric 2.0 External Chain Code Actual Warfare-Wizard