Controllers

We define controllers as the business logic applied to models. Every possible action over a model, should be configured thought controllers.

The package  @worldsibu/convector-corecontains the necessary code to create controllers. Here's an example of one:

The first thing to notice is we're extending  ConvectorController. This is a really thin class, only containing the basic information of the sender and the transaction.

The  @Controller decorator let Convector know this class is a controller, and all its methods should be available in the chaincode using the namespace provided.

The @Invokable decorator let Convector know all the available methods in this controller. You can probably have more methods in this class, but not all of them are exposed in the chaincode API

Finally, the  @Param decorator parses the arguments coming in the transactions to the appropriate schema you define. You can also use Models as schemas and they will be instantiated and validated for you. By default it will only parse the value to the model, but it won't reject incomplete models. The second parameter of it are options for the yup validator, pass `{strict: true}` to validate the model.

Native API

To access the native Fabric API you can use (inside of a chaincode controller) the this.tx it will allow you to access the stub and some other details of the transaction.

You will need to change your Controller's signature with this:

this.tx.stub

this.tx.identity

Anatomy of a Controller

Controllers are meant for you to validate permissions and update models. It is very usual (but not strict) that you'll have 3 main sections in your controller bodies:

  • Validation - Check for permissions or other conditionals that might end up in throwing an error and cancelling the transaction
  • Modification
  • Store

Multi-controller Smart Contract

Controllers are logic units. A controller is not a smart contract but a smart contract is made of Controllers.

Usually the relationship is 1 on 1. But you can have a smart contract made of multiple controllers.

There are two ways of using multiple controllers:

  • Multiple controllers in the same chaincode package. When you create a new project with Convector CLI a default controller is created. You can create a new one in the same folder and then bundle both controllers.
  • Multiple controllers in separate chaincode packages. You can create multiple chaincode packages with the CLI and each one of them with just one controller, but bundled into the same chaincode.

Bundling Controllers

The way to bundle multiple controllers in the same smart contract you have to leverage the packaging process. It's a task inside of your root package.json called by default cc:package. This task calls this code:  f() { npm run lerna:build; chaincode-manager --config ./org1.chaincode.config.json --output ./chaincode-drug package; }; f

The configuration file usually resides in your root (called *.chaincode.config.json) and looks like this (focus on the controllers array). 

This example references this repo: https://github.com/worldsibu/convector-example-drug-supply-chain

In this example, there are three separate packages, each one with a separate controller. If you would like to have them in the same package, you can just copy the three controller files in the same package and reference them like this:

Make sure your models and controllers are exported in the index.ts file in the root. Take a look to the already exported packages and replicate that export.

Adding dependencies

Packages can depend on other local packages. Lerna helps with it.

You can add one package as a dependency of another through the package.json file.

Do's and Don'ts

Do declare new public or private methods in the class to have common logic. Don't call another  @Invokable method directly from your controller, this will mess up the arguments since Convector is expecting some additional parameters from the blockchain and not the ones you declare.

Do store information in blockchain using models. Don't use any instance property inside a controller. The instance properties here are reset every time the controller is instantiated again.

Do use  this.sender as a way to identify the tx sender and validate permissions. Don't trust any parameter in your function as an identity validation, since anyone could have invoked you and moked that param.

Don't use non-deterministic data sources such as external API calls, random numbers, and others as these will be different on each blockchain peer. Also, avoid using dates generated (in the smart contract) fields as the date in one peer may be different from the others.

More advanced

After the chaincode is installed and instantiated in the peers, the method  initControllers is invoked. This method is responsible of iterating over all the controllers specified for the chaincode, import them, instantiate all of them and register all of its @Invokable methods in the chaincode to be accessible from the outside. 

In order to avoid method collisions between multiple controllers, the functions are registered under a namespace, the controller name provided in the  @Controller decorator. All the methods get registered in the Blockchain using the name {controller}_{method}.

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us