Add an Organization to a Decentralized Fabric Network with No System Channel (Fabric v2.3)
Introduction
In my previous article we have seen how to bring up a decentralized fabric network with no system channel. With decentralization, there is no separate orderer organization and orderers are contributed by each participating organization. Each organization now does not need to use an orderer from a third party. We also see the simplified process when the system channel is no longer needed any more.
This article is built upon the previous one. Here I am showing how to add a new organization, with her own orderer and peer, joining a running network. We will see the overall process, and see how we can do configuration updates just on the application channel (remember, no more system channel) to achieve this.
Overview
We are building things on top of my previous article. First we have a network of two organizations (org1 and org2). After the network is running, we add org3. The overall process follows this tutorial. In the past when the system channel was here, we needed to modify both system channel and application channel. See these two articles for further explanation (link, link).
Thanks to Fabric v2.3 we now can get rid of the system channel. We only work on the application channel when we bring up a channel and make configuration updates.
The scenario is like this. We now have a two-org fabric network up and running. An application channel is running and a chaincode is deployed. Now we have the third organization joining this network (channel). The components of this new organization are identical to those existing ones, that is, there is an orderer and a peer.
The flow is straightforward and logical. The most critical part is to update the configuration to include the new organization MSP. Besides, as a new orderer is joining the cluster, the configuration update needs to include this as well. After this is properly done, we can bring up the components of the new organization and join them to the existing channel. If chaincode is used in this new organization (for query or for endorsement), the chaincode needs to be installed and approved by the new organization.
Certain modification is made on the configuration files. We update the docker-compose.yaml file to include rca-org3 (CA for org3), peer1-org3 and orderer1-org3. We also add those lines for org3 in the script for crypto material. Following the tutorial, we separate a configtx file for org3 (org3/configtx.yaml
), for generating the org3MSP when we create the configuration update.
Here is the repository of this setup.
Demonstration
Note: the first four steps are similar to my previous work. The difference is the addition of configuration for org3. For detail please refer to my previous article.
Step 1: Prepare crypto material for all the three organizations
We use the same logic as the previous setup. Here we combine the two scripts (allCAnReg.sh
and enrollAllOrgs.sh
) into one file, start_cryptomaterial.sh
. Crypto material for org3 are added in this script..
Modify the first several lines to reflect your setup. For example, change the user (mine is vagrant) and permission setting if needed.
cd super-network
./start_cryptomaterial
After the script is completely executed, we see the crypto material ready in /tmp/hyperledger/
directory.
Step 2: Bring up a fabric network with both org1 and org2
docker-compose up -d orderer1-org1 peer1-org1 orderer1-org2 peer1-org2
Step 3: Create application channel mychannel and joins all components to mychannel
From now on we prepare two terminals, with proper environment settings. One for org1 and one for org2.
For terminal for org1
source term-org1
For terminal for org2
source term-org2
Now prepare the genesis block for mychannel.
On terminal for org1
configtxgen -profile SampleAppChannelEtcdRaft -configPath ${PWD} -outputBlock mychannel.block -channelID mychannel
On terminal for org1, join orderer1-org1
osnadmin channel join --channelID mychannel --config-block mychannel.block -o localhost:7080 --ca-file $ORDERER_CA --client-cert /tmp/hyperledger/org1/admin/tls-msp/signcerts/cert.pem --client-key /tmp/hyperledger/org1/admin/tls-msp/keystore/key.pem
On terminal for org2, join orderer1-org2
osnadmin channel join --channelID mychannel --config-block mychannel.block -o localhost:8080 --ca-file $ORDERER_CA --client-cert /tmp/hyperledger/org2/admin/tls-msp/signcerts/cert.pem --client-key /tmp/hyperledger/org2/admin/tls-msp/keystore/key.pem
On terminal for org1, join peer1-org1
peer channel join -b mychannel.block
On terminal for org2, join peer1-org2
peer channel join -b mychannel.block
With this, all the components from both org1 and org2 have joined mychannel. And we will test it with our sacc chaincode.
Step 4: Test with sacc
Again we skip the detail of the lifecycle chaincode, and only observe the result.
Invoking set by admin-org1 (and using orderer1-org1), and results are obtained from the peer of both org
Invoking set by admin-org2 (and using orderer1-org2), and results are obtained from the peer of both org
Step 5: Fetch and inspect configuration block
Now we are ready to work on the addition of org3 into mychannel. We first fetch the configuration block of mychannel (not system channel!) and make some inspection on it.
On terminal for org1, fetch the configuration block
peer channel fetch config -c mychannel
We see the latest block is #5, and the configuration block we are interested is block #0.
Extract the relevant information, we have the config.json
for inspection.
configtxlator proto_decode --input mychannel_config.block --type common.Block --output mychannel_config.jsonjq .data.data[0].payload.data.config mychannel_config.json > config.json
We will expand the config.json. First we see the structure we are interested in. We first locate Application (line 4–421) and Orderer (line 422–898). We expand and locate where we make modifications.
Application
Now expand the Application object. We see both org1MSP and org2MSP. Zoom in the structure inside org1MSP. Note that inside values, we only have MSP.
We will add org3MSP later.
Orderer
Then expand the Orderer object. We again see both org1MSP and org2MSP. Again, zoom in the structure inside org1MSP. Now we see inside values we have Endpoints and MSP.
We will add org3MSP later.
Consenter inside Orderer
We further scroll down the Orderer we will see ConsensusType, and inside which we see consenters. It is where we specify orderers which will form the raft cluster. We will add that for orderer1-org3 here later.
As a result, we will work on these three parts to include configuration for org3. They are
- Add org3MSP in Application
- Add org3MSP in Orderer
- Add orderer1-org3 in Consenter inside Orderer
Step 6: Create MSP for org3
Now we will use org3/configtx.yaml
to create the org3.json
, the material will be used to modify the configuration file in step 5.
cd org3
configtxgen -configPath ${PWD} -printOrg org3MSP > ./org3.json
If we take a look at org3.json, we see it is similar to the org1MSP and org2MSP on both Application and Orderer objects in step 5. We notice that inside values, here we have both Endpoints and MSP. We will take out Endpoints when we add this to Application.
Step 7: Create configuration file to include org3 information
Here I directly modify the file with an editor. But this can be better done with proper jq
commands.
Copy the config.json
to modified_config.json
, and we work on modified_config.json
.
cp config.json modified_config.json
Application
First, in the Application object, we insert from org3.json
. Note we take out Endpoints as it is not needed. Only MSP is needed in values.
Orderer
Then in the Orderer object, we insert from org3.json
. Here we need to include the Endpoints, and we can see the addresses is correct for orderer1-org3:7050.
Consenter inside Orderer
Note: the certificate is base64 encoded. We can use base64 to make the conversion.
base64 /tmp/hyperledger/org3/orderer1/tls-msp/signcerts/cert.pem -w 0
We have modified the three parts to include org3 information. Now it is ready for the standard process to create a transaction signed by both org1 and org2 to update the configuration.
Step 8: Create configuration update transaction with envelope
This is the standard process through a series of encoding, comparison, decoding, adding envelope, and encoding.
configtxlator proto_encode --input config.json --type common.Config --output config.pbconfigtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pbconfigtxlator compute_update --channel_id mychannel --original config.pb --updated modified_config.pb --output org3_update.pbconfigtxlator proto_decode --input org3_update.pb --type common.ConfigUpdate --output org3_update.jsonecho '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":'$(cat org3_update.json)'}}}' | jq . > org3_update_in_envelope.jsonconfigtxlator proto_encode --input org3_update_in_envelope.json --type common.Envelope --output org3_update_in_envelope.pb
Now we have the configuration update file org3_update_in_envelope.pb
.
Step 9: Sign and submit the update transaction by both org1 and org2
As this configuration update requires the majority of organizations, we first let org1 sign the transaction, and let org2 update (implicitly sign and then send) the transaction. Before we first check the blockchain height, which is 6.
Now org1 signs the transaction (terminal for org1)
peer channel signconfigtx -f org3_update_in_envelope.pb
And org2 signs and submits the transaction (terminal for org2)
peer channel update -f org3_update_in_envelope.pb -c mychannel -o localhost:8050 --ordererTLSHostnameOverride orderer1-org2 --tls --cafile $ORDERER_CA
And we check if a new block (configuration block) is received in mychannel. Now block height is 7.
This is the most tedious part. We have updated the configuration for mychannel such that it contains both the orderer1-org3 and any peers for org3. We now can join both components to mychannel.
Step 10: Bring up orderer and peer for org3 and join mychannel
We now bring up components for org3. Note that they are not part of mychannel yet.
docker-compose up -d orderer1-org3 peer1-org3
Now we bring up another terminal for org3.
source term-org3
And we first join orderer1-org3 to mychannel. Note that we are using the same mychannel.block
, created in step 3, the genesis block from which the orderer can learn the whole chain after it joins the raft cluster.
osnadmin channel join --channelID mychannel --config-block mychannel.block -o localhost:9080 --ca-file $ORDERER_CA --client-cert /tmp/hyperledger/org3/admin/tls-msp/signcerts/cert.pem --client-key /tmp/hyperledger/org3/admin/tls-msp/keystore/key.pem
And from the log we see a cluster of three members are formed, and all blocks are synchronized. Orderer1-org3 is now ready to use.
Now join the peer1-org3
peer channel join -b mychannel.block
And from the log we see all blocks are synchronized as well.
Now we can take a look on peer1-org3, and see if the blockchain height is the same as other peers.
Now we have org3 successfully joining mychannel, with orderer1-org3 in the raft cluster, and peer1-org3 fully in synchronization with other peers.
Step 11: Install and approve chaincode sacc and make observation
To see if org3 is functioning well, we install and approve the chaincode, like what we did on org1 and org2 in step 4.
First, we check if we can read the same value (Amy) per our last update on peer1-org3.
Then we check if we can invoke and change to another name (John). Note we only need two out of three organizations as our endorsement policy is majority. Here I am using peer1-org1 and peer1-org3 for endorsement.
Now check all the three peers, and the ledger is updated.
So everything works fine. Org3 successfully joined mychannel.
Summary
Through this demonstration we can see how to add an organization in a decentralized fabric network, in which organizations come with their own orderer(s) and peer(s). Meanwhile, the introduction of no-system-channel setup in Fabric v2.3 simplifies the process as now we do not work on the system channel any more. Everything is done in the application channel directly.