Convector: Writing an Open Source Development Framework

Convector (a.k.a Convector Smart Contracts) is a JavaScript development framework built for enterprise blockchain frameworks. It enhances the development experience while helping developers create more robust and secure smart contract systems. It spans through the chaincode and backend all the way to the front end, allowing developers to reuse the same code base in the form of libraries. It’s based on a model/controller pattern, supports Hyperledger Fabric and runs natively along Fabric’s well-crafted patterns.

This blog post walks through the history of the project and highlights the challenges and solutions developed along the way.

Everything began when we started to work on Tellus, a code-less transaction designer written to run on a Hyperledger Fabric blockchain. Back then we had a bunch of Golang smart contracts.

Our first impression of the developer experience (DX) was not so good. There are two methods: init and invoke with no other way to add new methods other than by putting an if condition on the invoke method and using one of the parameters to indicate the method invoked. All parameters are positionally passed strings requiring complex parameters to be parsed manually and there was no way to test it locally.

At the beginning of the project, Fabric 1.1 landed adding support for Javascript chaincodes. We decided to try it out hoping for improved developer experience. Unfortunately, it follows the same pattern found in the Golang chaincodes, and you still have to do some dirty work in your everyday logic. We kept looking for a better solution and found a post about a library from TheLedger on making Fabric chaincodes in Typescript that really improves over what you have with raw Javascript.

During the migration of our smart contracts from Golang to Javascript a pattern emerged. Most of the time functions do things in the following order:

  1. Parse the arguments.
  2. Make some assertions.
  3. Perform the changes.
  4. Save the changes.

This led to a fundamental question about theproject plan: should smart contracts get migrated quickly or should more time be spent figuring out a pattern and making them flexible enough for multiple business cases. It all started in the ./src/utils/ of the project.

/** @module @worldsibu/convector-examples-token */

import * as yup from ‘yup’;
import {
 ConvectorModel,
 ReadOnly,
 Required,
 Validate
} from ‘@worldsibu/convector-core-model’;

export class Token extends ConvectorModel {
 @ReadOnly()
 public readonly type = ‘io.worldsibu.examples.token’;

 @ReadOnly()
 @Required()
 @Validate(yup.object())
 public balances: { [key: string]: number };

 @ReadOnly()
 @Required()
 @Validate(yup.number().moreThan(0))
 public totalSupply: number;

 @ReadOnly()
 @Required()
 @Validate(yup.string())
 public name: string;

 @ReadOnly()
 @Required()
 @Validate(yup.string())
 public symbol: string;
}

Figure 1 — Convector Model
Fabric does not have a restriction on the data shape stored in the blockchain. You basically have a key-value map where both are strings, which means you can serialize and store any complex object. We took apart the models to reuse them in code. We just passed all of the necessary parameters in.

@Invokable()
 public async transfer(
   @Param(yup.string())
   tokenId: string,
   @Param(yup.string())
   to: string,
   @Param(yup.number().moreThan(0))
   amount: number
 ) {
   const token = await Token.getOne(tokenId);

   if (token.balances[this.sender] < amount) {
     throw new Error(‘The sender does not have enough funds’);
   }

   token.balances[to] = token.balances[to] || 0;

   token.balances[to] += amount;
   token.balances[this.sender] -= amount;

   await token.save();
 }

Figure 2 — Convector Controller

With Fabric, you get a typed list of parameters for functions. We didn’t want to be parsing models all of the time in all of the functions, so we added some decorators to validate if all parameter type invariants were met successfully. Those parameters might be a primitive, complex or even a model.

Functions now started to look more like a controller. They handled the business logic while the model described the data.

Now came the time to integrate all of the chaincodes into our Nodejs REST API. In the process, we realized we were creating a wrapper library on the server to call my chaincodes with the fabric-client lib. This is a very common situation so we looked for a better way to automate this.

I wanted to use the same controller and model files on the server as well as on chaincode. Doing so meant decoupling the relationship between the models and the storage layer (Fabric) as well as the controllers and the execution action.

This is where we realize Hyperledger Fabric was just one of the multiple blockchains Convector can support.

Adapter and Storage come into play.

The adapter is the underlying layer for the controller. Controllers define the methods, params, and business logic while adapters deal with how to route the invocation to the right place. For example, in our API it uses an adapter to invoke the fabric-client library and send a transaction.

The storage provides the functionality to interact with the models. Whether you want to save, delete or query something, you interact with the model itself, and, behind the scenes, it interacts with the specified service. On chaincode this is the Fabric STUB object. In the Nodejs API, it might be sending a query transaction or read from CouchDB.

Pro Tip: Convector can be used with something other than a blockchain. Like, configure an adapter or a model to call an API or another database.

The weekend turned into a month of creating tools and perfecting the pattern. Here are some of the tools created in the journey that you can leverage today:

# Install the CLInpm i -g @worldsibu/convector-cli
# Create a new chaincodes projectconv new mychain -c token
cd mychainnpm i
# Install a dev environmentnpm run env:restart # Install the chaincodenpm run cc:start — token 1

Figure 3 — Convector CLI

Also, Convector already comes with a Fabric adapter, a Fabric storage, a CouchDB storage, and a mock adapter (for unit tests) that you can use to seamlessly create code for your chaincode as well for your NodeJS backend while creating tests that can be included in your CI/CD pipelines. This is critical in any real-life development.

Extra adapter and storage layers can be easily created and we’re excited to see what the community builds around these tools. At the same time we were building this, we continued working on our internal product’s migration, which helped to test the framework in real life scenarios before launching it.

I’m glad we didn’t take the easy path on this migration. We’re pretty happy with the result, and the journey of publishing an open source tool has been amazing. It’s also rewarding to see  hundreds of people using it every day.

Hyperledger Fabric is an excellent blockchain framework. The infrastructure it provides covers most of the use cases in a secure and reliable way. That’s why we think it deserves a robust interface for smart contracts too, and we want to contribute back to the community with the internal tools we created while working with it.

Because we believe the project can be useful for anyone in the blockchain ecosystem, Convector has joined the Hyperledger Labs initiative. We are really committed to building a community around Convector, which has already surpassed the 27,000 downloads mark, and welcome the input of the Hyperledger community. If you are looking to get involved in an open source project, refer to GitHub

The coordinates for the project are:

About the author
Diego Barahona is the CTO and Architect of WorldSibu, a startup dedicated to creating blockchain tools and platforms for non-blockchain experts and make the technology more accessible to solve business challenges.