[CouchDB] Hyperledger Fabric Account-based Wallet Java Chaincode
CouchDB Version — Account-based Wallet Model in A Transaction System
This is a tutorial for Hyperledger Fabric Java Chaincode with CouchDB.
Basically, this tutorial is the CouchDB version of my previous story — Hyperledger Fabric Account-based Wallet Java Chaincode, but with some new functions to demonstrate what the CouchDB could provide us — Rich Query, that enables us to query our blockchain data in a more flexible way, such as a query of “all wallets with balance greater than $10”. While in LevelDB, data is simply stored as a key-value pair.
Note that, in general, LevelDB has a better performance than CouchDB’s, while CouchDB could provide us Rich Query. The option depends on the use case.
Before We Start
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:
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:
- User wallet creation
- User wallet query with a walletId
- Wallet token transfer (i.e. to send money or tokens to from one wallet another wallet)
- User wallet query with a condition that the token amount needs to be greater than a certain value (new feature in this CouchDB version tutorial!)
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:
Type the following:
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:
*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:
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:
- User wallet creation
- User wallet query with a walletId
- Wallet token transfer (i.e. to send money to from one wallet another wallet)
- User wallet query with a condition that the token amount needs to be greater than a certain value
From this simple requirement above, we need to have the following classes:
- Wallet Class
- The Chaincode class — holding the Chaincode
And our Chaincode should provide these functions:
- Create a wallet
- Transfer tokens from one wallet to another
- Get (query) a wallet with a walletId
- Get (query) some wallets with a condition that the token amount needs to be greater than a certain value
- Init function — called when the Chaincode is instantiated
- Invoke function — called when a user wants to call the function (1), (2), (3), or (4)
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), (3), or (4). 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:
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)
Now we code for the Wallet class, let’s type:
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 there are two new functions in this tutorial, compared to the previous one — query() and getWalletsWithTokenAmountGreaterThan(). I have put some comments above them.
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:
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/”:
5.3 Bring Up the Network
cd ../../first-network
./byfn.sh up -l java -s couchdb
Note that this time, there is a “-s couchdb” to enable CouchDB.
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 ***
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 -s couchdb
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","200"]}'
Note that Tom has $100 and Sam has $200.
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:
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. Also, Tom has 100 tokens and Sam has 200 tokens.
Next, we make a transfer transaction — let’s transfer 50 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","50"]}'
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 50 tokens and Sam’s wallet has 250 tokens, the transaction is done and written into the Blockchain ledger.
Test the New Function for CouchDB
Now we try our new functions for this CouchDB version tutorial.
Remember that:
- Tom now has $50
- Sam now has $250
We will try to query all wallets with a token amount:
- > $40
- > 200
In (1), we should be able to obtain both Tom’s and Sam’s wallets.
In (2), we should be able to obtain Sam’s wallet only.
Now, let’s query:
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["getWalletsWithTokenAmountGreaterThan","40"]}'peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["getWalletsWithTokenAmountGreaterThan","200"]}'
Can you have the following result?
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!