Transitioning from Hyperledger Composer to native Go chaincode

Patrik Majkowski
KompiTech
Published in
5 min readJul 15, 2019

Composer

When starting with Hyperledger Fabric based applications in our company, we used Hyperledger Composer project (https://hyperledger.github.io/composer/latest/). It uses the term “business network” to define what you want your chaincode to do:

  • Assets and their attributes, including references to other assets and validations
  • Participants and what they can do (ACL)
  • Transactions and their attributes to mutate assets

These are defined using a platform independent language called CTO, which allows almost anyone (even non-developers) to do this. To define actual business logic that lives inside transactions, JavaScript is used. Combined with Composer API and the dynamic nature of JS, you can prototype the behavior of your network quite rapidly. For the ACL, you can use declarative form, or you can write JS code to handle more complicated cases. After you are done with this, everything gets compiled into a business network definition file (BNA).

This file is the boundary between declaration and implementation — you can deploy it locally on the back-end based on Docker, using third party services, or just testing it in Composer Playground (https://composer-playground.mybluemix.net/). A strong point of Composer is its REST server — it takes a BNA file as a parameter and generates HTTP methods for each asset, participant, and transaction without any effort on the developer’s side.

Go chaincode

In September 2018, after reading the news about Composer not being developed further (https://lists.hyperledger.org/g/composer/message/125), we needed to find a way how to continue the chaincode development process. All of our developers needed to learn the Go language and we switched to writing pure Go chaincode. We started using a helpful library called cckit (https://github.com/s7techlab/cckit) which greatly simplifies method routing and tests.

The Go language is inherently static, so it was clear from the beginning that we wouldn’t be able to match the flexibility of BNA + JS. Having no other choice at the time, we persevered and ported our Composer application into Go structs and custom chaincode. The biggest workload increase was with our equivalent REST server — that which was previously generated automatically for us, we now needed to write ourselves. Also, we needed to tell the REST server all the structures that are being exchanged with the chaincode — this was previously handled by its BNA file.

Making Go chaincode “dynamic”

By knowing the shortcomings of previous attempt and approach from Composer, I had quite a clear idea on how to do our next chaincode iteration. Here are some goals that were defined:

  • Everything should be JSON based, that’s what Fabric expects and already has tooling for.
  • We must leverage the existing JSON ecosystem tools currently available (JSON Schema, JSON Patch, JSON Pointer, …).
  • Do not hard code asset structs in Go. Instead of using CTO, define and validate assets using JSON Schema, and define references also on a schema level.
  • Have a generic system to define business logic for assets in Go.
  • Have a strong migration mechanism — often, you want to change an asset (make a new version of it) on a running instance without losing previous assets. If we can do business logic versioning as well, that would be awesome.
  • As a bonus — our REST server can be really “dumb”, as it’s simply passing JSONs between chaincode and the client without any knowledge of their inner structure.

So, new chaincode uses “registry” (a term borrowed from Composer) to store JSON Schema for each asset type that you want to manage. You can update existing registry items with new schema — this will create a new version. Since each asset instance knows which version it is, old assets will continue to work, they will just validate against different JSON Schema for older versions. Chaincode also includes a “migrate” method to change each asset instance’s version by having the client provide a JSON Patch and the target version.

Everything written in the previous paragraph can be done dynamically without any issues in Go. However, defining business logic is a different problem. Since Go is a static language and we do not currently have the resources to develop some domain specific language, we needed to stick to Go code as the most expressive way to define that logic. This leads to an unfortunate conclusion, that for business logic changes, the chaincode will need to be compiled.

Luckily, Fabric has the ability to “upgrade” existing chaincode, which allows us to deploy a new chaincode with the business logic changes that we aim to achieve. To also allow migrations for business logic, the chaincode just needs to define the version of the specific asset on which it is executed.

Code examples

As an example, let’s make a model of an author — book relationship. This is M:N relation (the book can have multiple different authors, and the author can contribute to multiple books). In Composer, the definition in the CTO language looks like this:

Assets in CTO

Analogical definition in for our chaincode would look like this:

Assets in Go

When comparing the two, it is clear that Composer CTO is much shorter. On the other hand, JSON Schema is quite common and standardized, which allows for reuse. Also, we are defining where those assets should be stored — dynamic chaincode allows some assets to be stored in a private data collection, instead of a blockchain state (Composer stores everything in state). Dynamic chaincode also uses UUID v4 to identify all assets — you do not need to define these fields in the schema.

JSON Schema by default allows the objects to have some extra keys without breaking the schema. This is something we definitely do not want, so it is required to set additionalProperties to false (chaincode checks that value and will refuse to accept the schema without this setting).

In Composer CTO, you can define default values directly in the schema (default keyword). In dynamic chaincode, we did not want to clutter the schema, so setting default values was moved to business logic code.

Both of these approaches will create functioning code which will properly manage references without any more code required. This is the ultimate goal, to reduce boilerplate code and focus on modelling the business you want to achieve.

Summary

By learning from Composer and features of the Go language (it is not that hard to work with dynamic structures in it) we were able to create this hybrid chaincode. It decouples asset declarations from implementation and also provides a strong migration mechanism during runtime.

--

--