Hyperledger Fabric — The Taste of Raft

Let’s try the newly added consensus — Raft, in Fabric

Sam LAM @ OriginBit
Coinmonks
Published in
6 min readApr 18, 2019

--

Hyperledger Fabric Image

Introduction

Since Fabric 1.4.1, Fabric supports a new consensus called Raft, rather than just Kafka and Solo. This short article is to “taste” Raft in Fabric’s Build-Your-First-Network tutorial.

Later, you will see that I shutdown some orderers in which each of them is a Raft leader. it is to show you that orderers using Raft can:

Re-elect leader under crash of leader node automatically

Discover and review best Blockchain softwares

Setting Up the Environment

We use Fabric’s Build-Your-First-Network, which contains:

  • 2 organizations
  • 5 orderers
  • 2 peers in each org, with CouchDB
  • A command line interface to control peers and orderers
  • A channel (mychannel)
  • A simple chaincode by Fabric

First of all, prepare the prerequisites by following instructions below:

https://hyperledger-fabric.readthedocs.io/en/release-1.4/prereqs.html

After prerequisites are satisfied, open your terminal, locate to a place you like, e.g. Desktop.

Then type the following command:

curl -sSL http://bit.ly/2ysbOFE | bash -s

This command is to download Fabric docker images and pull a directory fabric-samples which contains the tutorial files.

Now follow the commands one by one below:

cd fabric-samples/first-network../bin/cryptogen generate --config=./crypto-config.yamlexport FABRIC_CFG_PATH=$PWD../bin/configtxgen -profile SampleMultiNodeEtcdRaft -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.blockexport CHANNEL_NAME=mychannel../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSPdocker-compose -f docker-compose-cli.yaml -f docker-compose-couch.yaml -f docker-compose-etcdraft2.yaml up -ddocker exec -it cli bashCORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
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
export CHANNEL_NAME=mychannelpeer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pempeer channel join -b mychannel.blockCORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 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 peer channel join -b mychannel.blockCORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
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
peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pemCORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:9051 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 peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pempeer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -l java -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')"CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
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
peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
CORE_PEER_ADDRESS=peer1.org2.example.com:10051
CORE_PEER_LOCALMSPID="Org2MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt
peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/peer channel join -b mychannel.blockCORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer1.org1.example.com:8051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt
peer chaincode install -n mycc -v 1.0 -l java -p /opt/gopath/src/github.com/chaincode/chaincode_example02/java/peer channel join -b mychannel.blockCORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
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

In fact, if you think too many commands to type above, you can just type a single command (next time…):

./byfn.sh up -o etcdraft -l java -s couchdb

***Please don’t type the single command here, if you had finished that long list of commands already…

List Existing Nodes

Once the environment is setup, we take a look what nodes are running in the network.

exitdocker ps -a
List of running containers

We can see that we have orderer.example.com, orderer2.example.com,… (read the NAMES column, a bit small, sorry)

In total, we have 5 orderers thus 5 nodes in Raft.

Killing a Leader!

Next, I will shutdown a Raft leader, then see what will happen in the Raft network!

Before killing, let’s find out which orderer is the current Raft leader.

docker logs orderer3.example.com

In my case (your case might be different! But the checking process is the same), I observed the following logs:

[orderer.consensus.etcdraft] start -> INFO 03b Starting raft node as part of a new channel channel=mychannel node=3[...][orderer.consensus.etcdraft] serveRequest -> INFO 05d Raft leader changed: 0 -> 3 channel=mychannel node=3

If you cannot see similar logs like above, no worry, try “docker logs orderer3.example.com” command few more times or just try it later (e.g. 10–20 seconds later).

So, orderer3 is the Raft node 3, and the current leader is node 3. That is, orderer3 is the current Raft leader in my case!

Let’s kill it:

docker stop orderer3.example.com

Then, observe logs from another orderer:

docker logs orderer2.example.com

In my case, I observed:

[orderer.consensus.etcdraft] serveRequest -> INFO 05a Raft leader changed: 0 -> 1 channel=mychannel node=2

Now node 1 (it should be orderer.example.com) is the new Raft leader, which is re-elected automatically.

But to prove that the network is still working properly, let’s perform some transactions:

docker exec -it cli bashexport CHANNEL_NAME=mychannelpeer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'// then you should get 90, or whatever number peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'// then you should get 80, 
// or a number different from the query
// result above, thus the network is working!

Killing a Leader Again!

Kill a leader again to see what will happen. Remember that our current leader is node 1, that is orderer.example.com.

exitdocker stop orderer.example.com// now observe logs from another ordererdocker logs orderer4.example.com[orderer.consensus.etcdraft] serveRequest -> INFO 087 Raft leader changed: 0 -> 4 channel=mychannel node=4

In my case, the new re-elected leader is node 4 (it should orderer4.example.com) now.

Killing a Leader Again and Again!

docker stop orderer4.example.com// now observe logs from another ordererdocker logs orderer2.example.com

Oh? It seems that this time, no new leader is elected (no new election logs appear), why?

What’s Wrong?

In Raft, in order for the network to function properly, the majority of orderers needs to be held. In our case, we have 5 orderers, and I have killed 3 of them, the majority rule is broken!

Fix the Raft

Let turn on a stopped orderer, thus to fix the majority rule:

docker start orderer4.example.comdocker logs orderer4.example.com// you will observe log like this:[orderer.consensus.etcdraft] serveRequest -> INFO 0ab Raft leader changed: 0 -> 2 channel=mychannel node=4

The re-election process is normal again, once the majority rule is fulfilled.

Delete Orderer Data

We now try delete orderer data, then see whether data can be recovered from existing orderer.

docker start orderer4.example.comdocker logs orderer4.example.com// you will observe log like this:[orderer.consensus.etcdraft] serveRequest -> INFO 0ab Raft leader changed: 0 -> 2 channel=mychannel node=4

Conclusion

In this short article, in Fabric’s Raft, we see that orderers can:

  • Re-elect leader under crash of leader node automatically, given that the majority of orderers holds

Thanks! If you like my stories, please follow me for new updates!

--

--

Sam LAM @ OriginBit
Coinmonks

OriginBit Founder | HSUHK Lecturer | Gov Advisory Committee Member(SCSDITE) | GDG Organizer | MBuddhStud(HKU) | BEng(HKUST) | MCSE | MCP | CCNP | CEH