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 the IBM Blockchain Platform sidebar panel, under the LOCAL FABRIC OPS pane, click on the ellipsis (“…”) and elect to do a “Teardown Fabric Runtime.” Answer “yes” to the prompt (in the bottom right of your VSCode panel) to destroy world state and ledger data, then await the output messages confirming that the teardown is successful.

  3. Now elect to “Start a Fabric development runtime” under “LOCAL FABRIC OPS” on the sidebar pane. Await confirmation that you have “Successfully submitted proposal to join the channel” in the output messages.

  4. From the IBM Blockchain Platform sidebar panel, install papercontract version 0.0.1 using the “Install” option under LOCAL FABRIC OPS, and select the peer offered and the smart contract version 0.0.1 as indicated.

  5. Next, choose the “Instantiate” option and select the channel “mychannel,” and papercontract version 0.0.1.

  6. When prompted to enter a function, paste in the text below and hit ENTER twice (no parameters to the function):

    org.papernet.commercialpaper:instantiate
    
  7. 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 line (after the PaperNet-specific classes line, around line 16):

    const QueryUtils = require('./query.js');
    
  3. 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).

  4. Now paste in the following code block, which enables you to report the invoker CN of the transaction. The getInvoker function uses the clientIdentity object that’s available via the transaction context (ctx). Remember to highlight the code pastes, right-click > “Format Selection” if the pasted code is not indented correctly.

    // Add the invoking CN, to the Paper state for reporting purposes later on
    let invokingId = await this.getInvoker(ctx);
    paper.setCreator(invokingId);
    

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

  5. 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);
    
  6. 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);
    
  7. 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 getInvoker(ctx) {
    
         // Use the Client Identity object to get the invoker info.
         let cid = ctx.clientIdentity;
         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.

  8. Highlight the code you pasted and right-click > “Format selection” to format it correctly in your JavaScript file.

  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 “Package a Smart Contract” icon (“+”); you should see that version 0.0.2 becomes the latest edition of the available “papercontract” packages.

  4. Under the Local Fabric Ops panel in the IBM Blockchain Platform sidebar, expand the instantiated contract “papercontract” and highlight it.

  5. Right-click on papercontract@0.0.1Upgrade Smart Contract, and choose papercontract@0.0.2 from the list presented (up top) — then select the peer offered at the 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 commpaper repo that you cloned previously (see steps in the “Summary” section of 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.

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

Set up up the client connection for the application client

In preparation for connecting your local application JavaScript clients to the local Fabric development runtime, you need to do some simple actions to get connection information to use in those JavaScript clients. The high level steps are:

  1. Export the connection details (connection.json) from the IBM Blockchain Platform VSCode extension panel.

  2. Have the client application scripts (cloned earlier with the Fabric samples repo) used by this tutorial point to this exported connection file for its connection information. In the next sequence, you will change the following 3 Fabric sample clients in the subdirectories shown:

    'magnetocorp/organization/application/issue.js'
    'digibank/organization/application/buy.js'
    'digibank/organization/application/redeem.js'
    

    (Note: The fourth client script, application/buy2.js, which is in the custom github.com/mahoney1/commpaper repo you cloned previously and is used further down in this tutorial, already has the changes.)

  3. From the IBM Blockchain Platform extension sidebar, locate the development peer under “Nodes” in the sidebar pane LOCAL FABRIC OPs on the left.

  4. Right-click on the peer and select “Export Connection Details.” The export location creates a “local_fabric” subdirectory under the contract folder of your smart contract workspace (such as $HOME/fabric-samples/commercial-paper/organization/magnetocorp/contract/local_fabric/), where $HOME is “/home/user1” in this example — but should be different for you.

  5. From a terminal window, open up the file $HOME/fabric-samples/commercial-paper/organization/magnetocorp/application/issue.js in VSCode and edit the following sections:

    • Comment out the line beginning with const yaml = (approx line 19) so that it reads:
      //const yaml = require('js-yaml');
      
    • Comment out the following line (approx line 41) that begins with let connectionProfile = yaml.safeLoad so that it reads:

      // let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/networkConnection.yaml', 'utf8'));
      
    • Below this line, add the following 2 lines of code.

      Note: The path provided is in single quotes below. For readFileSync (below), you must provide (replace below as necessary) the full file export path to your connection.json. Copy that path from the “output” panel and add the filename as shown below. (The filepath below may be different for you; I am using the home directory for “user1” in this example!)

      let fpath = fs.readFileSync('/home/user1/fabric-samples/commercial-paper/organization/magnetocorp/contract/local_fabric/connection.json', 'utf8');
      let connectionProfile = JSON.parse(fpath);
      

    And that’s all of the changes for the “issue” client.

  6. Next, change the “buy.js” and “redeem.js” client scripts in exactly the same way — so complete step 5 above for each of the “buy.js” and “redeem.js” scripts so that each reads the local_fabric connection.json file path as shown.

Note: You will need to install Node.js application dependencies (use npm install for both Transactions #1 and #2 below) for the existing MagnetoCorp and DigiBank client applications below. You will also make use of the existing identity wallets populated in the fabric-samples repo. (These wallets are located at the same level as the application under both the organization/magnetocorp and organization/digibank subdirectories, respectively.)

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!