Illustrating Endorsement Policy in Lifecycle Chaincode (Hyperledger Fabric Release 2.0)

KC Tam
10 min readFeb 6, 2020

--

Overview

This is another article on Hyperledger Fabric release 2.0. In my previous work I have demonstrated the chaincode operation using lifecycle chaincode provided in 2.0, and compared the difference of the way we are used to in release 1.4. Now we will explore further on the lifecycle chaincode, and specifically about the endorsement policy. We will also see how to set endorsing policy on the application chaincode during the chaincode operation. As usual, to illustrate how things work, we will bring up a network and make some observation on this topic.

Two Types of Chaincodes

As we will involve two types of chaincode here, namely the lifecycle chaincode and application chaincode (or just simply chaincode). Let’s first do a quick introduction here.

Lifecycle chaincode is a system chaincode responsible for chaincode operation. It is all about deploying an application chaincode to a fabric network such that permissioned users can invoke the functions coded in the application chaincode. We do not need to install lifecycle chaincode as It is an integral part of Hyperledger Fabric software. We can use command line interface (CLI) or software development kit (SDK) to interact with the lifecycle chaincode functionality.

Application chaincode (or simply chaincode) is the “smart contract” functionality we always refer to in Hyperledger Fabric projects. It is closely tied to real business world, containing the business logic that the member organizations in a channel agree. Chaincode designers and developers build the chaincode according to the real business needs. The chaincode (and therefore the business application) is useable after being deployed in a fabric network.

This is where the two types of chaincode “meet”. We use lifecycle chaincode functions to make the application chaincode useable. A typical process for deploying an application involves the following steps (see documentation).

  1. Package the application chaincode into chaincode package
  2. Install the chaincode package on selected peers
  3. Approve a chaincode definition per organization
  4. Commit the chaincode definition to the channel when lifecycle endorsement requirement is met

This is how the lifecycle chaincode and application chaincode interacts.

Lifecycle chaincode deploys application chaincode in a fabric network.

Bear in mind that these are two separate chaincodes in a fabric system. Our interest here is to inspect the endorsement policy on lifecycle chaincode. Concretely, how endorsement requirement is met before an application chaincode is useable. To make a contrast, we also show the endorsement policy of application chaincode, which can be different from lifecycle chaincode. We will show this in our illustration.

Setup

In this illustration we use one fabric host which is installed with the fabric images, tools and fabric-sample repository. It can be built based on the documentation (prerequisite and installation). The configuration is largely adapted from First Network, with modification to meet my demonstration purpose.

My fabric network contains

  • one ordering service cluster with five orderers running Raft
  • three organizations (Org1, Org2 and Org3), each of which has one peer (peer0)
  • one channel mychannel is created, and peers in all organizations join the channel
  • anchor peer setting is ignored for sake of simplicity

Clone the repository and follow the instruction to generate the crypto material and channel artifacts.

Once we have both the crypto material and channel artifacts generated, we can use the following script to start a network.

./bringupnetwork.sh

After the script completes, we will have a fabric network setup for our demonstration.

And use this script to tear down all containers and images.

./teardownall.sh

Demonstration Flow

Our application chaincode is Simple Asset Chaincode (SACC), which comes in fabric-samples. This chaincode is simply a key/value store in the ledger, with two functions, set() and get(). Note: SACC comes with Init(). However the Init() has no impact on the chaincode illustration. As a result, in this demonstration we are NOT flag the init-required when approving and committing chaincode.

If we take a look on configtx.yaml, we will see the endorsement policy for both lifecycle chaincode and application chaincode is MAJORITY.

Extract from configtx.yaml

As mentioned before, for illustration purpose, we keep the endorsement policy of lifecycle chaincode of majority, and change the endorsement policy for SACC with “any one of member organizations is good enough”, this is translated into OR(Org1, Org2, Org3) if you are familiarized with the endorsement policy expression. Concretely, every organization can invoke and endorse proposal, and does not require endorsement from other organizations. We will not make modification in configtx.yaml. Instead we will specify when dealing with SACC in chaincode operation.

Lifecycle chaincode and application chaincode can have independent endorsement policy.

The demonstration is to simulate the chaincode operation (lifecycle chaincode) on SACC. The flow is like this

  1. Package SACC into chaincode package
  2. Install chaincode package to peers of all the three organizations
  3. Org1 approves chaincode package, and tries to commit the chaincode. It fails as the majority approval requirement is not met.
  4. Org3 approves chaincode package. Now Org1 can commit the chaincode as majority is already met. Now the application chaincode SACC is useable.
  5. Org1 performs a chaincode invoke on set() with a key/value pair. Since the endorsement policy for SACC is any organization, this chaincode invoke is successful. We also check the value of this key.
  6. Org2 performs a chaincode invoke on set() on the same key with a different value. We will see a failure as Org2 has not approved the chaincode yet. As a result, Org2 cannot interact with this chaincode despite the chaincode package is already installed (see Step 2).
  7. Now we switch back to lifecycle chaincode. Org2 approves the chaincode, and performs chaincode invoke on set() again, and now the chaincode invoke is successful. We can also see the value is correctly shown.

Let’s start now.

Step 1: Package Chaincode

Package SACC. The result is a tar.gz file which is being used in next step.

# If not done before
pushd ../chaincode/sacc
GO111MODULE=on go mod vendor
popd
docker exec cli peer lifecycle chaincode package sacc.tar.gz --path github.com/hyperledger/fabric-samples/chaincode/sacc/ --label sacc_1

Step 2: Install chaincode package to peers of all organizations

# peer0.org1
docker exec cli peer lifecycle chaincode install sacc.tar.gz
# peer0.org2
docker exec -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp -e CORE_PEER_ADDRESS=peer0.org2.example.com:9051 -e CORE_PEER_LOCALMSPID="Org2MSP" -e 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 cli peer lifecycle chaincode install sacc.tar.gz
# peer0.org3
docker exec -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp -e CORE_PEER_ADDRESS=peer0.org3.example.com:11051 -e CORE_PEER_LOCALMSPID="Org3MSP" -e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt cli peer lifecycle chaincode install sacc.tar.gz

A Package ID is received. And we will use this Package ID in coming steps when referring to this chaincode package for SACC.

Step 3: Chaincode commit fails if endorsement policy for lifecycle chaincode is not met

Org1 approves chaincode. Note that we have specified the signature-policy. This is the endorsement policy for the application chaincode (i.e. SACC) we are overriding, as the default is majority. Here we change it to either one of the member organizations.

docker exec cli peer lifecycle chaincode approveformyorg --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 --channelID mychannel --name mycc --version 1 --sequence 1 --waitForEvent --signature-policy "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')" --package-id sacc_1:bf57e4926742fd0dbd8716058897cbb60d3530914529a4b9b46817d2324f6399

Check the chaincode commit readiness now. We see only Org1 has made approval.

docker exec cli peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name mycc --version 1 --sequence 1 --signature-policy "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')" --output json

Now try to commit chaincode.

docker exec cli peer lifecycle chaincode commit -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 --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 --channelID mychannel --name mycc --version 1 --sequence 1 --signature-policy "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')"

The chaincode commit fails, and the error message shows it is due to the endorsement policy failure. Lifecycle chaincode requires endorsement of majority, which is not met as only one organization has made approval so far.

Step 4: Successful chaincode commit after one more organization approves chaincode

Let Org3 approve the chaincode package.

docker exec -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp -e CORE_PEER_ADDRESS=peer0.org3.example.com:11051 -e CORE_PEER_LOCALMSPID="Org3MSP" -e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt cli peer lifecycle chaincode approveformyorg --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 --channelID mychannel --name mycc --version 1 --sequence 1 --waitForEvent --signature-policy "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')" --package-id sacc_1:bf57e4926742fd0dbd8716058897cbb60d3530914529a4b9b46817d2324f6399

And check the chaincode commit readiness again.

docker exec cli peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name mycc --version 1 --sequence 1 --signature-policy "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')" --output json

Now commit chaincode. Note that we need to specify peerAddresses of both Org1 and Org3 (and their CA as TLS is enabled).

docker exec cli peer lifecycle chaincode commit -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 --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.org3.example.com:11051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt --channelID mychannel --name mycc --version 1 --sequence 1 --signature-policy "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')"

And we check the status of chaincode commit.

docker exec cli peer lifecycle chaincode querycommitted --channelID mychannel --name mycc --output json

Now the SACC chaincode is committed and useable in the fabric network.

Step 5: Endorsement policy on application chaincode SACC

Org1 invokes set() with key “name” and value “kc”.

docker exec cli peer chaincode invoke -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 mychannel -n mycc -c '{"Args":["set","name","kc"]}'

And check the value of key “name”,

docker exec cli peer chaincode query -C mychannel -n mycc -c '{"Args":["get","name"]}'

We see Org1 is able to invoke chaincode function. As the chaincode endorsement policy requires only peer from one organization, endorsement by itself (peer0.org1.example.com) is good enough.

Step 6: Organization having not approved chaincode cannot invoke chaincode functions.

Now let Org2 invoke set().

docker exec -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp -e CORE_PEER_ADDRESS=peer0.org2.example.com:9051 -e CORE_PEER_LOCALMSPID="Org2MSP" -e 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 cli peer chaincode invoke -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 mychannel -n mycc -c '{"Args":["set","name","someone"]}'

We see the invoke fails. The reason is that Org2 has not approved the SACC chaincode.

Now we switch back to lifecycle chaincode, to let Org2 approve the chaincode first before invoke set() again.

Step 7: After organization approves chaincode, it can invoke the chaincode functions.

We now make Org2 approve the chaincode, as Org1 and Org3 did before. Note that it is done after the application chaincode is already committed.

docker exec -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp -e CORE_PEER_ADDRESS=peer0.org2.example.com:9051 -e CORE_PEER_LOCALMSPID="Org2MSP" -e 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 cli peer lifecycle chaincode approveformyorg --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 --channelID mychannel --name mycc --version 1 --sequence 1 --waitForEvent --signature-policy "OR ('Org1MSP.peer','Org2MSP.peer','Org3MSP.peer')" --package-id sacc_1:bf57e4926742fd0dbd8716058897cbb60d3530914529a4b9b46817d2324f6399

And now check the chaincode commit status again. We see Org2 has made the approval.

docker exec cli peer lifecycle chaincode querycommitted --channelID mychannel --name mycc --output json

Org2 invokes set() with a new value for the same key “name”.

docker exec -e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp -e CORE_PEER_ADDRESS=peer0.org2.example.com:9051 -e CORE_PEER_LOCALMSPID="Org2MSP" -e 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 cli peer chaincode invoke -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 mychannel -n mycc -c '{"Args":["set","name","someone"]}'

And check the value of key “name”,

docker exec cli peer chaincode query -C mychannel -n mycc -c '{"Args":["get","name"]}'

Org2 can use SACC chaincode after approval is made.

This ends our demonstration.

Observation

We have walked through the demonstration. Here is some observation.

Chaincode approval is made by consortium member organizations individually. The approval process is a kind of consent that the organization accepts this chaincode. An organization of the consortium can keep “not approving” a chaincode, but it cannot stop the chaincode being useable (committed) as far as the pre-agreed endorsement policy is met. In our example, Org2 can keep “not approving” the chaincode, but as far as majority is met, the chaincode is still committed and useable by those organizations which have made approval.

Those organizations “not approving” chaincode can use the chaincode once they make approval on it, even after the chaincode is already committed and used. In our example, Org2 can use chaincode immediately after it makes approval on a committed chaincode.

An important note is that, even though organizations “not approving” chaincode cannot use the chaincode, they still receive new blocks from ordering service and their ledger is updated. In our example, Org2 will receive all the new blocks and its ledger gets updated even though Org2 has not made approval yet. New blocks arrive in all peers in the channel. The RWSet inside the transactions are used to update the ledger World State in each peer. This ledger update does not involve and rely on any application chaincode. The application chaincode is needed when the peer is to perform chaincode functions defined in the chaincode. It is needed when the peer is an endorsing peer, or query through chaincode functions is made on the peer.

Summary

Through this illustration, we know more about the lifecycle chaincode, and how the endorsement policy is working in an application chaincode. We also show the difference of application chaincode from lifecycle chaincode, and the application chaincode can have a different endorsement policy. All we need is to specify the desired endorsement policy of the application chaincode during we approve and commit it.

--

--