Hyperledger Fabric — The Taste of Raft

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

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

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.yaml
export FABRIC_CFG_PATH=$PWD
../bin/configtxgen -profile SampleMultiNodeEtcdRaft -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block
export 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 Org2MSP
docker-compose -f docker-compose-cli.yaml -f docker-compose-couch.yaml -f docker-compose-etcdraft2.yaml up -d
docker exec -it cli bash
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
export CHANNEL_NAME=mychannel
peer 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.pem
peer channel join -b mychannel.block
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=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.block
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 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.pem
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=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.pem
peer 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.block
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=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.block
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
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.

exit
docker 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 bash
export CHANNEL_NAME=mychannel
peer 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.

exit
docker stop orderer.example.com
// now observe logs from another orderer
docker 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 orderer
docker 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.com
docker 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.


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!