In my tutorial Run a commercial paper smart contract with the IBM Blockchain VSCode extension, I showed you how to deploy and interact with a commercial paper smart contract in a scenario that tracks the lifecycle of a commercial paper. But what if you want to see the “paper” trail of all the activity that has taken place during its lifecycle — meaning an immutable history of the asset (who did what, when it took place, etc.)?

Figure 1. “Papernet” — overview of commercial paper query history tutorial and organizations involved

Transaction flow

This tutorial, the second in a three-part series, shows you how to use the IBM Blockchain Platform VSCode extension to add query transaction functions to the Fabric Samples Commercial Paper smart contract using the extension to upgrade the contract after adding query functionality. You’ll provide the code changes by adding a new query class (containing query functions), and with it the means to query key/extract information from the ledger. I’ll then show you the interaction with the smart contract from a client application in DigiBank. The goal is to get the history of transactions for the lifecycle of a commercial paper instance and display its history in a nicely formatted UI/HTML table in a client browser. This tutorial is aimed at developers, and it provides specific instructions (in particular, editing source files to add provided code blocks). Take time to see what’s going on — you don’t necessarily have to understand JavaScript in great detail for this.

Papernet: Asset history and lifecycle from a Blockchain query

To complete these tasks, you’ll use the IBM Blockchain Platform VSCode Extension, along with the new Hyperledger Fabric programming model and SDK features under the covers.

Background

There is a fantastic description of the commercial paper use case scenario in the latest Fabric Developing Applications docs, and the scenario depicted there makes for fascinating reading. In short, it’s a way for large institutions/organizations to obtain funds to meet short-term debt obligations — and a chance for investors to get a return on investment upon maturity.

The commercial paper scenario in the last tutorial began with employees from MagnetoCorp and DigiBank transacting as participants from their respective organizations, to create an initial history. In this tutorial, you’ll complete the lifecycle but you’ll add a third investor, Hedgematic, into the mix in order to show more historical data. (Creating this history is quick and easy.)

Prerequisites

  1. You will need to have completed the previous commercial paper tutorial — specifically, you should have version 0.0.1 of the commercial paper smart contract package loaded into VSCode under your “Smart Contract Packages” in the “IBM Blockchain Platform” sidebar.

  2. From a terminal window, go to the basic-network directory under the $HOME/fabric-samples (wherever you downloaded the cloned Github Fabric Samples repository) and run the following houskeeping commands/scripts in sequence:

    • cd $HOME/fabric-samples/basic-network
    • ./teardown.sh
    • docker volume prune (answer ‘y’ when prompted)`
    • docker network prune (answer ‘y’ when prompted)`
    • ./start.sh
  3. Go back into VSCode and click on the IBM Blockchain Platform icon. You should see version 0.0.1 of your smart contract packages listed at the top (this is from the previous tutorial). Go ahead and connect to your “fresh” Fabric, via your running myfabric connection, which should still be present in the Blockchain Connections panel (from the previous tutorial). Then install the smart contract package onto peer0.org1.example.com, and instantiate the contract by traversing your myfabric network, just as you did in the previous tutorial:

    • Under ‘Blockchain connections’, select 'myfabric....mychannel....right-click... Instantiate Smart contract'.
    • Supply org.papernet.commercialpaper:instantiate when prompted for which function to call.
  4. In VSCode Explorer, choose File > Open Folder and select the contracts folder by navigating to the $HOME/fabric-samples/commercial-paper/organization/magnetocorp directory. The contracts folder must be your top-level project folder in VSCode.

Completion of these prerequisites is the basis from which this tutorial will proceed. Next, you’ll complete the detailed transaction commercial paper history.

Estimated time

Once the prerequisites are completed, this tutorial should take approximately 45-60 minutes to complete.

Scenario

Isabella, an employee of MagnetoCorp, and Balaji, an investment trader for DigiBank, should both be able to see the history (from the ledger) of a commercial paper now that it has been redeemed (some six months after it was initially issued). Luke, a developer at DigiBank, needs to add query functionality to the smart contract and provide the client apps for DigiBank so that Balaji (or Isabella) can query the ledger from the application. The upgraded smart contract should be active on the channel so the client applications can perform queries and report on the ledger history.

OK — let’s get started!

Steps

Step 1. Add the main query transaction functions in papercontract.js

  1. In VSCode, open the contract folder (if it isn’t already open), which contains the smart contract that you completed in the previous tutorial.

    Figure 2. Open the commercial paper sample project in VSCode Open the commercial paper sample project in VSCode

  2. Open the main contract script file papercontract.js under the lib folder, and add the following lines:

    After the const { Contract, Context } line (around line 8), add the following lines:

    // Tutorial specific 'require' for reporting identity of transactor using the Client Identity Chaincode Library (CID)
    
    const ClientIdentity = require('fabric-shim').ClientIdentity;
    

    This library enables you to obtain the invoker or identity submitting each transaction in your commercial paper lifecycle. It is done for reporting purposes, when you render the invoking identity information in a browser app, later on in the tutorial.

  3. After the // PaperNet specific classes line (around line 16), add another class declaration as follows:

    const QueryUtils = require('./query.js');
    

    For now, don’t worry about any errors reported under “Problems” (if you have ESLint enabled) in the status bar.

  4. While you’re still in papercontract.js, find the function that begins async issue (around line 70) and scroll down to the line paper.setOwner(issuer); then, in the function, create a blank/new line directly under it (which should align with the correct indentation in your code).

  5. Now paste in the following code block, which enables you to report the true identity of the transaction. idGen is a class-based function in papercontract.js that uses the Client Identity Chaincode Library (CID) to obtain the attribute that contains the invoking ID (from its X509 Certificate):

     // Add the invoking CN, to the Paper state
     let invokingId = await this.idGen(ctx);
     paper.setCreator(invokingId);
    

    Note: This code should be located before the line await ctx.paperList.addPaper(paper); in the issue function.

  6. Once again, paste the three-line block into the functions beginning with async buy and async redeem, as you did above. Paste the code block near the end of each of those functions and before the following line shown in each function:

    await ctx.paperList.updatePaper(paper);
    
  7. In the async buy function only, at around line 120 in the code (specifically, the line with comment // Check paper is not already REDEEMED), add this single line of code below the line paper.setOwner(newOwner); but inside the isTrading() code branch:

    paper.setPrice(price);
    
  8. Add the following code block, which contains three functions (two of which are the “callable” query transaction functions) directly after the closing curly bracket of the redeem transaction function, but before the last closing bracket in the file papercontract.js (the one immediately before the module.exports declaration). These two main query functions call the “worker” query functions/iterators in the file query.js, and the idGen function below gets identity information used for reporting:

    /**
     * grab the invoking CN from the X509 transactor cert
     * @param {Context} ctx the transaction context
     */
    
     async idGen(ctx)     {
    
         // Use the Client Identity Chaincode Library (CID) to get the invoker info.
         let cid = new ClientIdentity(ctx.stub);
         let id = cid.getID(); // X509 Certificate invoker is in CN form
         let CN = id.substring(id.indexOf("CN=") + 3, id.lastIndexOf("::"));
         return CN;
     }
    
     /**
     * queryHist commercial paper
     * @param {Context} ctx the transaction context
     * @param {String} issuer commercial paper issuer
     * @param {Integer} paperNumber paper number for this issuer
     */
     async queryHist(ctx, issuer, paperNumber) {
    
         // Get a key to be used for History query
         let cpKey = CommercialPaper.makeKey([issuer, paperNumber]);
         let myObj = new QueryUtils(ctx, 'org.papernet.commercialpaperlist');
         let results = await myObj.getHistory(cpKey);
         //console.log('main: queryHist was called and returned ' + JSON.stringify(results) );
         return results;
    
     }
    
     /**
     * queryOwner commercial paper
     * @param {Context} ctx the transaction context
     * @param {String} issuer commercial paper issuer
     * @param {Integer} paperNumber paper number for this issuer
     */
     async queryOwner(ctx, owner, paperNumber) {
    
         // Get a key to be used for the paper, and get this from world state
         // let cpKey = CommercialPaper.makeKey([issuer, paperNumber]);
         let myObj = new QueryUtils(ctx, 'org.papernet.commercialpaperlist');
         let owner_results = await myObj.queryKeyByOwner(owner);
    
         return owner_results;
     }
    

    Note: Once you’ve pasted this into VSCode, the ESLinter extension (if it is enabled in your VSCode session) may report problems in the Problems pane at the bottom. If it does, you can easily rectify the formatting issues in the Problems pane by choosing right-click…. then Fix all auto-fixable issues. Likewise, it will remove any trailing spaces reported by ESLint (if the line number is reported). Once you complete the formatting task, be sure to save your file via the menu. (You can use Ctrl+S to save your file.) In addition, the ESLint extension (also available from the VSCode extension marketplace) is also useful, and I recommend using it to fix any indentation, incorrect pasting, or general errors that can be detected before you package up the smart contract.

  9. You have two more small functions to add inside the source file, paper.js. Open paper.js under the lib directory in your VSCode session.

  10. After the existing setOwner(newOwner) function (at about line 40) — and under the description called //basic setters and gettersadd the following code block (which contains two functions):

    setCreator(creator) {
        this.creator = creator;
    }
    setPrice(price) {
        this.price = price;
    }
    

    Then press Ctrl+S to save the file.

Step 2. Add requisite “worker” query class functions to your VSCode project (new file: query.js)

  1. Create a new file via the VSCode menu under the contract/lib folder using VSCode and call it query.js.

  2. Copy the contents of the query.js file from the GitHub repo github.com/mahoney1/commpaper, which you cloned earlier.

  3. Paste the contents into your query.js VSCode edit session. You should have all the copied contents in your new query JavaScript “worker” query.js file. Now go ahead and save this file. You’re done with the smart contract edits.

Now let’s get this added smart contract functionality out on the blockchain to replace the older smart contract edition.

Step 3. Upgrade your smart contract version using IBM Blockchain Platform VSCode Extension

  1. You need to add a version change to the package.json file in your project, in preparation for the contract upgrade. Click on the package.json file in Explorer and:

    • Change the version to 0.0.2.
    • Press Ctrl+S to save it.
  2. Click on the source control sidebar icon and click the tick icon to commit, with a message of “adding queries,” and press Enter.

    Now you’re ready to upgrade your smart contract using the VSCode extension.

  3. Package the contract: Click on the IBM Blockchain Platform sidebar icon and under “Smart Contract Packages,” choose the “Add new package” icon (“+”); you should see that version 0.0.2 becomes the latest edition of the available “papercontract” packages.

  4. Upgrade the contract itself: Expand the “Blockchain Connections” pane below, and under the channel mychannel choose peer0.org1.example.com. Expand the peer0.org1.example.com twisty and select the papercontract@0.0.1 entry. You should see all of your transaction functions listed below (expand the twisty to view them), including the new query functions you added.

  5. Right-click on papercontract@0.0.1Upgrade Smart Contract, and choose papercontract@0.0.2 from the list presented (up top).

    • Enter or paste the text org.papernet.commercialpaper:instantiate when prompted to enter “a function name to call,” then press Enter.
    • When prompted to enter any arguments, just use Enter.

    Figure 3. Upgrade the smart contract using VSCode extension Upgrade the smart contract using IBP VSCode extension

    You should get a message in the console that the upgrade is taking place. It will take a minute or so (as it has to build the new smart contract container), and when it has completed you should see a “successful” pop-up message. If you check it using docker ps from a terminal, you should see a new Docker container with the contract version as a suffix under the “Names” column.

Step 4. Create a new DigiBank query client app to invoke query transactions

  1. In VSCode, click on the menu option Fileopen Folder, open the folder under organization/digibank/application, and press Enter.

  2. Right-click on the folder in the left pane and create a new file named queryapp.js. Then paste the contents of the other file named queryapp.js, which is located in the commercial-paper repo directory copied from Step 5 (from $HOME/commpaper/queryapp.js) in the previous Commercial Paper tutorial.

  3. Now you can fix any formatting errors if ESLint is enabled (right-click on “Fix all auto-fixable errors,” and it should automatically fix any indentation issues).

  4. Press Ctrl+S to save the file, then click on the Source Control icon to commit the file, with a commit message. The queryapp.js client contains two query functions:

    • A queryHist function that gets the history of a commercial paper instance
    • A queryOwner function that gets the list of commercial papers owned by an organization (provided as a parameter to the query function)

    Next, you’ll create the transaction history, then run the new query client app to execute a set of queries. (You’ll do this from a terminal window in DigiBank’s application folder; it doesn’t matter whether you test from MagnetoCorp or DigiBank in this example — you should see the same data on the ledger from either application client.)

Step 5. Perform the issue, buy, and redeem transactions to update the ledger

Let’s create some transactions, invoked as different identities, to create a history of transactions on the ledger. The sequence is:

  1. Issue a paper as “MagnetoCorp.”
  2. Buy the paper as “DigiBank,” the new owner.
  3. Buy the paper as “Hedgematic,” the changed owner.
  4. Redeem the paper at face value, as existing owner “Hedgematic,” with MagnetoCorp as the original issuer.

Note: You will have installed any Node.js application dependencies (npm install) for the existing client applications from the previous tutorial. You will also make use of the existing identity wallets populated in that tutorial. (These are located at the same level as application under both the magnetocorp and digibank subdirectories, respectively.)

Figure 4. “Papernet” — overview of transaction flow Transaction flow

Transaction #1. Execute an issue transaction as Isabella@MagnetoCorp

  1. Open a terminal window and change the directory to MagnetoCorp’s application directory (assuming $HOME is the holding location below):

    cd $HOME/fabric-samples/commercial-paper/organization/magnetocorp/application
    
  2. Now execute the first commercial paper transaction from the application directory — the issue transaction:

    node issue.js
    

    You should get messages confirming it was successful:

    Figure 5. The issue transaction The issue transaction

Transaction #2. Execute a buy transaction as Balaji@DigiBank

  1. In the same terminal window, change the directory to DigiBank’s application directory:

    cd ../../digibank/application
    
  2. Now execute the first commercial paper buy transaction from the application directory:

    node buy.js
    

    You should get messages confirming it was successful:

    Figure 6. The buy transaction The buy transaction

Transaction #3. Execute another buy transaction as Bart@Hedgematic

DigiBank is restructuring its investment portfolio and has decided to sell the commercial paper for a small profit to release funds earlier. The purchaser, Hedgematic, sees this as an opportunity to increase its commercial paper portfolio and recoup the face value of the paper at some point in the future. Let’s execute this transaction as an employee of Hedgematic. (For convenience, you’re providing a temporary wallet for the Hedgematic employee “Bart,” so Hedgematic can invoke a buy transaction.)

  1. Copy the buy2.js client application script from the commpaper repo directory into the current commercial-paper/organization/digibank/application directory for now (make sure to insert the dot “.” in the command below):

    cp $HOME/commpaper/buy2.js .
    
  2. Copy the file wallet.zip from the commpaper repo directory into the /tmp directory, then unzip it as follows:

    cp $HOME/commpaper/wallet.zip /tmp
    
    cd /tmp
    
    unzip wallet.zip
    

    This will unzip the user Bart’s wallet into a subdirectory under /tmp/wallet. After this extraction, you will have a directory named /tmp/wallet/bart@hedgematic that contains Bart@Hedgematic’s identity wallet.

  3. Now run the second buy transaction (using Bart’s identity in the client application, buy2.js), as follows:

    node buy2.js
    

    You should get messages confirming it was successful:

    Figure 7. The second buy transaction The second buy transaction

Transaction #4: Execute a redeem transaction as Bart@Hedgematic — six months later

The time has come in this commercial paper’s lifecycle for the commercial paper to be redeemed by its current owner (Hedgematic) at face value, so it recoups its investment outlay. A client application named redeem.js performs this task, and it needs to use bart@hedgematic’s identity from owner Hegematic to perform it. (Currently, the redeem.js sample script uses balaji‘s identity, but because Hedgematic has since bought the paper from DigiBank, you need to modify the script to redeem it properly as Hedgematic’s Bart.) For the purposes of this tutorial, you just need to run the client application script for redeem from the digibank application subdirectory.

  1. Once again, from a terminal window and the same directory, $HOME/fabric-samples/commercial-paper/organization/digibank/application, edit the file redeem.js.

  2. Change the line beginning with const wallet = (around line 25) to read as follows (you may prefer to copy the line and comment the original using //; the wallet points to the downloaded wallet directory as shown below):

    const wallet = new FileSystemWallet('/tmp/wallet');
    

    Note: If you prefer, you can issue your own identity using the currently active CA server, using the Fabric-CA utilities or APIs, and change this script as appropriate.

  3. Change the line beginning with const userName = (around line 38) to read as follows (you may prefer to copy the existing line, and comment the original line, using // ), so the userName points to Bart, the Hedgematic employee:

    const userName = 'bart@hedgematic';
    

    Note: If you prefer, you can issue your own identity using the currently active CA server, using the Fabric-CA utilities or APIs, and change this script as appropriate.

  4. Change the line beginning with const redeemResponse (around line 67), and change the fourth parameter to “Hedgematic”:

    const redeemResponse = await contract.submitTransaction('redeem', 'MagnetoCorp', '00001', 'Hedgematic', '2020-11-30')
    

    Then save your file and commit any changes.

  5. Now run the redeem.js script:

    node redeem.js
    

    You should get messages confirming it was successful:

    Figure 8. The redeem transaction — the last in the lifecycle The redeem transaction

Step 6. Launch the sample DigiBank client query application

  1. From a terminal window, change the directory to the $HOME/fabric-samples/commercial-paper/organization/digibank/application folder.

  2. Run the queryapp client using the node:

    node queryapp.js
    
  3. You should see the JSON results from both the queryHist and queryOwner functions in the terminal window. It also creates a file called results.json in the current directory (history of the asset) as a result of the queryHist query invocation.

    Figure 9. The queryapp client results The queryapp client results

Step 7. Display the history in a nice HTML-based UI

For this step, you use a simple Tabulator that renders your results in an HTML table. You don’t have to install any code or client per se, nor use jQuery — you just need to use a simple HTML file that uses online CSS formatting and performs a local XMLHttpRequest() GET REST API call to load the local results (from the JSON file, avoiding CORS issues) and render it in the table. That index.html file comes from the “commpaper” Github repo cloned previously; please take some time to peruse the HTML file.

Note: This HTML file is provided as-is, and is purely used for rendering in a Firefox browser. At the time of this writing, some of the JavaScript formatting (it doesn’t use jQuery) does not work in Chrome (which doesn’t like forEach), but has been tested in Firefox.

  1. In a terminal window, open the DigiBank application directory once again (you should already be there).

  2. Copy the index.html file from the commpaper repo (cloned earlier) into the directory (with the trailing “.” for the current directory):

    cp $HOME/commpaper/index.html .
    

    If you examine the HTML file in VSCode Explorer, you’ll see that it performs a REST /GET API call and loads a results file called results.json (created by the queries app invoked earlier) and renders these in a table in a browser. The results.json file contains the query results.

  3. Launch a Firefox browser session (see note above) providing the index.html file provided as a parameter — tested with Firefox:

    firefox index.html
    
  4. You should see the results in tabular form in the browser; expand or contract column widths as necessary, such as longer columns like Txn ID. Note that TxId here is the Fabric transaction ID. The Invoking ID is the invoker Common Name, which is extracted using the Client Identity library mentioned earlier. Obviously, this would need to be made available as an attribute by any of the organizations that execute transactions using this shared smart contract. As an alternative, a hash of the signer certificate can be used.

    Figure 10. Commercial paper: Asset history report Commercial Paper: Asset History Report

Well done! You’ve now managed to successfully add query functionality to the commercial paper sample smart contract using the IBM Blockchain Platform VSCode extension.

Summary

This tutorial showed you how to add queries and upgrade your existing commercial paper contract using the IBM Blockchain Platform VSCode extension and use features from Hyperledger Fabric’s new programming model. Take time to peruse the transaction (query) functions in both papercontract.js and the query class file query.js under the lib directory. And be sure to peruse the client application, queryapp.js.

You’ve learned how to render the history results (the history of a commercial paper asset) in a simple browser-based HTML application. The final tutorial in this series will show only changes during the lifecycle.

As a last step, it is good practice to close out your current folders in VSCode, in preparation for the next tutorial. The third and final tutorial in this series will show you how to add smart contract functionality to query only the deltas for the history of a particular asset.

Until then, thanks for joining me!