In Chapter 2, we referenced the Commercial Paper scenario provided as part of the Hyperledger Fabric samples repository. In this chapter, we will examine this scenario in depth as a tutorial to better understand what it is doing. We will then extend the tutorial to create a new transaction in the smart contract and develop a new command line application to invoke it.
The Commercial Paper tutorial simulates a simple commercial paper trading network called PaperNet. Commercial paper is a type of unsecured lending called a promissory note. Paper is normally issued by large corporations to raise funds to meet short-term financial obligations at a fixed rate of interest. Once issued at a fixed price for a fixed term, another company or bank will purchase commercial paper at a cost lower than its face value, and when the term is up, it will be redeemed for its face value.
As an example, if paper was issued at a face value of $10 million for a six-month term at 2% interest, then it could be bought for $9.8 million ($10 million – 2%) by another company or bank happy to bear the risk that the issuer will not default. Once the term is up, then the paper could be redeemed or sold back to the issuer for its full face value of $10 million. Between buying and redemption, the paper can be bought or sold multiple times between different parties on a commercial paper market.
Previously, we looked at how we can use the idea of assets, participants, and transactions to analyze blockchain use cases.
From this description, we can see that the main asset here is the commercial paper itself, which will have multiple attributes such as the issuing company, the face value of the paper, the redemption date, and the current state (such as issued, trading, and redeemed) of the paper.
We can also see that there are multiple participants in this scenario: an issuer who will be responsible for creating or issuing commercial paper, and one or more buyers of commercial paper who will own the paper until it is either redeemed or sold on to another party.
Finally, we have the transactions: they are issue, buy and redeem, to issue new commercial paper, trade it, and redeem the face value of the paper with the issuer, respectively.
In the tutorial there are two key participants: MagnetoCorp and DigiBank. The tutorial shows MagnetoCorp initially acting as an issuer, with DigiBank taking on the role of the buyer and redeemer of the commercial paper in the PaperNet network.
Figure 5-1 shows an overview of the network, with Isabella working for MagnetoCorp, the issuer, Balaji working for an example trader (DigiBank), and the two organizations communicating over the PaperNet blockchain:
Hyperledger Fabric contains a tutorial for Commercial Paper. In this section, we introduce the purpose of this tutorial and the main tasks involved.
A more detailed description of the Commercial Paper tutorial is available online; if you are a developer looking for hands-on experience of Hyperledger Fabric, then we recommend that you follow this. At the time of writing, the tutorial is available here.
Initially, the tutorial takes you through installing some prerequisite software and downloading the samples. Once this is done you take on the role of Isabella from MagnetoCorp, who will create the network that DigiBank will join. After installing and instantiating the smart contract, she will run a command-line application that will invoke the smart contract’s issue transaction.
Next, you switch to the role of Balaji from DigiBank, who will use different command-line applications to buy the paper that Isabella issued and then redeem it with another application.
The tutorial uses a command-line application for each smart contract transaction. Although this makes it very easy to see what is going on from the tutorial’s point of view, in a more realistic scenario, one application might be expected to call several smart contract transactions.
At the end of the tutorial, one commercial paper asset has been issued, bought, and redeemed in the network.
Although the tutorial shows many aspects of smart contract development and transaction invocation, it currently does not provide a way to query for particular paper. Therefore, in this section we will look at extending the commercial paper smart contract to add a new getPaper
transaction that will return a string representation of the requested paper to the caller. We will then create a new command-line application based on one of the existing ones in order to invoke it.
This will provide us with a way of looking at the different states commercial paper will go through during its lifecycle. We will then issue new paper and follow it through its lifecycle, querying it at each step along the way.
The new getPaper
transaction follows the same model as the buy and redeem transactions, but it is much simpler as we do not need to update any properties of the paper in the world state:
/**
*
Get
commercial
paper
*
@param
{
Context
}
ctx
the
transaction
context
*
@param
{
String
}
issuer
commercial
paper
issuer
*
@param
{
Integer
}
num
paper
number
for
this
issuer
*/
async
getPaper
(
ctx
,
issuer
,
num
)
{
try
{
console
.
log
(
"getPaper for: "
+
issuer
+
" "
+
num
);
let
paperKey
=
CommercialPaper
.
makeKey
([
issuer
,
num
]);
let
paper
=
await
ctx
.
paperList
.
getPaper
(
paperKey
);
return
paper
.
toBuffer
();
}
catch
(
e
)
{
throw
new
Error
(
'Paper does not exist'
+
issuer
+
num
);
}
}
Add this code to the commercial-paper/organization/magnetocorp/contract/lib/papercontract.js file and save it. It needs to sit as a top-level transaction like the other transactions, so place it after the redeem transaction almost at the bottom of the file.
We’ll now walk through the changes we made.
After the comments, there is the definition of the getPaper
transaction, which takes three parameters. The first one (ctx
) is of type Context
and is the first parameter passed to all transactions. This allows the framework to pass extra information into the transaction function when it is called. For example, it can pass information about the identity of the caller of the contract as well as methods to query the worldstate when the transaction is called. The second and third parameters (issuer
and num
) are passed in from the calling application and contain the issuer and number of the paper we wish to retrieve.
After logging the parameters passed in to the console log, the transaction calls the static CommercialPaper.makeKey
method. This method is defined in the ledger-api/state.js file and is a helper method to create a world state key for the paper. Remember from the last chapter that data in the world state is stored as key/value pairs, and for the commercial paper tutorial the key is defined using this method. In this example, the key is simply a concatenation of the issuer and the paper number, separated by a colon; for example, MagnetoCorp:00001
.
Next, the transaction uses the paperKey
return value to request a specific paper by calling ctx.paperList.getPaper
and passing in the key (paperKey
). As you can see, this is using the ctx
parameter to access the paperList
and call its getPaper
method. The definition of this method is provided in the lib/paperlist.js file.
In turn, paperList.getPaper
simply calls the getState
method defined in ledger-api/stateList.js. It is this method that actually accesses the world state to retrieve the requested commercial paper asset.
Once we have the requested paper, we simply return it to our caller as a buffer object.
If the requested commercial paper does not exist in the world state, an exception will be thrown, informing our caller that the requested paper does not exist.
The getPaper.js
application is based on the existing buy.js
application that comes with the Commercial Paper tutorial. We can’t show you all the code here as there are over 100 lines of code. However, most of these lines remain the same—we are only going to change about 12 of them.
To get started, make a copy of the commercial-paper/organization/digibank/application/buy.js file and call the copy getPaper.js
. Make sure the copy is in the same folder as buy.js
.
Open the file in a code editor such as VSCode and look through the file for the console.log
lines that contain the string “Buy program;” for example:
console
.
log
(
'Buy program complete.'
);
...
console
.
log
(
'Buy program exception.'
);
To avoid confusion, change Buy
to GetPaper
throughout, so that when we subsequently review these console logs we see the correct application’s output.
Next, delete the buy commercial paper
and process response
sections in the main
method, as these are not relevant to the getPaper
application:
//
buy
commercial
paper
console
.
log
(
'Submit commercial paper buy transaction.'
);
const
buyResponse
=
await
contract
.
submitTransaction
(
'buy'
,
'MagnetoCorp'
,
'00001'
,
'MagnetoCorp'
,
'DigiBank'
,
'4900000'
,
'2020-05-31'
);
//
process
response
console
.
log
(
'Process buy transaction response.'
);
let
paper
=
CommercialPaper
.
fromBuffer
(
buyResponse
);
console
.
log
(
`$
{
paper
.
issuer
}
commercial
paper
:
$
{
paper
.
paperNumber
}
successfully
purchased
by
$
{
paper
.
owner
}
`
);
In place of this deleted block, just before the Transaction Complete
log message, add the following code. (Note that you will be adding in a few more lines than you deleted, but that’s okay—we are doing a little more work to nicely format the returned paper object. Make sure you save the file when you are done.)
//
get
commercial
paper
console
.
log
(
'Evaluate getPaper transaction.'
);
const
getPaperResponse
=
await
contract
.
evaluateTransaction
(
'getPaper'
,
'MagnetoCorp'
,
'00001'
);
console
.
log
(
'Process getPaper transaction response.'
);
let
paper
=
CommercialPaper
.
fromBuffer
(
getPaperResponse
);
let
paperState
=
"Unknown"
;
if
(
paper
.
isIssued
())
{
paperState
=
"ISSUED"
;
}
else
if
(
paper
.
isTrading
())
{
paperState
=
"TRADING"
;
}
else
if
(
paper
.
isRedeemed
())
{
paperState
=
"REDEEMED"
;
}
console
.
log
(
` +--------- Paper Retrieved ---------+ `
);
console
.
log
(
` | Paper number: "${paper.paperNumber}"`
);
console
.
log
(
` | Paper is owned by: "${paper.owner}"`
);
console
.
log
(
` | Paper is currently: "${paperState}"`
);
console
.
log
(
` | Paper face value: "${paper.faceValue}"`
);
console
.
log
(
` | Paper is issued by: "${paper.issuer}"`
);
console
.
log
(
` | Paper issue on: "${paper.issueDateTime}"`
);
console
.
log
(
` | Paper matures: "${paper.maturityDateTime}"`
);
console
.
log
(
` +-----------------------------------+ `
);
Looking at this new code that we inserted, we can see that it starts by logging the call it is about to make on the contract to the console. It then calls the evaluateTransaction
method of the contract to submit the call to the peer to run the getPaper
transaction.
Note that in getPaper
we are using evaluateTransaction
rather than the submitTransaction
method that the other transactions use; the difference is that evaluateTransaction
does not record the transaction on the ledger, and as we are not changing any state when returning the paper—this is okay.
The parameters passed to evaluateTransaction
indicate that we want to get MagnetoCorp’s paper 00001, which was the one issued when you ran the tutorial. Once the paper has been returned, we find out which state the paper is in (ISSUED
, TRADING
, or REDEEMED
) and then print this out along with the other information contained within the paper such as its paper number, issuer, owner, and face value.
Now that we have edited the smart contract to add the new transaction and created the new getPaper
command line application, it is time to test them both out. To do this, we first have to install the modified smart contract on to the peer. From the magnetocorp
console window, which you should have open from running the tutorial, issue this command to install the new version 2 of the contract:
(magnetocorp admin)$ docker exec cliMagnetoCorp peer chaincode install -n papercontract -v 2 -p /opt/gopath/src/github.com/co ntract -l node
When it completes, you should see output like this:
2019-02-21 13:32:23.824 UTC [chaincodeCmd] checkChaincodeCmdPa rams -> INFO 001 Using default escc 2019-02-21 13:32:23.824 UTC [chaincodeCmd] checkChaincodeCmdPa rams -> INFO 002 Using default vscc 2019-02-21 13:32:23.832 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" >
Next, we have to upgrade the version 2 smart contract on the peer to make it live:
(magnetocorp admin)$ docker exec cliMagnetoCorp peer chaincode upgrade -n papercontract -v 2 -l node -c '{"Args":["org.papern et.commercialpaper:instantiate"]}' -C mychannel -P "AND ('Org1 MSP.member')"
When it completes (which can take a few minutes), you should see output like this:
2019-02-21 13:32:35.508 UTC [chaincodeCmd] InitCmdFactory -> INFO 001 Retrieved channel (mychannel) orderer endpoint: orderer.example.com:7050 2019-02-21 13:32:35.511 UTC [chaincodeCmd] checkChaincodeCmdPar ams -> INFO 002 Using default escc 2019-02-21 13:32:35.511 UTC [chaincodeCmd] checkChaincodeCmdPar ams -> INFO 003 Using default vscc
Now we are ready to invoke the getPaper.js
application and test out the new smart contract transaction. First, check that the console window for Balaji from DigiBank, which you should have open from running the tutorial, is currently in the commercial-paper/organization/digibank/application directory. If it is not, just use cd
to change to this directory. This is where the new getPaper.js
application is located.
Issue this command to run the application:
(balaji)$ node getPaper.js
You should expect to see output like this:
Connect to Fabric gateway. Use network channel: mychannel. Use org.papernet.commercialpaper smart contract. Submit commercial paper getPaper transaction. Process getPaper transaction response. +--------- Paper Retrieved ---------+ | Paper number: "00001" | Paper is owned by: "MagnetoCorp" | Paper is currently: "REDEEMED" | Paper face value: "5000000" | Paper is issued by: "MagnetoCorp" | Paper issue on: "2020-05-31" | Paper matures on: "2020-11-20" +-----------------------------------+ Transaction complete. Disconnect from Fabric gateway. GetPaper program complete.
Assuming that you have completed the tutorial successfully earlier, you should now see the final REDEEMED
state of paper number 00001 that MagnetoCorp issued after it was bought and then redeemed by DigiBank.
Now let’s go through this one more time, issuing, buying, and redeeming a second paper (number 00002). But this time, we will look at the output from getPaper
each step along the way.
To do this we will need to edit all four programs to use the new paper. We’ll start with one of the easiest: getPaper.js
. Open up getPaper.js
in your editor and find this line of code:
const
getPaperResponse
=
await
contract
.
evaluateTransaction
(
'getPaper'
,
'MargetoCorp'
,
'00001'
);
Change the paper number from 00001
to 00002
and save the change to the file. Although you could run getPaper.js
at this point, it would return an error as there is currently no paper with the number 00002. So, let’s create one.
Open up issue.js
from the organization/magnetocorp/application directory and find this line:
const
issueResponse
=
await
contract
.
submitTransaction
(
'issue'
,
'MagnetoCorp'
,
'00001'
,
'2020-05-31'
,
'2020-11-20'
,
'5000000'
);
Change the paper number from 00001
to 00002
, and so we can see more differences, change the amount from 5000000
to 6000000
. You can change the dates as well as if you wish. Your changed line should look something like this:
const
issueResponse
=
await
contract
.
submitTransaction
(
'issue'
,
'MagnetoCorp'
,
'00002'
,
'2019-06-30'
,
'2019-12-30'
,
'6000000'
);
From the MagnetoCorp console window, issue this command to issue paper 00002:
(magnetocorp admin)$ node issue.js
You should see output indicating that the paper was issued successfully, with output of the form:
MagnetoCorp commercial paper : 00002 successfully issued for value 6000000
Next, from Balaji’s console window run the getPaper
application:
(
balaji
)
$
node
getPaper
.
js
The output should contain:
+--------- Paper Retrieved ---------+ | Paper number: "00002" | Paper is owned by: "MagnetoCorp" | Paper is currently: "ISSUED" | Paper face value: "6000000" | Paper is issued by: "MagnetoCorp" | Paper issue on: "2019-06-30" | Paper matures on: "2019-12-30" +-----------------------------------+
Now we can see the paper has now been issued, but it is still owned by MagnetoCorp as it has not yet been sold. Let’s now buy this new paper as DigiBank. Open up buy.js
from the organization/digibank/application folder and find this line:
const
buyResponse
=
await
contract
.
submitTransaction
(
'buy'
,
'MagnetoCorp'
,
'00001'
,
'MagnetoCorp'
,
'DigiBank'
,
'4900000'
,
'2020-05-31'
);
If we look at the buy
transaction in the papercontract.js smart contract, we can see the parameters representing the transaction to call (buy
), the issuer, the paper number, the current owner, the new owner, the price, and the purchase date and time. Edit the line to update the parameters for the new paper to look like this:
const
buyResponse
=
await
contract
.
submitTransaction
(
'buy'
,
'MagnetoCorp'
,
'00002'
,
'MagnetoCorp'
,
'DigiBank'
,
'5880000'
,
'2019-10-30'
);
When you are done, save the file and from Balaji’s console window run the buy
command:
(
balaji
)
$
node
buy
.
js
You should see output stating that the transaction was successful:
MagnetoCorp commercial paper : 00002 successfully purchased by DigiBank
Next, let’s run getPaper
again:
(
balaji
)
$
node
getPaper
.
js
The output should include this:
+--------- Paper Retrieved ---------+ | Paper number: "00002" | Paper is owned by: "DigiBank" | Paper is currently: "TRADING" | Paper face value: "6000000" | Paper is issued by: "MagnetoCorp" | Paper issue on: "2019-06-31" | Paper matures on: "2019-12-30" +-----------------------------------+
As we can see, the paper is now owned by DigiBank and is now in the TRADING
state.
Finally, let’s change redeem.js
, which is alongside buy.js
in the DigiBank application folder. Open the file in your editor and find this line:
const
redeemResponse
=
await
contract
.
submitTransaction
(
'redeem'
,
'MagnetoCorp'
,
'00001'
,
'DigiBank'
,
'2020-11-30'
);
Here we need to change the paper number and the redeeming date for completeness, so edit the line to look like this:
const
redeemResponse
=
await
contract
.
submitTransaction
(
'redeem'
,
'MagnetoCorp'
,
'00002'
,
'DigiBank'
,
'2019-11-30'
);
When you are done, save the file and from Balaji’s console window run the redeem command:
(
balaji
)
$
node
redeem
.
js
You should see output stating that the transaction was successful:
MagnetoCorp commercial paper : 00002 successfully redeemed with MagnetoCorp
Next, let’s run getPaper
one last time:
(
balaji
)
$
node
getPaper
.
js
The output should look like this:
+--------- Paper Retrieved ---------+ | Paper number: "00002" | Paper is owned by: "MagnetoCorp" | Paper is currently: "REDEEMED" | Paper face value: "6000000" | Paper is issued by: "MagnetoCorp" | Paper issue on: "2019-06-30" | Paper matures on: "2019-12-30" +-----------------------------------+
Here we can see the paper is now redeemed and the cycle is complete. At this point, feel free to experiment on your own, maybe by extending the smart contract further to improve error checking or by writing a new command-line application to find all TRADING
or REDEEMED
papers. Or, you could try and make the applications easier to use by taking arguments from the command line for the parameters such as paperNumber
to save having to edit the files manually to work with a new asset.
We have taken a whirlwind tour of the Commercial Paper tutorial, and have updated its smart contract to include a new getPaper
transaction. We have installed and upgraded the PaperNet network to the new version of the contract, and we have even written a new command-line application to execute the new getPaper
transaction. Finally, we issued a new commercial paper asset and followed it on its lifecycle, looking at the state changes made to it on its journey.
If you are a developer of smart contracts, there is a free-to-use tool available to make your smart contract development much easier. The IBM Blockchain Platform Extension for VSCode helps Hyperledger Fabric developers to develop and test smart contracts and client applications on their local machines, as well as package their projects for deployment into IBM Blockchain Platform runtimes.
In the final chapter, we will take a look at the shape of things to come and where the future lies for blockchain technology.