Hyperledger Fabric Account-based Wallet Java Chaincode

Learning by Example — Account-based Wallet Model in A Transaction System

Sam LAM @ OriginBit
Coinmonks
Published in
8 min readFeb 9, 2019

--

Hyperledger Fabric Image

This is a tutorial for Hyperledger Fabric Java Chaincode. There are Go and NodeJS Chaincodes as well and we might talk about them in the future. Also, I adopt the account-based wallet model as an example, and we will use Hyperledger Fabric “Build Your First Network (BYFN)” as our testing environment (v1.4 network).

In case you don’t know how to start a “BYFN” network, I will also provide the steps, no worry. But If you want to learn how “BYFN” works, you could refer to:

(1) Hyperledger Fabric official “BYFN” tutorial

(2) My previous story about “BYFN”

What Account-based Wallet Model Is Basically

Account-based wallet model is one of the implementations of the transaction system, in which, generally speaking, there are wallets and each wallet contains some values or tokens that could be used to transfer. Of course, there are lots of variations on the details of the implementations, such as the token exchange and multi-tokens features.

Features

In the Java Chaincode, we will implement:

  1. User wallet creation
  2. User wallet query
  3. Wallet token transfer (i.e. to send money or tokens to from one wallet another wallet)

[Optional] There are two database options in Hyperledger Fabric to hold the ledger world state — LevelDB and CouchDB, in this tutorial, I will use LevelDB for simplicity.

[Optional] Above, world state (or global state) is not the ledger itself and is separated from the ledger, though it is derived and could be recovered anytime from the ledger. Also, world state is stored in the database to represent the ledger, such that those nodes don’t need to always search the ledger for data retrieval and could retrieve data (states) more quickly from the database.

[Optional] In my load test and some papers about Hyperledger Fabric performance, LevelDB’s performance is better than CouchDB’s. But CouchDB can support rich query that could be used to build a more complex query, compared with the simple key-value query in LevelDB.

The IDE — Tool for Development of Java Chaincode

We use IntelliJ IDEA Community. Also, you need to have JDK. Please install them.

Of course, if you have your own Java IDE, you could also use that. But in this tutorial, I use IntelliJ.

Step 1 — Preparation for Development

Let’s open IntelliJ.

Create New Project in IntelliJ:

Choose Gradle on the left, then Next:

Type your GroupId and ArtifactId. In my case, I use “java_account_model_cc” for both. Then Next:

Then, just next. Now, you should configure your own project location. In my case, I use “~/Desktop/java_account_model_cc”. Click Finish.

1.1 settings.gradle

In the project files on the left, you should have “settings.gradle”. Let’s double click to open it:

Project files on the left panel

Type the following:

settings.gradle

1.2 build.gradle

In the project files panel on the left, you should have “build.gradle”. Let’s double click to open it.

Then, type the following:

build.gradle

*If there is a “Gradle project needs to be imported” popup on the bottom right in your IntelliJ, let’s choose “Import Changes”.

1.3 The Chaincode File

In the project files panel on the left, under src > main > java, right click on it and choose New > Java Class:

Type “org.hyperledger.fabric.chaincode.AccountBasedChaincode” for the Name field:

org.hyperledger.fabric.chaincode.AccountBasedChaincode

Then, you should have the following:

AccountBasedChaincode(.java) above is the place we write Chaincode in Java.

Step 2 — Analysis of the Requirement

Before we start coding, let’s organize what we need to code.

As said in the “Features” section, in the Java Chaincode, we will implement:

  1. User wallet creation
  2. User wallet query
  3. Wallet token transfer (i.e. to send money to from one wallet another wallet)

From this simple requirement above, we need to have the following classes:

  1. Wallet Class
  2. The Chaincode class — holding the Chaincode

And our Chaincode should provide these functions:

  1. Create a wallet
  2. Transfer tokens from one wallet to another
  3. Get (query) a wallet
  4. Init function — called when the Chaincode is instantiated
  5. Invoke function — called when a user wants to call the function (1), (2), or (3)

The Init function must be implemented in Chaincode and will be called automatically whenever we instantiate or upgrade the Chaincode. Usually, it is used to initialize some data in the Blockchain.

The Invoke function is used to receive all user function calls, then based on the call, it invokes (calls) the corresponding function — (1), (2), or (3). It is like a router — routing incoming requests to a different path.

Step 3 — The Wallet Class

Now, we create and code the Wallet class.

Create a new package under “org.hyperledger.fabric.chaincode”:

Call the package Models, then OK:

Package “Models”

Now you should have the following:

Under the Models package, create a new Java class and name it Wallet. (I don’t show detailed steps this time, let’s try yourself)

Creating Wallet class under Models package

Now we code for the Wallet class, let’s type:

Wallet.java

The wallet has a wallet id to identify a certain wallet and a token amount to specify how many tokens the wallet owns.

Note that in the production case, the Wallet class should be (far) more complicated. For example, you probably use “BigDecimal” data type instead of “Double” for tokenAmount. Also, in our case, we support only one token type (i.e. only one currency) in the whole transaction system.

Step 4— The Chaincode Class

Note that in the production case, the codes should be different from mine. These codes are in demo or tutorial purpose mainly.

Step 5 — Running the Chaincode in BYFN

5.1 Install Prerequisites

We use Hyperledger Fabric v1.4 in this tutorial.

First of all, you could install prerequisites by following the official instructions:

  1. Install prerequisites
  2. Install samples, programs, and docker images from Hyperledger Fabric

5.2 Chaincode Preparation

Then, let’s switch to this directory (*supposed that you finish the prerequisite part, you should have all needed files and directories):

cd fabric-samples/chaincode/chaincode_example02/
mv java java_01
mkdir java

Now, copy the following highlighted files in your project directory to ”fabric-samples/chaincode/chaincode_example02/java/”:

Copy the highlighted files to ”fabric-samples/chaincode/chaincode_example02/java/”

5.3 Bring Up the Network

cd ../../first-network
./byfn.sh up -l java

After running this script, you might need to wait for a moment…

*** If you see the following error (instead of other errors), that’s OKAY, just proceed to the next steps ***

Allowable error in our tutorial

Also, remember to start Docker, before you run the command above.

*In case you make something wrongly, you can run the following commands to turn down the network, and bring up again:

./byfn.sh down
./byfn.sh up -l java

Now, we test whether our Chaincode works.

5.4 Access the Cli

There is a cli Docker container created automatically, it is a command line interface to control the nodes.

Let’s get access to the cli:

docker exec -it cli bash

Then, set up environment variables used by some programs:

export CHANNEL_NAME=mychannel
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
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.crt

You could just copy all of them, then paste to your terminal and press “enter”.

These environment variables are used to let some Hyperledger Fabric programs know that we need to use peer0.org1.example.com:7051 to invoke Chaincode functions.

Now, we create two wallets with wallet IDs — “tom” and “sam”:

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:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["createWallet","tom","100"]}'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:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["createWallet","sam","100"]}'

Update: Note that we have two --peerAddresses in order to send to 2 Peers to endorse the transaction, since the default endorsement policy in Build Your First Network (BYFN) is an AND policy, which requires both organizations, Org1 and Org2, to sign each transaction. For more explainations, please refer to here.

You should see something like this in the terminal after running each command above:

Success message from Chaincode

Update: As suggested, --peerAddresses can support array for its argument as well. Instead of the --peerAddresses usage in the Invoke commands above (using --peerAddresses two times for two Peers), you may consider (using --peerAddresses one time and passing it an array of two Peers):

[...] --peerAddresses peer0.org1.example.com:7051 peer0.org2.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 /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt [...]

Now, we get two wallets created above to verify whether they are existing in the Blockchain:

peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["getWallet","tom"]}'peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["getWallet","sam"]}'

You should see something like this in the terminal after running each command above:

Above, we can see that the two wallets created previously can be queried. And both of them have 100 tokens.

Next, we make a transfer transaction — let’s transfer 10 tokens from “tom” wallet to “sam” wallet:

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:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["transfer","tom","sam","10"]}'

Finally, let’s verify two wallets again:

peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["getWallet","tom"]}'peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["getWallet","sam"]}'

Can you have the following result?

Note that now Tom’s wallet remains 90 tokens and Sam’s wallet has 110 tokens, the transaction is done and written into the Blockchain ledger.

Step 6— Cleaning Up

exit
./byfn.sh down

Then, delete directory “fabric-samples/chaincode/chaincode_example02/java” and rename directory ”java_01” back to ”java”.

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