Overview

Skill Level: Intermediate

In this recipe, blockchain developers can quickly learn and setup Composer Playground for deploying business networks via implementing models, transaction logic, access control, and query definitions in Hyperledger Composer.

Ingredients

To follow and complete this recipe, you need to have good knowledge of blockchain, Hyperledger transactions, Object-Oriented (OO) programming, JavaScript, Node.JS as well as basic knowledge of Linux and Express.JS.

Step-by-step

  1. Hyperledger Composer Overview

    Hyperledger Composer is a set of collaboration tools for business owners and developers that make it easy to write chaincode (also known as smart contracts) for Hyperledger Fabric and Decentralized Applications (DApps). With Composer, you can quickly build Proof-of-Concept and deploy chaincode to the blockchain in a short amount of time. Hyperledger Composer consists of the following toolsets:

    • A modeling language called CTO: A domain modeling language that defines a¬†business model, concept, and function for a business network definition
    • Playground: Rapid configuration, deployment, and testing of a business network
    • Command-line interface (CLI) tools: The client command-line tool is used to¬†integrate business network with Hyperledger Fabric

    In this recipe, we will explore the Hyperledger Composer development components, including implementing models, transaction logic, access control, and query definitions. Here is a good article for those who are not familiar with Hyperledger family and projects. Also, to better understand and follow this recipe, reading Hyperledger Composer business network and development requirements is highly recommended.
    The objective of this recipe is to review essential features of Hyperledger Composer for developing blockchain applications. Following this recipe, blockchain developers can quickly learn and setup Composer Playground for configuring and deploying business networks. We also explain how Hyperledger Composer API works.

    Recipe Overview

    In this recipe, we will set up a development environment, and cover the use of the Hyperledger Playground for testing.  Specifically, this recipe has two main parts: i- Setup Business Network on Playground and ii- Generate a Business Network Project in 14 steps.

  2. Setup Business Network on Playground

    We will use Yeoman to generate a businessnetwork project template. Here, we need to modify the provided template files.
    In Playground, select an empty-business-network template to start our model, logic, and
    ACL implementation:
    set up a development environment

     

    We first rename the mode file under the models folder to org.packt.farmatrace.cto. Then we add a new script file by clicking on add a file, selecting Script File (.js), and adding a new Query File (.qry) by clicking add a file. You should then select Query File (.qry):
    set up Playground development environment

     

    Remove the default generated content in these files. In earlier FarmaTrace use case analyses, we identified that we need to create the following businessnetwork components:

    • participants: Manufacturer, distribution, pharmacy, hospital, physician, customer
    • asset: drugReceipt, Evident, Drug
    • transaction: Init(), makeDrug(), sendToDistribution(), three distribute() functions (to pharmacy, physician and hospital), three buy function and finally close
    • query: Query participant by ID, including manufacturer, distribution, pharmacy, hospital, physician, customer, and so on
    • permissions.acl: Defines participant permissions

     

  3. Generate Business Network Project

    Execute the following steps to generate a businessnetwork project:

    1. Define participant, asset, concept, enum, transaction and event for our model file: org.packt.farmatrace.cto.

    2. Define enum for ParticipantType: MANUAFACTURER, DISTRIBUTION, PHARMACY, HOSPITAL, PHYSICIAN, and CUSTOMER.

    3. The model file defines the FarmaTrace flow status for the receipt, as follows:

    enum ReceiptStatus {START

    CREATE_DRUG

    PICK_UP_DRUG

    DEVLIER_PHARMACY

    DEVLIER_HOSPITAL

    DEVLIER_PHYSICIAN

    CUSTOMER_RECEIVED

    CLOSED

    }

    4. The model file defines six participants in the network, as follows:
    //address

    concept Address {

    String street

    String city

    String country default = “US”
    }

    concept Entity {

    String name

    String desc

    String phone

    Address address

    }

    concept Person {

    String firstName
    String lastName
    String phone
    Address address

    }

    participant Manufacturer identified by manufacturerId { String manufacturerId
    Entity entityInfo

    }

    ..

    5. Define the Evident asset. To do this, trace some important information, such as when a transaction happens, which participants are involved in this transaction, and what ReceiptStatus is, when we make this evident:

     

    asset Evident identified by evidentId {
    String evidentId
    DateTime lastUpdate
    ParticipantType from
    ParticipantType to
    String fromId
    String toId
    ReceiptStatus status
    }

    6. Define the Drug asset. The Drug asset defines important information, including the date of manufacture, when a drug will expire, name, and description.

    7. Define the DrugReceipt asset. Here, the receipt will record all transactions and drug information. It maintains the current transaction status:

    asset DrugReceipt identified by receiptId { String receiptId
    ReceiptStatus currentStatus

    –> Evident[] evidents

    –> Drug drug

    String closeReason optional

    }

    8. Define the FarmaTrace transaction and event in the model file and implement them in the script file.

    9. Create a Drug asset with the default set to the default manufacture date and the expiration date as 1900-01-01 (invalidated date):

     

    async function initialApplication(application) { const factory = getFactory();
    const namespace = ‘org.packt.farmatrace’;
    const drugReceipt = factory.newResource(namespace, ‘DrugReceipt’,application.receiptId); drugReceipt.currentStatus = ‘START’; drugReceipt.evidents = []; //initial drug

    const drug = factory.newResource(namespace, ‘Drug’, application.drugId);
    drug.manu_date=”1900-01-01″;
    drug.expire_date=”1900-01-01″;
    drug.name=application.drug_name;
    drug.desc=application.drug_desc;
    drugReceipt.drug = drug;

    //save the application…

    //save the application

    ..

    // emit event
    …

    }
    10. Implement the makeDrug function‚ÄĒin this step, the makeDrug function implements the logic for tracing makeDrug by manufacturer. We first verify that the receipt status is set to START; if not, the smart contract will throw an exception:

     

    /**

    @param {org.packt.farmatrace.makeDrug} makeDrug – Manufacturer make a drug\

    @transaction

     

    */

    async function makeDrug(request) {

    const factory = getFactory();

    const namespace = ‘org.packt.farmatrace’; let drugReceipt = request.drugReceipt;

    if (drugReceipt.currentStatus != ‘START’) {

    throw new Error (‘This drug receipt should be in START’);

    }
    drugReceipt.currentStatus = ‘CREATE_DRUG’;

    ….
    }

    11. Query the blockchain to make sure that input from the fromId and toId parameters is valid by querying findManufacturerById, which is defined in queries.qry:

     

    let fromResults = await query(‘findManufacturerById’,{ “manufacturerId”: request.fromId

    });

    if(fromResults.length==0) {

    throw new Error (‘Can’t find manufacturer’);

    }
    12. Create evident and add it to the receipt, as follows:
    let evidentId = Math.random().toString(36).replace(‘0.’, ”); let evident = factory.newResource(namespace, ‘Evident’,evidentId);
    evident.lastUpdate = new Date();
    evident.from = ‘MANUAFACTURER’;
    evident.to = ‘MANUAFACTURER’;
    evident.fromId = request.fromId;
    evident.toId = request.toId;
    evident.status =’CREATE_DRUG’

    //save the application

    const evidentRegistry = await getAssetRegistry(namespace+ ‘.Evident’);
    await evidentRegistry.add(evident);
    drugReceipt.evidents.push(evident);

    13. Update the drug manufacture date and expiration date.

    let drug = drugReceipt.drug;
    var currentDate = new Date()
    drug.manu_date = currentDate.getFullYear();+ “-” + (currentDate.getMonth() + 1) + “-” + currentDate.getDate();

    drug.expire_date = (currentDate.getFullYear();+ 3) + “-” + (currentDate.getMonth() +

    1)+ “-” + currentDate.getDate();

    const drugAssetRegistry = await

    getAssetRegistry(drug.getFullyQualifiedType());

    await drugAssetRegistry.update(drug);
    14. Update the receipt and emit the event in the blockchain. Other transactions in FarmaTrace will be similar.

    How Business Network Project Works

    The Composer model’s language is an Object-Oriented language. We first analyze and capture all critical information and then apply OOD design to define our six participants in the system. We can then define these participants using the Composer model language. There are two types of participant: one is organization-based, the other is person-based. The relationship for these participants is as follows:
    How Business Network Project Works

     

    Distribution, Hospital, Manufacturer, and Pharmacy have entity information, which is an organization-based participant. Customer and Physician have person information, which is a person-based participant. Both Entity and Person contain an Address asset.
    We also define transaction methods in the model file, and implement transaction logic in the logic file. The transaction function can be considered to be smart contract execution. These transactions will link to the implementation of the transaction function through annotations in the comment of the function. @param connects this transaction to the makeDrug defined in the model. @transaction marks this function as a transaction function:

    /**

    Manufacturer make a drug

    @param {org.packt.farmatrace.makeDrug} makeDrug – Manufacturer make a drug

    @transaction

     

    */

    The script can directly invoke a query file search function, just like a regular JavaScript post method. This can pass JSON-formatted data as a parameter. In our example, we pass the manufacturerId parameter and call findManufacturerById. If no manufacturer record is found, the transaction will throw an exception and be aborted.
    There are three functions required to create or update a transaction and event record. The first one is getFactory(), which allows an asset or participant to be created as part of a transaction. The second is getAssetRegistry(namespace+ ‘.xyzAsset), getParticipantRegistry(namespace + ‘.xyzParticipant), or factory.newEvent(‘org.namespace’, ‘xyzEvent’). This creates xyzAsset,xyzParticipant, or xyzEvent defined in a specified namespace. Then the required properties on asset, participant, or event must be set. Finally, to emit an event, it should use xyzEventRegistry.add(). To add a new asset or participant, it should use xyzParticipantRegistry.add() or xyzAssetRegistry.add(). To update a new asset or participant, it should use xyzParticipantRegistry.update() or xyzAssetRegistry.update():
    const drugReceiptAssetRegistry = await getAssetRegistry(request.drugReceipt.getFullyQualifiedType());

    await drugReceiptAssetRegistry.update(drugReceipt); // emit event

    const makeDrugEvent = factory.newEvent(namespace, ‘makeDrugEvent’); makeDrugEvent.drugReceipt = drugReceipt; emit(makeDrugEvent);

    emit(makeDrugEvent);

  4. Additional Insights

    We have just looked at how to use a query function to find a manufacturer by ID. There is another way to get the same result. Composer provides getNativeAPI to call the Hyperledger Fabric API in a Composer transaction processor function. getNativeAPI allows users to call the Fabric shim API, which provides APIs for the chaincode to access its state variables, transaction context, and call other chaincodes:
    const nativeSupport = request.nativeSupport;

    const nativeFromIdKey =
    getNativeAPI().createCompositeKey(‘Participant:org.packt.farmatrace.Manufacturer’, [request.fromId]);

    const fromIterator = await

    getNativeAPI().getHistoryForKey(nativeFromIdKey);

    let fromResults = [];        let fromRes = {done : false};

    while (!fromRes.done) {

    fromRes = await fromIterator.next();

    if (fromRes && fromRes.value && fromRes.value.value) {

    let val = fromRes.value.value.toString(‘utf8’);

    if (val.length > 0) {

    fromResults.push(JSON.parse(val));

    }

    }

    if (fromRes && fromRes.done) {

    try {

    fromIterator.close();

    }

    catch (err) {

    }

    }

    }

    if(fromResults.length==0) {

    throw new Error (‘Cant find manufacturer’);

    }

    The getState and putState Hyperledger Fabric API functions will bypass the Hyperledger Composer access-control rules.

  5. How Hyperledger Composer API Works

    Playground uses a Composer API and calls the Composer proxy Connector to connect to a Composer Connector Server via SOCKETI/O. All Playground data in web mode is stored in the location storage. The Composer Playground API is an express App that directly connects to Composer Connector Server, which interacts with Composer HLF Runtime. When we load Playground, we will see some samples. These samples are taken from NPM libraries. The Playground UI communicates with the Composer Playground API via HTTP.
    The following diagram shows how these components interact with each other:

    How Hyperledger Composer API  Works

    By now, you have learned how to setup business network and create your first project using Hyperledger Composer and Hyperledger Playground. Here are two next steps: a- Deploying, Testing, and Exporting Business Network Archives Using the Composer Command-Line Interface and b- Interacting with Composer through the RESTful API.

    This article is written in collaboration with Brian Wu who is a senior Hyperledger instructor at Coding Bootcamps school in Washington DC.

Join The Discussion