Introduction
I began my learning journey of Hyperledger Fabric by studying First Network two years ago. It was a very good sample setup from which I have learnt a lot about this platform. I know many people are taking a similar path. This article written more than a year ago is still one of my most-read articles.
Fabric v2.0 came with a new sample network: Test Network. In a previous article I have already highlighted the difference between these two sample networks. Here I come up this article just about Test Network. My previous work on First Network was showing a step-by-step execution of script byfn.sh
. I took a similar approach in this article as another step-by-step manual execution of script network.sh
. During the process I will make some explanation and discussion on relevant items.
I wish this can also provide you a more complete picture of Test Network. Through this you can grasp some basic operation of bringing up a consortium network and deploying chaincode in Fabric v2.0.
Overview: Test Network
Without repeating what is in previous article, here is some quick information about Test Network,
- A three-organization setup
- One orderer organization:
example.com
, with one orderer - Two peer organizations:
org1.example.com
andorg2.example.com
, each of which has one peer (peer0) - One channel mychannel is created and joined by peers of both peer organizations.
Here is how Test Network looks like.
Test Network comes with a script network.sh
. And here are commands
network.sh up
: bring up network components, one orderer and one peer for each peer organization.network.sh up createChannel
: bring up network components, create mychannel and have all peers join mychannel.network.sh createChannel
: create mychannel and have all peers join mychannel. applicable afternetwork.sh up
is executed.network.sh deployCC
: deploy and interact with Fabcar chaincode. executable after channel is created withnetwork.sh up createChannel
ornetwork.sh createChannel
.
Note that the crypto material by default is generated through cryptogen
. Test Network also supports another way to bring up crypto material using Fabric CA Server. In this article I am using cryptogen
. For information about using Fabric CA Server you can refer to my another article.
While network.sh
comes with several commands as shown above, I break the execution of network.sh
script into several steps, showing how things are done manually. Here are the steps:
- Generate Crypto Material with
cryptogen
- Generate Consortium Genesis Block
- Bring Up Test Network Components
- Join Peers of Both Organizations to mychannel
- Deploy Fabric on mychannel
- Interact with Fabric Chaincode Functions
Demonstration
Step 1–3 is equivalent to executing command
./network.sh up
.
Step 1: Generate Crypto Material with cryptogen
Configuration files
We generate crypto material using cryptogen
in this tutorial. The configuration file tells cryptogen
what type of components and users are needed. In Test Network there are three cryptogen
configuration files inside organizations/cryptogen/
.
For orderer organization, organizations/cryptogen/crypto-config-orderer.yaml
For peer organization Org1, organizations/cryptogen/crypto-config-org1.yaml
For peer organization Org2, organizations/cryptogen/crypto-config-org2.yaml
Binary tools
All the tools and commands (cryptgen
, configtxgen
, peer
, etc.) are stored in fabric-samples/bin/
. Update the PATH to include this directory.
cd fabric-samples/test-network
export PATH=${PWD}/../bin:${PWD}:$PATH
Crypto material generation for all organizations
The resulting materials are all placed inside organizations/
. Orderer organization is stored in organizations/ordererOrganizations/
, and peer organizations in organizations/peerOrganizations/
.
cryptogen generate --config=./organizations/cryptogen/crypto-config-orderer.yaml --output="organizations"cryptogen generate --config=./organizations/cryptogen/crypto-config-org1.yaml --output="organizations"cryptogen generate --config=./organizations/cryptogen/crypto-config-org2.yaml --output="organizations"
Crypto material in network components
These crypto materials generated will be placed into the corresponding network components. It is done through docker compose file. Here is the docker/docker-compose-test-net.yaml
.
Step 2: Generate Consortium Genesis Block
Configuration file
The consortium configuration is in configtx/configtx.yaml
. Here we only show the portion relevant to the consortium genesis block.
Consortium genesis block generation
Here is how we generate this consortium genesis block. We specify the profile TwoOrgsOrdererGenesis. The result is kept in system-genesis-block/
.
export FABRIC_CFG_PATH=${PWD}/configtxconfigtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel -outputBlock ./system-genesis-block/genesis.block
Consortium genesis block in orderer
This consortium genesis block only appears in orderers. In Test Network, it only appears in orderer.example.com. Here is an extract from docker/docker-compose-test-net.yaml
.
Step 3: Bring Up Test Network Components
Configuration file
Use docker-compose to bring up the three network components of Test Network. The docker compose configuration file to be used is docker/docker-compose-test-net.yaml
.
Bring up all network components
IMAGE_TAG=latest docker-compose -f docker/docker-compose-test-net.yaml up -d
Here is how the three containers running in localhost.
Step 4 is equivalent to executing command
./network.sh createChannel
.
Step 4: Join Peers of Both Organizations to mychannel
In Test Network, creating and joining mychannel involves three steps
- use
configtxgen
to generate transactions for mychannel, including configuration transaction and anchor peer update transactions - prepare a channel genesis block for mychannel, which is a file
- join channel using the channel genesis block file
Note: Don’t mix up with this channel genesis block file with the consortium genesis block file in Step 2. Consortium genesis block file contains the setup of the whole consortium of member organizations. A channel is a subset (or a whole set) of the consortium. There can be more than one channels defined in a consortium, to reflect the real business relationship and logic. Each channel begins with a channel genesis block file. In Test Network, we only have one channel, mychannel, and all consortium members, org1 and org2, join this mychannel.
Unlike First Network, Test Network does not come with CLI
container. The channel artifacts are generated and stored in localhost, and peer
commands are issued directly from localhost.
Generate channel artifacts
Channel artifacts for mychannel includes a configuration transaction and anchor peer update transaction for each peer organization. Note that we are using the profile TwoOrgsChannel specified in configtx/configtx.yaml
.
mkdir -p channel-artifactsconfigtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/mychannel.tx -channelID mychannelconfigtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSPconfigtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP
The material generated are all kept in channel-artifacts/
.
Channel genesis block file
The channel genesis block file, block #0 for mychannel, is generated using configuration transaction generated previously.
As we need TLS CA certificates when reaching components, we can define these environment variables for tidy display.
export ORDERER_CA=${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pemexport PEER0_ORG1_CA=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crtexport PEER0_ORG2_CA=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
We target this peer command from peer0.org1.example.com
. Note the variables (in italic) applied in the beginning of this command. We will use this to specify where the peer command is issued.
export FABRIC_CFG_PATH=$PWD/../config/CORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=localhost:7051 peer channel create -o localhost:7050 -c mychannel --ordererTLSHostnameOverride orderer.example.com -f ./channel-artifacts/mychannel.tx --outputBlock ./channel-artifacts/mychannel.block --tls true --cafile $ORDERER_CA
We specify the following in this peer
command
localhost:7050
as orderer. UseordererTLSHostnameOverride
to override with name orderer.example.com. Plus TLS material (orderer CA)- channel name
mychannel
- channel configuration file
./channel-artifacts/mychannel.tx
- output channel genesis block as a file:
./channel-artifacts/mychannel.block
.
Now we have the file mychannel.block
. We will use it to join peers of both peer organizations.
Join peers to mychannel
Peers of both organizations join mychannel using the block file.
CORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=localhost:7051 peer channel join -b ./channel-artifacts/mychannel.blockCORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=localhost:9051 peer channel join -b ./channel-artifacts/mychannel.block
And both share the same blockchain, with height 1 (only block #0 exists).
Update anchor peers
Apply the update transaction.
CORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=localhost:7051 peer channel update -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com -c mychannel -f ./channel-artifacts/Org1MSPanchors.tx --tls true --cafile $ORDERER_CACORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=localhost:9051 peer channel update -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com -c mychannel -f ./channel-artifacts/Org2MSPanchors.tx --tls true --cafile $ORDERER_CA
We specify the following in this peer
command
localhost:7050
as orderer. UseordererTLSHostnameOverride
to override with name orderer.example.com. Plus TLS material (orderer CA)- channel name
mychannel
- update configuration file
./channel-artifacts/OrgnMSPanchors.tx,
which we generate in the beginning of this step
Check the blockchain of both peers: now it should be of height 3 (a new block is committed after each anchor peer update). Blockchain content (hash) in both peers remains identical.
With this, mychannel is created and joined by peers of all organizations.
Step 5–6 is equivalent to executing command
./network.sh deployCC
.
Step 5: Deploy Fabcar on mychannel
We follow the process of lifecycle chaincode to deploy Fabcar on mychannel. In Fabric v2.0, chaincode deployment is done using lifecycle chaincode. For more detail about lifecycle chaincode, you can refer to my previous article.
Package Fabcar chaincode
We first install dependency on the chaincode if not done yet.
# install dependence if not done before
pushd ../chaincode/fabcar/go
GO111MODULE=on go mod vendor
popdCORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=localhost:7051 peer lifecycle chaincode package fabcar.tar.gz --path ../chaincode/fabcar/go/ --label fabcar_1
The result is the chaincode package file stored locally.
Install Fabcar to peers of both organizations
CORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=localhost:7051 peer lifecycle chaincode install fabcar.tar.gzCORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=localhost:9051 peer lifecycle chaincode install fabcar.tar.gz
We will get back package ID after chaincode package installation. Note that they are the same in this tutorial, but it is not necessary as v2.0 this process is done by each organizations separately. There are chances that individual organization extends chaincode. See this article for more detail.
We can check if the installation is successful.
Meanwhile, a new chaincode container image is built, one for each peer. They are not instantiated yet (not running as a docker container yet). This is also why it takes some time as images are being built.
For those who are interested in the chaincode container in Fabric v2.0, you can refer to my another article about this.
It is how the setup looks like. Two chaincode container images are built, but not running yet as container.
Approve chaincode definition for both organizations
We first approve for Org1.
CORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=localhost:7051 peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls true --cafile $ORDERER_CA --channelID mychannel --name fabcar --version 1 --init-required --package-id fabcar_1:2939b5f219f516bc94df2253438bad440d6a91432f6b5bbd17ef05d2228766e8 --sequence 1
With this, we can check readiness of chaincode commit from both organizations. The approval is recorded and both organizations see the same result.
Now we approve for Org2.
CORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG2_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=localhost:9051 peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls true --cafile $ORDERER_CA --channelID mychannel --name fabcar --version 1 --init-required --package-id fabcar_1:2939b5f219f516bc94df2253438bad440d6a91432f6b5bbd17ef05d2228766e8 --sequence 1
And check again the readiness of chaincode commit. Now both organizations approve the chaincode definition.
Commit chaincode definition on mychannel
As this satisfies the lifecycle endorsement policy (majority), the chaincode definition can be committed to mychannel.
CORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=localhost:7051 peer lifecycle chaincode commit -o localhost:7050 --tls true --cafile $ORDERER_CA --peerAddresses localhost:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses localhost:9051 --tlsRootCertFiles $PEER0_ORG2_CA --channelID mychannel --name fabcar --version 1 --sequence 1 --init-required
And we can check the committed chaincode.
And we see the chaincode containers for both peers are instantiated.
Now there are total five containers running in localhost, with the two new chaincode containers, one for each peer.
Now the chaincode is ready for use.
Step 6: Interact with Fabcar Chaincode Functions
Invoke Chaincode Function
We invoke initLedger() functions, specifying peer of both organizations as endorsers. (Note: in configtx/configtx.yaml
, endorsement policy by default is majority. Therefore both organizations are required.)
CORE_PEER_TLS_ENABLED=true CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=$PEER0_ORG1_CA CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=localhost:7051 peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls true --cafile $ORDERER_CA -C mychannel -n fabcar --peerAddresses localhost:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses localhost:9051 --tlsRootCertFiles $PEER0_ORG2_CA --isInit -c '{"function":"initLedger","Args":[]}'
Query Chaincode Function
Then we query function queryAllCars() from both peers.
Summary
We have walked through the script network.sh
for Test Network, showing how to prepare crypto material and channel artifacts for a simple three-organization network. We also deploy Fabcar chaincode using lifecycle chaincode and after deployment, invoked chaincode functions to check if everything is working well.
Hope this tutorial helps you understand Test Network more, and you can use it for testing your own network setup or chaincode development.