Implementation of a blockchain application with Hyperledger Fabric and Composer

No Comments

There is currently no way around the topic of blockchain. But apart from cryptocurrencies, there are more interesting applications which lead to many exciting software ecosystems. This also applies to the Hyperledger project, which provides a remarkably modular blockchain framework. Let’s see how easy it is to implement a blockchain application with Hyperledger Fabric and Composer.

About project Hyperledger

Hyperledger is the name of an umbrella project under which open source blockchain approaches and tools are developed collaboratively. It was launched in 2015 by the Linux Foundation and enjoys lively participation by software giants such as IBM, Intel and Fujitsu as well as a large community. Hyperledger’s GitHub repositories are currently more active than ever. Anyone can potentially participate in the development.

In Hyperledger not only a single blockchain framework (or platform) is developed. Rather, the focus is on pursuing several approaches in parallel, creating synergies, reusable components and flexibility. From the perspective of Hyperledger concepts, a blockchain network is not comparable with representatives of the crypto currencies such as Bitcoin or Ethereum. Instead, nodes of the Hyperledger networks are distributed across the participating organizations, which makes it particularly interesting for private, permissioned or consortium blockchain networks. At first we can forget about Proof of Work, Proof of Stake and other consensus mechanisms from the world of public blockchain. The organizations involved validate each other’s transactions and benefit as a consortium from the applications business value and the trust involved. This also largely resolves scalability problems (which we know from the Bitcoin network) and makes a high transaction throughput possible.

A Hyperledger blockchain application is usually structured into organisations and its participants

The different blockchain approaches of project Hyperledger are Fabric, Burrow, Iroha, Indy and Sawtooth. Private, permissioned and consortium blockchains can be developed with all of them, but each implementation follows a different approach.
We will take a closer look at Fabric in this article as it has the most active community and is the most flexible of the variants. Fabric is universally usable due to its strong modularization. “You could think of Hyperledger Fabric as kind of like an Apache Web Server,” said Brian Behlendorf, Executive Director of Hyperledger at the Linux Foundation. The other approaches are intended more for the implementation of special cases in limited contexts.

Hyperledger Fabric – Flexible blockchain application platform

With Fabric as a platform, completely individual distributed ledger solutions can be developed. Fabric contains concepts that can be implemented as freely as possible.
The basis for a blockchain network is the modeling of the desired organizational structure. Each participant has a fixed identity and can identify itself through issued certificates. In addition to authentication, authorization is also covered. Using this role-based system, flexible aspects of privacy and confidentiality in permissioned blockchains are given.
For the administration of certificates and participants, the Fabric Certificate Authority (before version 1.0 Membership Service Provider) can be used.

The definition of assets (items that are to be managed on the blockchain) is entirely up to the blockchain application. These assets, e.g. engine blocks from the automotive industry, are defined by a model of key-value pairs in JSON and/or binary format.

The concept of the chain code is intended to implement business logic based on the assets and their owners. This can be used to implement rules in languages such as Go, Java or Node.js that define read rights or modifications to assets. Executing a chain code function can read and return assets and/or create and modify assets and store them in the local ledger database. After the local persistence of the changes on a node, the changes are proposed to the network (“endorsement”) and inserted into the blockchain after acceptance by the other organizations. Chain code can be compared to Smart Contracts in the context of Ethereum or other public blockchain platforms.

Transaction flow in a Hyperledger Fabric based blockchain application

Channels are used to implement areas of privacy. In the simplest scenario, the entire chain code is deployed on a single channel to which all participants join. However, in order to create encapsulated areas and allow only selected participants to communicate in them, channels with restricted participant groups can be configured. Different chain codes can be deployed per channel, so that functional isolation can be achieved. In addition, communication in the channel can be partially or completely encrypted with AES.

As a result, one distributed ledger is maintained in each channel, which can be imagined as a cash book of linked transactions. Each participant keeps a copy of the ledger for each channel they are a member of. This creates a blockchain data structure for each existing channel in the network.
As usual with a blockchain, the transactions are stored in blocks that become an encrypted chain in a single concatenated list.
However, to provide client applications with individual views of ledger data, even complex read requests against the network can be executed. This is possible due to usage of a document-oriented database like CouchDB. This provides flexible data access for clients connecting to Fabric networks.

Add simpler concepts with Composer

Hyperledger Composer is one of the tools in the Hyperledger ecosystem. You can think of it as a framework for Fabric. It is practical, if not obligatory, if you want to develop, build and administer Fabric networks. It introduces further concepts based on Fabric to provide nicely abstracted concepts.

In addition to assets, the scheme of network participants, transactions and events can be defined in the Composer Modeling Language. Flows for each transaction type are implemented on a simple API through JavaScript code. Access control files can be used to limit the access rights for participants to certain resources. Frequently used queries on the data in the ledger can be defined in the Composer Query Language, a SQL-like language.

All required files must then be packaged to a BND (Business Network Definition) in a .bna file. This archive can then be installed on an existing Fabric network. The source code for BNDs can of course be developed and tested locally in our preferred editor and thus be versioned via Git.
For prototyping and demo purposes there is the Composer Playground. This provides a modern, clear and intuitively usable web interface that accesses the local configurations of the Composer CLI. With the Playground you can easily create, install, test, edit, import and export BNDs.

Composer business network definitions contain queries, access rules, transactions and models for the blockchain application

In the Composer Playground, you can install, modify and test a new business network in a user-friendly manner and without much previous knowledge from sample blockchain applications (e.g. vehicle life cycle, automobile auction or tracking of farm animals). The same can be done locally after setting up the tools, so that we can leave the hosted playground after shortly playing around with it.
This playground is ideal for validating ideas using prototypes and getting a feel for the underlying Composer & Fabric model.

Use case: Supply chain tracking of engine blocks

To implement a private blockchain network with Hyperledger Fabric and Composer, the tracking of engine blocks from the automotive industry is presented as an example. In this case, there are manufacturers and dealers as network participants. Engines and the vehicles in which they were installed are shown as assets. The companies of the manufacturers and dealers are introduced and identified as organizations in the network.

The Fabric chain code should provide the following functionality:

  1. Production of an engine block with unique serial number
  2. Transmission of an engine block to a dealer after production
  3. Tracking a vehicle with its serial number
  4. Installation of an engine block into a registered vehicle

The next step is about installing the required tools and setting up the project.

Development environment setup and project creation

First, all requirements for Fabric, listed in the documentations, need to be installed.
Then we install the requirements for Composer and Composer and its related tools itself.

Then it is best to familiarize ourselves quickly with the new environment. If we followed the instructions of the last link exactly, fabric-tools is now in our home directory. With the scripts described we can start a simple Fabric network in Docker-Compose, get peer admin access and stop and delete it again. First, we download the Docker images of version 1.1 and start the network:

export FABRIC_VERSION=hlfv11 && ./downloadFabric.sh && ./startFabric.sh

While the network is running, the composer-playground web-UI can be started via composer-playground. It uses all managed configurations of the composer-cli and accesses the running Fabric network. From now on we see Fabric more as a configurable platform/infrastructure whose state we change with suitable tools. We don’t develop chain code, permissions or any models directly with Fabric concepts as Composer provides more advantages.

Implementation of functionalities

Now we create our BND project in a directory of our choice. For Yeoman (code generator for setting up projects using templates, like Maven Archtypes) there is a template (hyperledger-composer:businessnetwork. However, I have already prepared a repository where we can now also use JavaScript ES6 and some nice tooling. The repository has the start branch “initial” with which we should start with. The “master” branch has the final and working version. We clone the repository’s initial branch first.

git clone -b initial git@github.com:jverhoelen/fabric-composer-engine-supplychain.git

Now we open the folder in the editor of our choice. Visual Studio Code is very suitable for Composer because it has an installable extension for syntax highlighting. After a short look you will notice that it is an NPM project, so we start with npm install to install all dependencies. With npm test we can run the unit tests, with npm run lint we can test the code style, and with npm run createArchive we can create the.bna file, our finished business network definition in a packaged format. Let’s try it right away to see if everything works.

Then we familiarize ourselves with the project structure. The lib/ folder contains the JS files that implement the transaction processor functions. Of course we want to test this business logic and store our unit tests in the test/ folder. Model definitions (participants, assets, transactions etc.) are in models/.

We want to model the desired blockchain network first. To do this, we delete the content of the model file and give it a new namespace in the first line:

namespace org.acme.enginesupplychain

We model the participants manufacturers and dealers as such and use the inheritance of the Composer modeling language. We also want each participant to have an optional address in addition to their name. We put these attributes into a concept:

participant Member identified by memberId {
    o String memberId
    o String name
    o Address address optional
}
 
participant Manufacturer extends Member {
}
 
participant Merchant extends Member {
}
 
concept Address {
    o String country
    o String city
    o String street
    o String streetNo
}

Then we introduce the assets of our network: the engine blocks and cars in which the engines are installed later. Here we learn that assets and participants can reference each other. A reference always points to an existing resource of any type. Properties that we start with a small “o” always live in the resource itself.

asset Engine identified by engineId {
    o String engineId
    o EngineProperties data
 
    --> Manufacturer manufacturer
    --> Car currentCar optional
    --> Merchant merchant optional
}
 
asset Car identified by carId {
    o String carId
    o String legalDocumentId
}
 
concept EngineProperties {
    o String brand
    o String model
    o Double horsePower
    o Double cubicCapacity
    o Integer cylindersAmount
}

After modelling the system, we define all available operations that can be executed on top of the existing assets and participants These are the transaction resources. We test and implement the underlying transaction logic for each of the following transaction models afterwards.

transaction EngineMerchantTransfer {
    --> Engine engine
    --> Merchant merchant
}
 
transaction EngineCarInstallation {
    --> Engine engine
    --> Car car
}
 
transaction EngineCreation {
    --> Manufacturer manufacturer
    o EngineProperties data
}
 
transaction CarCreation {
    o String legalIdDocument
}

Now that we defined what can happen, we can start implementing how it’s affecting the ledger state when the transaction is submitted. First we dedicate ourselves to the creation of an engine asset. An engine should get a randomly generated ID in UUID format and should always belong to a manufacturer right from the start.
So we empty the logic.js file and start from scratch. We define the constant “modelsNamespace” and the function “uuid” because we will need them more often.
This is followed by the “createEngineAsset” function. The documentation block above the function is important so that Composer recognizes which transaction type is implemented when packaging the code.

/* global getAssetRegistry getFactory */
 
const modelsNamespace = 'org.acme.enginesupplychain'
function uuid() {
    const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1)
    return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`
}
 
/**
* Creation of a Engine asset triggered by physical production.
* @param {org.acme.enginesupplychain.EngineCreation} tx - the transaction to create an engine
* @transaction
*/
async function createEngineAsset(tx) { // eslint-disable-line no-unused-vars
    const engineRegistry = await getAssetRegistry(modelsNamespace + '.Engine')
    const engine = getFactory().newResource(modelsNamespace, 'Engine', uuid())
    const engineData = getFactory().newConcept(modelsNamespace, 'EngineProperties')
 
    engine.data = Object.assign(engineData, tx.data)
    engine.manufacturer = tx.manufacturer
 
    await engineRegistry.add(engine)
}

In this manner we also implement the other transaction types “EngineMerchantTransfer”, “EngineCarInstallation” and “CarCreation”.

/**
* An engine is transfered to a merchant.
* @param {org.acme.enginesupplychain.EngineMerchantTransfer} tx - the engine transfer transaction
* @transaction
*/
async function transferEngineToMerchant(tx) { // eslint-disable-line no-unused-vars
    const engineRegistry = await getAssetRegistry(modelsNamespace + '.Engine')
    tx.engine.merchant = tx.merchant
 
    await engineRegistry.update(tx.engine)
}
 
/**
* An engine is installed in a car.
* @param {org.acme.enginesupplychain.EngineCarInstallation} tx - the engine into car installation transaction
* @transaction
*/
async function installEngineToCar(tx) { // eslint-disable-line no-unused-vars
    const engineRegistry = await getAssetRegistry(modelsNamespace + '.Engine')
    if (tx.car) {
        tx.engine.currentCar = tx.car
        await engineRegistry.update(tx.engine)
    } else {
        return Promise.reject('No target car was set on the transaction!')
    }
}
 
/**
* A car is created.
* @param {org.acme.enginesupplychain.CarCreation} tx - transaction to create a new car
* @transaction
*/
async function createCar(tx) { // eslint-disable-line no-unused-vars
    const carRegistry = await getAssetRegistry(modelsNamespace + '.Car')
    const factory = getFactory()
    const carId = uuid()
    const car = factory.newResource(modelsNamespace, 'Car', carId)
    car.legalDocumentId = tx.legalIdDocument
 
    await carRegistry.add(car)
}

Unit testing of the functions themselves is relatively simple, we don’t need to learn more if we have experience with it. Only the boostrapping of the objects needed for this is still somewhat overloaded with boilerplate code. The tests first start an in-memory Fabric network, install the business network on it and then authenticate themselves against it as default admin. For this Composer provides the libraries “composer-admin”, “composer-client”, “composer-common” and “composer-connector-embedded”.
After the test setup we can now write test cases against the embedded network. The setup code has not been included in the listing due to its length, but can be viewed and tested on the master branch in test/EngineSupplychainSpec.js.

The unit test cases for testing a transaction type often have a similar schema. They recreate a transaction with its properties and relationships, execute it against the network and then check the data status of the assets and participants involved. Let’s take a look at the existing test case for “createEngineAsset”.

describe(‘EngineSupplychainSpec’, () => {
 
   // setup is done in the before and beforeEach hook
   // results are the bnc (BusinessNetworkConnection), target namespace
   // as well as test assets, participants and required registries
   describe('createEngineAsset', () => {
       it('should create an Engine by submitting a valid EngineCreation transaction', async () => {
           const factory = bnc.getBusinessNetwork().getFactory()
 
           const engineCreationTrans = factory.newTransaction(namespace, 'EngineCreation')
           engineCreationTrans.data = factory.newConcept(namespace, 'EngineProperties')
           engineCreationTrans.data.brand = 'Audi'
           engineCreationTrans.data.model = 'Fancy engine model'
           engineCreationTrans.data.horsePower = 400
           engineCreationTrans.data.cubicCapacity = 4000
           engineCreationTrans.data.cylindersAmount = 10
 
           const manufacturerRegistry = await bnc.getParticipantRegistry(namespace + '.Manufacturer')
           await manufacturerRegistry.addAll([])
           engineCreationTrans.manufacturer = factory.newRelationship(namespace, 'Manufacturer', testManufacturer.$identifier)
 
           await bnc.submitTransaction(engineCreationTrans)
 
           const allEngines = await engineRegistry.getAll()
           allEngines.length.should.equal(2)
       })
   })
})

The approach to implementing Business Network Definitions in Hyperledger Composer should have become clear with these insights. In addition, a BND can define a few more things for us. In permissions.acl you can use Access Control Language to define access restrictions for participants to resources given a simple condition. The event and query features are also very useful and interesting for many applications.

Finally, let’s take a look at the solution on the master branch. All of these requirements have been implemented and tested in it. We now generate the finished “.bna” file with npm run createArchive, which is then in the dist/ folder. We can now import it into the Composer Playground, which we started in the console, to try it out on our local Fabric network. The way through the web UI should be self-explanatory, but it is also officially documented.

Summary and outlook

We have already got to know a significant part of the Hyperledger project. Specifically, we now know that Fabric functions as a blockchain platform with rudimentary concepts. Composer adds many important concepts and makes it very convenient for developers to implement and manage blockchain networks.
Through the implemented blockchain application case about the production and tracking of engine blocks, we got to know a simple but powerful use case for private/consortium blockchains.

The resulting blockchain network was initially only executed locally. We have not expanded the configuration of the peer organizations and the ordering service. But we could easily add more organizations and distribute the peer nodes via multiple hosts. For a blockchain network to be spanned by a real organization consortium, we still have a few problems to solve:
How do we manage the organizations and peer nodes? How can organizations automatically add new peer nodes to the network? How do we get a fair and homogeneous network that can withstand failures? How can clients communicate with the network?

The still young platform already offers a lot of functions and comfort. However, there are still a number of tasks to be completed. From the developer’s point of view, the unit test code still looks quite bloated. Soon libraries will follow, with which the usual test patterns can be implemented more easily. We are eager to see how projects such as Hyperledger will continue to drive the adaptation of distributed ledger technologies in business.

Troubleshooting

Make sure all tools are compatible with Fabric 1.1. This means that all docker images of this version must be downloaded. The Composer and Composer Playground should be installed which the newest version. Currently that is v0.19.1. All links to the Fabric and Composer documentation in the article are deliberately fixed to Fabric 1.1 and the latest Composer version.

Jonas Verhoelen

Jonas is passionate about creating more business value through good software at speed. He prefers to develop in Type- or JavaScript as well as various JVM languages and loves to work full-stack. In addition, he is available to the customer with expert knowledge, tools and methods for consulting around Distributed Ledger Technology and IT Security. Teamwork, self-organisation and an agile mindset are the basis of his daily work.

Comment

Your email address will not be published. Required fields are marked *