Hyperledger Fabric Private Data Tutorial

Hyperledger Fabric

What is Hyperledger Fabric? In layman’s terms, it is a framework Blockchain implementation of a permissioned network, tailored made for enterprises. So…what is Blockchain and what do you mean by permissioned? Basically, in a very general sense, a Blockchain is a distributed (everyone in the network has the same information) and decentralized (no one participant has more power than another participant over the network) transaction ledger that is immutable — extremely difficult to tamper with.

Okay cool, what about permissioned network? It just means that you need permission to belong to the network, every participant in the network has a very clear identity and everyone in the network knows who’s who. So is there a “permissionless” blockchain implementation or more commonly known as public blockchains? Of course! There is Bitcoin and Ethereum, just to name a couple of the biggest public blockchains.

OK so now that we have that out of the way, let’s get started with the tutorial! To keep things simple, please follow this well explained Building Your First Network tutorial to set up your first Hyperledger Fabric network so we can get started with understanding private data and its importance. Be sure to install the prerequisites and the binaries and download the Hyperledger Fabric libraries using this (once you have set up your GOPATH variable):

cd $GOPATH/src/github.com
mkdir hyperledger
cd hyperledger
git clone https://github.com/hyperledger/fabric.git

Once you have your network up and running by executing the command:

./byfn.sh up

And hitting the y key at this prompt:

Starting for channel 'mychannel' with CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n] y

You should see this output if everything ran correctly: (if not, review that you followed the steps correctly)

========= All GOOD, BYFN execution completed =========== 


_____ _ _ ____
| ____| | \ | | | _ \
| _| | \| | | | | |
| |___ | |\ | | |_| |
|_____| |_| \_| |____/

Awesome! So exactly, what did we just do? We started up a network with 2 organizations (Org1 and Org2), 2 peers per organization and an orderer on a channel called mychannel. A channel is basically a private ledger where only authenticated participants in that channel can interact and see the data sent, any participants that are not on that channel, will not be able to see anything that goes on there. A network can have many channels (and organizations can belong to more than one channel), but we’re starting off with just one, to keep things simple.

Let’s get to the fun stuff, writing some code and exploring private data! First off, what is private data? The team at Hyperledger Fabric didn’t want to complicate this so it is just what it means, data that you want to keep private to yourself or to specific organizations, without letting anyone (that you don’t want) know the data that you’re storing or sending, easy huh? So this lets organizations transact with each other on the same channel without fear of having an organization see private information (for example, the price that you’re paying to provider A for product X should be kept secret from the price you’re paying to provider B for the same product X and vice versa, right?).

I can hear you guys asking, why not just have several channels Enrique, instead of going through the trouble of implementing private data? That’s a fair question, and there are situations when you should use completely different channels and not use private data. But, in my opinion, the basic rule of thumb is, if you need to share most of the information publicly between the organizations in a channel, and keep some of the data private to a few organizations, go for private data, else (get it?) just implement a new channel with the required organizations, even though managing many channels on a network can get to be quite a hassle.

Whew! Now let’s really start with the code!

So how do you define who gets to see and receive the private data? That is all done with a JSON file, typically called collections_config.json and is passed as a parameter whenever you are going to instantiate a specific chaincode with the `— collections-config` flag. For example, this is the collections_config.json we’re going to be using today:

[{"name": "collectionMedium","policy": "OR('Org1MSP.member', 'Org2MSP.member')","requiredPeerCount": 0,"maxPeerCount": 3,"blockToLive":1000000},{"name": "collectionPrivate","policy": "OR('Org2MSP.member')","requiredPeerCount": 0,"maxPeerCount": 3,"blockToLive":5}]

This file just spells out who’s going to going to be able to see each collection (the private data), set with the “policy” property. collectionMedium is going to be available to members from Org1 and members from Org2, so…everyone. On the other hand, collectionPrivate (creative, I know) is just going be visible to members from Org2, nobody from Org1 will be able to see this info!
The “blockToLive” property lets you set the amount of blocks that you want this information to stay on the blockchain — 5 for now (0 if you want to keep the data forever on the blockchain).
Save this file in the following directory with the name collections_config.json:

fabric-samples/chaincode/medium/

Open up your editor of choice (Visual Studio Code for me) and let’s write a simple chaincode (fancy name for Fabric blockchain code) in Go to tap into this private data potential!

I’ll walk you guys down this simple code to demonstrate private transactions. First things first, importing what you need:

Importing packages

Pretty straightforward here, we’re going to be working with JSON, printing some stuff with fmt and converting strings to numbers with strconv. The most important thing to note here is the last 2 packages listed, you need these to be able to work with the chaincode.

Creating structs

We’re going to create two structs: product and productPrice. The struct product is going to hold the publicly available information listed above. On the other hand, productPrice is only going to hold BuyPrice and SellPrice, which we’re going to pretend is sensitive(private) information we don’t want anyone to know. Notice the ‘json’ tag at the end of each property, this is to allow you to customize the encoded JSON key names.

Declaring empty chaincode struct

We’re going to be filling MediumChaincode with different Chaincode shim functions so we can communicate with the blockchain.

Simple and empty Init function

This function is called whenever you initialize (or upgrade) the chaincode on the channel. It’s used to initialize any data, store information in the blockchain, etc — we’re not doing any of that today, just printing out with fmt.Println that the chaincode has been successfully initialized.

Creating Invoke function

This function is called every time you try to do a transaction on the chaincode. So here we see the first use of shim with stub.GetFunctionAndParameters(), which does just that, get the function name and the parameters you sent to the chaincode. Now that we have the function name, we do a simple switch-case to determine what we do. I created three different functions to portray what we want to do (show private transactions), createProduct, getProduct, getProductPrice.

createProduct function

This is the first function, createProduct, which will allow us to create a new product and store it in the blockchain. The first part of the function is assigning variable names to the parameters we passed initially to the Invoke function. We pass the variables corresponding to the product struct (that is everything except buyPrice and sellPrice) and create product (variable name). One thing to note here is that we can’t store structs as structs on the blockchain, so we have to turn it into bytes with json.Marshal.

We do the exact same thing with productPrice (passing in the appropriate parameters). Now this is were we come in contact for the first time with the blockchain (beautiful moment) using shim’s PutPrivateData which does just that. Fabric’s blockchain basically works like a hashmap — whenever you store data in the blockchain, you specify a key (identifier) and value (the information you’re storing). So in this case, we’re storing the productBytes as the value with key id under the collection collectionMedium which as we defined earlier (in collections_config.json), is open for access to Org1 and Org2.

In the same way, we’re storing productPriceBytes with key id, the only difference being the collection were we’re storing it, this time it’s collectionPrivate, so this means that only Org2 will be able to see the information, and Org1 will only see the hash of what was stored. Finally, we’re returning the product we created initially.

getProduct function

This is the getProduct function that is in charge of retrieving the products from the collection collectionMedium (in this example, information available to Org1 and Org2) that holds all of the product’s information except BuyPrice and SellPrice. Once it’s done, it returns the product requested to you.

getProductPrice function

This is the getProductPrice function that retrieves the product’s BuyPrice and SellPrice with id id from the collection collectionPrivate (only Org2 will be able to correctly retrieve this info). Finally, it returns productPrice to you.

And with that, we are done with the chaincode! Hope you guys are still with me!

Save the file inside (with the name medium.go):

fabric-samples/chaincode/medium/

Once that’s done, run the command:

go build

This will compile your code into an executable named medium.

Now we’re ready to install and instantiate the chaincode onto the peers on Org1 and Org2 and try out private transactions!

Open up your terminal and write the following command to connect with the CLI container you created previously:

docker exec -it cli bash

Once you’re in, you should see something similar to this:

root@162d8da28704:/opt/gopath/src/github.com/hyperledger/fabric/peer#

Navigate to the directory:

/opt/gopath/src/github.com/chaincode/medium

and run the command

ls

You should see the following files:

collections_config.json  medium  medium.go

If everything is correct, let’s deploy this chaincode onto the peers!

You should be identified as Org1 on the cli but just in case, run the commands:

export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID=Org1MSP
export 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.crtexport CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp

This will set the variables necessary to identify yourself as member of Org1, more specifically as peer0 of Org1. Now run the following command to install the chaincode on peer0:

peer chaincode install -n medium -v 1.0 -p github.com/chaincode/medium/

The -n parameter is the name of the chaincode, -v is the version you’re going to deploy, and -p is the path to chaincode.

You should see the following output:

Installed remotely response:<status:200 payload:"OK" >

Now we’re going to switch to peer1 of Org1 changing the following variable:

export CORE_PEER_ADDRESS=peer1.org1.example.com:7051

Install the chaincode once again.

Now we need to switch over to Org2, so run the following commands which will do just that.

export CORE_PEER_ADDRESS=peer0.org2.example.com:7051
export CORE_PEER_LOCALMSPID=Org2MSP
export 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.crtexport CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp

And install the chaincode on both peers (peer0 and peer1, with the same method we used before).

Now it’s time to instantiate this chaincode on the network, and we do that running the following commands!

export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pempeer chaincode instantiate -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C mychannel -n medium -v 1.0 -c '{"Args":["init"]}' -P "OR('Org1MSP.member','Org2MSP.member')" --collections-config  $GOPATH/src/github.com/chaincode/medium/collections_config.json

Now this might seem a bit confusing but the first command is just creating a variable with the directory of where the certificate for the orderer is located. The second command effectively instantiates the chaincode on the channel mychannel. The -o flag tells what the orderer endpoint is, — tls tells it to use TLS when communicating with the orderer endpoint mentioned. The — cafile uses the ORDERER_CA variable created in the first command. The flag -C is to declare the channel name, -n the chaincode name, -v the version, -c the command to send to the chaincode, which in this case is init. -P is the endorsement policy for this chaincode.

Most importantly is the — collections-config flag which dictates the path to the collections_config.json file, this holds the information on what collections will be used and who is going to be allowed to access that information (extremely important for private data).

You should see this output if everything ran correctly:

INFO 001 Using default escc
INFO 002 Using default vscc

Let’s test this code out! Run this command to create the product log.

peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C mychannel -n medium -c '{"Args":["createProduct","1","log","brown","1m","2m", "100", "200"]}'

As Args or parameters of the function we’re passing: the id, the name of the product, color, length, width, BuyPrice and SellPrice — in that order (as that was the order we set to capture the values in the chaincode). You should see the following, remember you are running this command as peer1 of Org2. (double check by echoing $CORE_PEER_ADDRESS):

Chaincode invoke successful. result: status:200 payload:"{\"Id\":\"1\",\"Name\":\"log\",\"Color\":\"brown\",\"Length\":\"1m\",\"Width\":\"2m\"}"

Now run the following command to retrieve the product info (not its price…yet):

peer chaincode query -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C mychannel -n medium -c '{"Args":["getProduct","1"]}'

You should see this output:

{"Id":"1","Name":"log","Color":"brown","Length":"1m","Width":"2m"}

Now let’s try querying the product’s price:

peer chaincode query -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C mychannel -n medium -c '{"Args":["getProductPrice","1"]}'

And this is the output!

{"Id":"1","BuyPrice":100,"SellPrice":200}

Great! As a peer of Org2, we can see the BuyPrice and SellPrice of the products (so everything’s going according to plan!).

Now let’s drive the point home and see if we can see the same information as a peer of Org1.

So let’s change organizations and peers with this command:

export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID=Org1MSP
export 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.crtexport CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp

Now let’s try using the same queries to see if we can see the info.

peer chaincode query -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C mychannel -n medium -c '{"Args":["getProduct","1"]}'

Output:

{"Id":"1","Name":"log","Color":"brown","Length":"1m","Width":"2m"}

Alright awesome! So we can query the log basic info, now for the last part, can we see the BuyPrice and SellPrice of the product log as a peer of Org1? Let’s try!

peer chaincode query -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C mychannel -n medium -c '{"Args":["getProductPrice","1"]}'

And we should see (something along those lines):

Error: endorsement failure during query. response: status:500 message:"GET_STATE failed: transaction ID: 532c167303a90abbc6c29f0e8c91619f1f74bd236a3eb0bac73360abcb2114eb: private data matching public hash version is not available. Public hash version = &version.Height{BlockNum:0x6, TxNum:0x0}, Private data version = (*version.Height)(nil)"

Great!! So because we’re trying to query the product’s BuyPrice and SellPrice as a peer of Org1, we get the message above, that we can’t see the data because its private — we can only see information about its hash! So this means that the sensitive data that Org2 stores in the blockchain, will remain private only to the organizations that are detailed in the collections_config.json file.

You can update the collections_config.json file anytime you want, you just need to upgrade the chaincode and pass in the location of the modified collections_config.json file.

Conclusions

So we have successfully:

  • Deployed a Hyperledger Fabric blockchain network
  • Wrote a new chaincode called medium from scratch
  • Wrote a collections_config.json file to determine who can see the private data
  • Installed and instantiated the chaincode on the channel mychannel
  • Tested how private data works on the blockchain

So we have done a lot today! Hope you guys enjoyed the tutorial and learned how to implement private data into your Hyperledger Fabric project!

You can download the code used today from: https://github.com/macienrique/hfPrivateData

Until next time guys! Take care!

--

--