Rework: A Companion Guide to Fabric CA Operation Guides for Fabric v2.2

KC Tam
8 min readSep 13, 2020


This is another rework on my recent article. The source is a tutorial of Fabric CA in Hyperledger Fabric documentation. The tutorial was done with version 1.4, and my first work done early this July was also made on the same version. Since then I keep receiving queries to make it running in v2.0, as there are many changes from v1.4 to v2.0 and the commands are not used directly in v2.0. As a result I rewrite this companion guide to make it workable in v2.2.

We follow the same network design and setup. Unlike the previous work, we consolidate the four scripts in our previous work into one, bringing up all the CA and registering all the entities. No CLI containers are needed as we are using binary peer directly in the localhost. Meanwhile, all material is now in this repository.

Here in this article I just highlight the difference from the previous one, and most of the explanation is omitted. You can always refer to my previous work for more detailed explanation.

Network Design and Setup

Here is a quick summary of the network design and setup in this tutorial.

Fabric network looks like this

The fabric network of this tutorial

Each organization comes with an identity CA (organization root CA), which issues the certificates for every component and user in that organization. In addition, one TLS CA is responsible for issuing TLS server certificates for network components (orderer and peer) of ALL organizations. As a result, there are four CAs in this tutorial.

Four CAs in this setup and those entities each CA is issuing certificate


Step 1: Preparation

Clone the repository if not done before

All material is now in the repository. We will refer to these files later. Meanwhile, for sake of consistency, change the directory name to guide/.

cd fabric-samples
git clone
// without changing the document we rename the folder
mv fabric-ca-tutorial-v2 guide

Set path for fabric binaries if not done before

In the script we will directly use fabric-ca-client, which is inside fabric-samples/bin/. Set the PATH environment variable if not done yet

cd fabric-samples
export PATH=$PATH:${PWD}/bin/
// use this to see if we can reach fabric-ca-client
which fabric-ca-client

Tear down everything

We just make sure we have a clean environment. Also, as all crypto material generated is kept in /tmp/hyperledger/, we empty this directory as well.

cd fabric-samples/guide/
docker-compose down
docker rm $(docker ps -aq)
docker rmi $(docker images dev-* -q)
// remove any material of previous deployment
mkdir -p /tmp/hyperledger
cd /tmp/hyperledger
rm -rf *

Bring up two terminals, Org1 and Org2

As we will use two terminals accessing each peer, two files are prepared for all the environment variables needed. Open two terminals and include those variables.

// terminal for Org1 (blue background colour)
source terminalorg1
// terminal for Org2 (green background colour)
source terminalorg2
Terminal for Org1
Terminal for Org2

Step 2: Bring Up the four CAs

In the main terminal,

cd fabric-samples/guide/docker-compose up -d ca-tls rca-org0 rca-org1 rca-org2

Note: in case you have permission problems, change owner of the /tmp/hyperledger/. For example,

sudo chown -R ubuntu:ubuntu /tmp/hyperledger/*

Step 3: Execute Registration and Enrollment scripts

As mentioned before, we have prepared two scripts to process all the registration and enrollment for the network.

Enrol registrar of each CA and register all entities

For each CA, we first enrol a registrar. After that, let this registrar register each entity into the CA database. Note the id.type of each entity. Besides, we have put an attribute abac-init to admin-org2 as we are going to test attribute-based access control (ABAC) later in Step 7 when we deploy chaincode.


Enrol entities one by one for each organization

For each organization, we now enrol each entity (network component and user) from the CAs. In the repository we have prepared three configuration files (orgx-config.yaml) for node OU, one for each organization. They are copied to the organizations respectively.


Step 4: Prepare channel artifacts

We are preparing the three artifacts: one consortium genesis block, one channel transaction and one anchor peer update. We keep them in /tmp/hyperledger/org0/orderer/ directory.

In the main terminal,

configtxgen -profile OrgsOrdererGenesis -outputBlock /tmp/hyperledger/org0/orderer/genesis.block -channelID syschannelconfigtxgen -profile OrgsChannel -outputCreateChannelTx /tmp/hyperledger/org0/orderer/channel.tx -channelID mychannelconfigtxgen -profile OrgsChannel -outputAnchorPeersUpdate /tmp/hyperledger/org0/orderer/org1MSPanchors.tx -channelID mychannel -asOrg org1MSP

Step 5: Bring up the whole network

In the main terminal,

docker-compose up -d

all the components (four peers and one orderer) are up and running

Step 6: Create and Join Channel

Create channel genesis block

Terminal for Org1

peer channel create -c mychannel -f /tmp/hyperledger/org0/orderer/channel.tx -o localhost:7050 --ordererTLSHostnameOverride orderer1-org0 --outputBlock /tmp/hyperledger/org1/peer1/assets/mychannel.block --tls --cafile /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem

Join peers of Org1 to mychannel

Note that the default environment variable is set for peer1-org1. For peer2-org1, we specify the correct CORE_PEER_ADDRESS.

Terminal for Org1

peer channel join -b /tmp/hyperledger/org1/peer1/assets/mychannel.blockCORE_PEER_ADDRESS=localhost:8051 peer channel join -b /tmp/hyperledger/org1/peer1/assets/mychannel.block

Join peers of Org2 to mychannel

Similar, the default environment variable is set for peer1-org2. For peer2-org2, we specify the correct CORE_PEER_ADDRESS.

Terminal for Org2

peer channel join -b /tmp/hyperledger/org1/peer1/assets/mychannel.blockCORE_PEER_ADDRESS=localhost:10051 peer channel join -b /tmp/hyperledger/org1/peer1/assets/mychannel.block

Update anchor peer for Org1

Terminal for Org1

peer channel update -c mychannel -f /tmp/hyperledger/org0/orderer/org1MSPanchors.tx -o localhost:7050 --ordererTLSHostnameOverride orderer1-org0 --tls --cafile /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem

Check if all the four peers having the same ledger

After all peers have joined the same channel, they will have the same ledger. We use peer channel getinfo to check both the blockchain height and current block hash are identical for all peers.

Terminal for Org1

peer channel getinfo -c mychannelCORE_PEER_ADDRESS=localhost:8051 peer channel getinfo -c mychannel
Blockchain information of peer1-org1 and peer2-org1

Terminal for Org2

peer channel getinfo -c mychannelCORE_PEER_ADDRESS=localhost:10051 peer channel getinfo -c mychannel
Blockchain information of peer1-org2 and peer2-org2

Step 7: Deploy chaincode abac

Note since Fabric v2.0, chaincode is deployed through lifecycle chaincode. You can get more ideas about the difference between v1.4 and v2.0 here.

Package chaincode abac

Terminal for Org1

// if not done before
cd fabric-samples/chaincode/abac/go/
GO111MODULE=on go mod vendor
cd fabric-samples/guide
peer lifecycle chaincode package abac.tar.gz --path ../chaincode/abac/go/ --label abac_1

Install chaincode to peer1-org1 and peer1-org2

Terminal for Org1 and Org2

peer lifecycle chaincode install abac.tar.gz
Chaincode installation: Terminal for Org1
Chaincode installation: Terminal for Org2

Approve chaincode for Org1 and Org2

Use the package ID obtained from installation.

Terminal for Org1 and Org2

peer lifecycle chaincode approveformyorg --tls --cafile /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem -o localhost:7050 --ordererTLSHostnameOverride orderer1-org0 --channelID mychannel --name mycc --version 1 --sequence 1 --waitForEvent --init-required --package-id abac_1:fd3168223d23448531a02d6f52e33c5e8ea3a63cbf6e53a0093aa3a60bd70a87
Chaincode approval: Terminal for Org1
Chaincode approval: Terminal for Org2

Commit chaincode

Terminal for Org1

peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer1-org0 --tls --cafile /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem --peerAddresses localhost:7051 --tlsRootCertFiles /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem --peerAddresses localhost:9051 --tlsRootCertFiles /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem --channelID mychannel --name mycc --init-required --version 1 --sequence 1
Chaincode commit

Step 8: Test chaincode abac

Invoke chaincode function Init()

The chaincode abac is the same as abstore. The major difference is that the init() can only be invoked by a user with abac.init attribute set. In our setup, only admin-org2 has this attribute (see Step 3). Environment variable for terminal 1 is set to admin-org1, for terminal 2 is set to admin-org2.

We will invoke this init() in both terminal 1 and terminal 2, and we can see invocation in terminal 1 fails while that in terminal 2 is successful.

On both terminals,

peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer1-org0 --tls true --cafile /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem -C mychannel -n mycc --peerAddresses localhost:7051 --tlsRootCertFiles /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem --peerAddresses localhost:9051 --tlsRootCertFiles /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem --isInit -c '{"Args":["init","a","100","b","200"]}'
Terminal for Org1: failure to invoke init(), as abac.init is not in admin-org1
Terminal for Org2: successful as abac.init is in admin-org2

Query values from both peers

peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
Terminal for Org1: peer1-org1
Terminal for Org2: peer1-org2

Invoke chaincode function invoke()

The “restriction” on admin-org2 is only applicable in init(). We will invoke invoke() in the code using terminal 1. And the invocation is successful.

peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer1-org0 --tls true --cafile /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem -C mychannel -n mycc --peerAddresses localhost:7051 --tlsRootCertFiles /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem --peerAddresses localhost:9051 --tlsRootCertFiles /tmp/hyperledger/org1/peer1/tls-msp/tlscacerts/tls-0-0-0-0-7052.pem -c '{"Args":["invoke","a","b","10"]}'

Query values from both peers

The ledger is updated correctly.

This is the end of demonstration.