CCKit: Routing and middleware for Hyperledger Fabric Golang chaincode

Viktor Nosov
Coinmonks
Published in
4 min readMay 20, 2018

--

There are a lot of examples and tutorials about Hyperledger Fabric chaincode development. This article describes the methods that simplify and accelerate the chaincode development through the usage of the CCKit library, containing implementation of basic chaincode building components — routing, middleware and chaincode invocation context, as well as provide chaincode testing utilities.

Chaincode components

Chaincode is a program, written in Go, Javascript or Java. Chaincode (smart contracts in the Fabric world) implements some business logic (for example, ERC20 token functionality) and interacts with the shared ledger. Chaincode must implement a prescribedChaincode interface.

The Init method is called when a chaincode receives an instantiate or upgrade transaction. Init method may perform any necessary initialization, including setting default application state. The Invoke method is called in response to receiving an invoke transaction to process transaction proposals.

The another interface in the chaincode “shim” APIs is the ChaincodeStubInterface which is used for access and modify the ledger, and to make invocations between chaincodes.

Chaincode examples

In addition to Hyperledger Fabric documentation, several chaincode examples are also available :

They are all quite verbose and contain a lot of function switching logic (some kind of “routing”), argument number validation, json marshalling / unmarshalling etc

Invoke hell

Introducing library for chaincode method routing

To simplify chaincode development, we tried to compose in single library CCKit common software development patterns, such as routing, middleware and invoke context:

Routing refers to determining how an application responds to a client request to a particular endpoint. Chaincode router uses rules about how to map chaincode invocation to particular handler, as well as what kind of middleware needs to be used during request, for example how to convert incoming argument from []byte to target type (string, struct etc)

Invoke context is abstraction over ChaincodeStubInterface, represents the context of the current chaincode invocation. It holds request, response and client (Identity) reference, converted parameters, as well as state and log reference . As Context is an interface, it is easy to extend it with custom methods.

Middleware functions are functions that have access to the invoke context, invoke result and the next middleware function in the chaincode’s invoke-response cycle. The next middleware function is commonly denoted by a variable named next.

Middleware functions can perform the following tasks:

  • Convert input args from byte slice to desired type
  • Check access control requirements
  • End the request-response cycle.
  • Call the next middleware function in the stack.

CCKit library includes implementation of these patterns and provides:

  • Centralized chaincode invocation handling
  • Middleware support
  • Chaincode method access control
  • Automatic json marshalling / unmarshalling, entry key generation
  • Testing tools (extended version of MockStub)

Simple asset chaincode — cars registration example

We will demonstrate the use of router and middleware by implementing a simple chaincode application that manages simple “assets” — cars. In this example only one authority can register cars and all can view information about registered cars

Getting started with CCKit

Before you begin, be sure to get CCkit from github.com:

git clone git@github.com:s7techlab/cckit.git

and get dependencies using dep command:

dep ensure -vendor-only

Implementing chaincode interface

As with every chaincode, it implements the Chaincode interface in particular, Init and Invoke functions

In our example this functions are very simple — in Init function we put in chaincode state identifier of user, who instantiate chaincode (“owner”). In Invoke we delegate handling of chaincode method invocation to router.

Chaincode asset

As shown in many other examples, assets can be represented as complex structures (Golang structs). The chaincode itself can store data as a string in a key/value pair setup. Thus, we will marshal struct to JSON string before putting into chaincode state and unmarshal after getting from state.

Attributes of a car:

  1. id (unique string, will be used as key)
  2. title (string)
  3. owner (string)

Also we create payload structure for car registration information. It contains only fields, allowed for pass to chaincode.

Defining chaincode methods

In chaincode creation func we can determine chaincode functions and theirs arguments. In this example chaincode have three functions:

  • carList, returns slice of Car structs
  • carList, takes car id and returns Carstruct
  • carRegister, takes CarPayload struct and returns car registration result. Access to this method allowed only for chaincode owner, access control is implemented usingowner.Only middleware

Chaincode methods implementation

Chaincode methods, handled by router takesContext as argument and returns interface{} and error .

func (router.Context) (interface{}, error)

Returned values will be automatically converted to Pb.response : interface{} will be marshalled to []byte, response status will be shim.OK or shim.Error depending on the error value.

Method arguments, defined in the router, will be automatically converted from byte slice to desired type (string or CarPayload struct)

Chaincode function also use Statemethods, which simplifies interaction with chaincode state.

All together

https://github.com/s7techlab/cckit/blob/master/examples/cars/cars.go

Chaincode main file

Finally, we need to create main package with main function, which will call the shim.Start function.

https://github.com/s7techlab/cckit/blob/master/examples/cars/bin/cars/main.go

Without access control

With router you can easy change access control logic, you just need to change or remove middleware call in router definition. For example chaincode without access control of carRegister function. We modified only two pieces of code — Init function and registration carRegister function in router, while carRegister function body remained unchanged.

https://github.com/s7techlab/cckit/blob/master/examples/cars/cars_noacces_control.go

and chaincode main file without access control

https://github.com/s7techlab/cckit/blob/master/examples/cars/bin/cars_noac/main.go

Testing the chaincode

For testing we use the extended version of the MockStub to which we added Creator mocking and some sugar to method invocation.

More about chaincode testing will be in the next article

--

--