During our latest blockchain projects we have been using the Hyperledger Fabric (HF) framework. Which is a great framework for creating permissioned distributed ledgers with custom business/validation logic on the ledger itself. When you look at the modular architecture of the platform you see that it has been created from the ground up to be an enterprise grade permissioned distributed ledger implementation. That’s great but it also makes it more complex to work with it. There are a lot of moving parts required to start working with HF which are still evolving and the documentation is very limited. Even though there are already stable versions available, version 1.0 and recently version 1.1 was released as well.
Because of this complexity and the lack of advanced examples we found it difficult to create our first distributed ledger application. It has a steep learning curve. There are a lot of new things you need to wrap your head around. We didn’t find any relevant best practices. That’s why we decided to open source a few of the tools we have created for our first HF project. These tools are the result of a lot of blood, sweat and tears while trying to understand HF and creating a project using all of its components. We are not stating that this is the best way to setup a HF project, we are still learning every day and these tools will evolve while learning. We open sourced our tools so that all future HF projects can benefit from these learnings. Our tools are available for your and our next HF projects. Feel free to use them and to contribute, because this is the only way to make these tools better.
HF uses certificates to identify all the stakeholders from the distributed ledger network (organisations, users, services, …). We noticed that creating a good certificate tree is a crucial part of the distributed ledger network but that it’s very hard to set it up using the HF CA. We didn’t want to fiddle with multiple ca servers for each intermediate CA. Also setting up HF on multiple machines within AWS seemed quite a challenge and there isn’t much information to find about this. That’s why we created our own scripts to do all of this and we combined them within our network-setup script. This script is still a little rough around the edges because it was creating while still figuring everything out.
Another thing we liked about HF is that it’s build using existing and commonly used programming languages like Go and Node.js. There is a powerful API available to interact with the blockchain written in Node.js and it’s even possible to write the smart contracts (also known as chaincode) using Go and Node.js (since version 1.1). The problems we experienced with these API’s is that they are very low-level API’s and we needed to rewrite a lot of boilerplate code all the time. That’s why we decided to create higher level API’s that wrap around the existing API’s to reduce the amount of errors and code duplication. These higher level API’s are created from our learnings and all of the projects using them will benefit from these learnings. We created such an API for the client and we created one for Node.js chaincode as well. In the future we would also like to create a similar set of tools for the Go chaincode.
We are used to making websites and web applications where we are very spoiled with all the different development tools that exist for this like automatic building of your web application and live reloading while developing. So developing on HF was really frustrating at the beginning. Ok, there is a tutorial on setting up your development environment … but it’s quite complex and you still need to do a lot of things manually. Because of this we tried to recreate a development environment that closely matches those of developing for web which watches your chaincode and automatically upgrades the chaincode when something changes. This tool also has generators in place for quickly setting up a chaincode project which integrates our node-chaincode-utils. More information about the chaincode-dev-setup tool can be found on github.
Setting up a simple token example
In the end we wanted to create a clear example project that integrates all of these tools. We created a simple token. There is the balance-transfer example but it’s too simple. It’s possible for anyone to send tokens from any wallet, without any form of security. That’s why we decided to create our own example of a simple token, the kuma-token. This post will describe high-level how we created this project. We will not go into too much details on how to install the necessary tools and we assume the people who are going to try this have basic knowledge about Node.js, npm, etc.. More information and installation instructions for each of the tools can be found on their github page. The full source code of this example can also be found on github.
For setting up the network the network-setup tool has been used. With this tool it’s possible to bootstrap an initial example configuration by executing the bootstrap command:
kuma-hf-network bootstrap .
This command will output two files,
- aws.json which contains the configuration for deploying the network to Amazon AWS
- crypto_config.yaml which contains the configuration for generating all the cryptographic and channel artefacts and all the docker-compose files needed to setup the network.
For now we will focus on the crypto_config.yaml because we don’t need Amazon AWS while developing. The configuration for the kuma-token example can be found here. It exists out of two organisations "KumaOrg" and "AuthOrg" with certificates coming from one Root CA. There is also one channel provided the “kumachannel” and both organisations are a member of it. The “Devmode” configuration option makes it possible to supply the organisation which will be used for setting up a node for the development environment using the chaincode-dev-setup. This is an optional parameter you can also choose to use the default configuration provided with the chaincode-dev-setup. If you want to use your own configuration in your development environment then it is important that this organisation has at least one orderer and one peer configured. If everything is configured correctly all the cryptographic, channel artefacts and the docker-compose files can be generated using the generate command:
kuma-hf-network generate ./configuration/crypto_config.yaml --genPath ./generated
Now that everything is generated it is possible to start developing the chaincode and use the chaincode-dev-setup for testing.
kuma-hf-chaincode-dev init .
This command initialises an empty chaincode project. With a chaincodes directory that will contain all the chaincodes and a common directory that can contain shared resources between all the chaincodes.
The package.json file at the root contains configuration that is needed for the chaincode-dev-setup and network-setup to work. It has a list of all the chaincodes and it contains the different paths which are needed for building and deploying the chaincodes. Now everything is ready to start creating the first chaincode. Before you continue don’t forget to run
npm install to install all the dependencies needed to continue with the rest of this tutorial.
kuma-hf-chaincode-dev create-chaincode your-token
This command will generate a new chaincode directory and add the new chaincode to the chaincodes list within the package.json. The chaincode contract functions can be added to the Chaincode class in src/chaincodes/your-token/chaincode.js. As you can this class extends from the ChaincodeBase class which is part of the node-chaincode-utils library and a lot of the boilerplate code has been abstracted away in there. It’s possible to start writing the contract functions directly without messing with the
In the kuma-token example we developed two different kind of wallets, a user wallet and a chaincode wallet. A user wallet is linked to the certificate of a user that is used to interact with the blockchain and a chaincode wallet can only be controlled by another chaincode that has business logic on how and when funds can be moved from the wallet. To implement the wallets we are using helper functions provided by the node-chaincode-utils library. For example:
txHelper.getCreatorPublicKey()will return a hash of the public key from the creator of the current transaction.
txHelper.uuid(somePrefix)can be used to generate a UUID that can be used to store things in the database.
txHelper.invokedByChaincode(chaincodeName, chaincodeFunction)can be used to check if the current chaincode invocation was invoked by another chaincode.
More information about these helper functions and others can be found on github. Also a full implementation of this token example together with the implementation of a Multisig chaincode that manages a chaincode wallet within the token chaincode is available on github. When you are ready to start testing your chaincode you can start the blockchain in dev mode by running the following command.
npm run start
Which uses the
kuma-hf-chaincode-dev start-dev --watch command in the back that will boot up a simple blockchain network with one orderer and one peer. It’s using the configuration generated by the network-setup tool as configured in the package.json. It’s also possible to use the chaincode-dev-setup in standalone mode. When the network is booted it will install and instantiate all the chaincodes and everytime you make changes to the chaincode it will automatically install and instantiate the new version of the changed chaincode.
Interacting with the blockchain
The client-utils tool is a wrapper around the Node.js client SDK provided by HF itself. It provides a
invoke function that can be used to interact with the blockchain. There is also a baseService provided that can be used to create a service instance per chaincode. When creating this service for a chaincode you just need to provide it with the different functions the chaincode exposes and with the channel, peer and orderer information. We created two services in the kuma-token example, one for the token and one for the multisig chaincode. When the service is created for a specific chaincode you can reuse this service throughout the whole application by calling the chaincode function with the right arguments on this service and you don’t need to worry about the peers, orderers, etc.. The services abstract everything away so that you don’t need to think about blockchain things in the rest of your application.
Right now we only have end-to-end tests written for this application. The problem with these tests is that they modify things on the blockchain, so you can only execute them on your development network. That’s why we are looking into creating unit tests for our chaincode. But we still need to create some good mocking tools as are available for Go chaincode.
Deploying to AWS
Once all chaincode is finished, it is possible to boot up the network on Amazon AWS using our network-setup tool. But for this you first need to configure a Virtual Private Cloud (VPC) on Amazon AWS together with it’s subnet, a route table, an internet gateway and a security group. A detailed tutorial for this can be found here.
When all of this is configured we need to link the aws-cli to the right Amazon account and configure the aws.json file. In this file it’s possible to configure all of the AWS instances with the services you want to run on them. In the kuma-token example we added a very basic configuration linked to one of our AWS accounts. This will boot two Amazon EC2 instances, one with an orderer and a peer and one with just a peer. More information about the configuration options can be found here. If everything is configured correctly it is possible to start the network by running the following command:
kuma-hf-network network-up configuration/crypto_config.yaml configuration/aws.json --genPath ./generated
Within a few minutes, depending on your configuration, the network will be booted and the chaincode will be installed and instantiated. Now it’s possible for the client application to connect to the services on Amazon AWS. For this to work the client needs to be reconfigured to using the domain names of the services instead of localhost. The client will be able to communicate with the services because the network-setup script updated the /etc/hosts file linking the domain names to the right ip addresses on Amazon AWS. So if another machine running the client needs to communicate to the Amazon AWS services make sure to run the set_hosts_public.sh script first which was generated from the
network-up command on that machine. In the kuma-token example we have both configurations configured here.
Whenever an update is needed for a certain chaincode it is possible to update it on the network by bumping the version inside the package.json of the chaincode and then running the following command:
kuma-hf-network update-chaincodes --genPath ./generated
This script will only upgrade and instantiate the chaincodes with a version higher then the ones that is already installed on the blockchain.
All of these tools are still work in progress we have been using them in our latest HF projects. We will keep improving them while learning. For example during our latest project we thought it would be better to use Typescript to make our contracts more strict and readable. So one of the next things we would like to do is update our chaincode-node-utils tool to Typescript and try this out in the kuma-token example. As said earlier we are also still looking into a good way to mock things so that we can write unit-tests for chaincode.
We are open for suggestions, feedback and contributions to improve these tools. Because the only way to make this community better is by sharing.