github.com/kaituanwang/hyperledger@v2.0.1+incompatible/docs/source/tutorial/commercial_paper.md (about)

     1  # Commercial paper tutorial
     2  
     3  **Audience:** Architects, application and smart contract developers,
     4  administrators
     5  
     6  This tutorial will show you how to install and use a commercial paper sample
     7  application and smart contract. It is a task-oriented topic, so it emphasizes
     8  procedures above concepts. When you’d like to understand the concepts in more
     9  detail, you can read the
    10  [Developing Applications](../developapps/developing_applications.html) topic.
    11  
    12  ![commercialpaper.tutorial](./commercial_paper.diagram.1.png) *In this tutorial
    13  two organizations, MagnetoCorp and DigiBank, trade commercial paper with each
    14  other using PaperNet, a Hyperledger Fabric blockchain network.*
    15  
    16  Once you've set up the test network, you'll act as Isabella, an employee of
    17  MagnetoCorp, who will issue a commercial paper on its behalf. You'll then switch
    18  roles to take the role of Balaji, an employee of DigiBank, who will buy this
    19  commercial paper, hold it for a period of time, and then redeem it with
    20  MagnetoCorp for a small profit.
    21  
    22  You'll act as an developer, end user, and administrator, each in different
    23  organizations, performing the following steps designed to help you understand
    24  what it's like to collaborate as two different organizations working
    25  independently, but according to mutually agreed rules in a Hyperledger Fabric
    26  network.
    27  
    28  * [Set up machine](#prerequisites) and [download samples](#download-samples)
    29  * [Create the network](#create-the-network)
    30  * [Examine the commercial paper smart contract](#examine-the-commercial-paper-smart-contract)
    31  * [Deploy the smart contract to the channel](#deploy-the-smart-contract-to-the-channel)
    32    by approving the chaincode definition as MagnetoCorp and Digibank.
    33  * Understand the structure of a MagnetoCorp [application](#application-structure),
    34    including its [dependencies](#application-dependencies)
    35  * Configure and use a [wallet and identities](#wallet)
    36  * Run a MagnetoCorp application to [issue a commercial paper](#issue-application)
    37  * Understand how DigiBank uses the smart contract in their [applications](#digibank-applications)
    38  * As Digibank, run applications that
    39    [buy](#buy-application) and [redeem](#redeem-application) commercial paper
    40  
    41  This tutorial has been tested on MacOS and Ubuntu, and should work on other
    42  Linux distributions. A Windows version is under development.
    43  
    44  ## Prerequisites
    45  
    46  Before you start, you must install some prerequisite technology required by the
    47  tutorial. We've kept these to a minimum so that you can get going quickly.
    48  
    49  You **must** have the following technologies installed:
    50  
    51    * [**Node**](https://nodejs.org/en/about/) version 8.9.0, or higher. Node is
    52      a JavaScript runtime that you can use to run applications and smart
    53      contracts. You are recommended to use the LTS (Long Term Support) version
    54      of node. Install node [here](https://nodejs.org/en/).
    55  
    56  
    57    * [**Docker**](https://www.docker.com/get-started) version 18.06, or higher.
    58      Docker help developers and administrators create standard environments for
    59      building and running applications and smart contracts. Hyperledger Fabric is
    60      provided as a set of Docker images, and the PaperNet smart contract will run
    61      in a Docker container. Install Docker
    62      [here](https://www.docker.com/get-started).
    63  
    64  You **will** find it helpful to install the following technologies:
    65  
    66    * A source code editor, such as
    67      [**Visual Studio Code**](https://code.visualstudio.com/) version 1.28, or
    68      higher. VS Code will help you develop and test your application and smart
    69      contract. Install VS Code [here](https://code.visualstudio.com/Download).
    70  
    71      Many excellent code editors are available including
    72      [Atom](https://atom.io/), [Sublime Text](http://www.sublimetext.com/) and
    73      [Brackets](http://www.sublimetext.com/).
    74  
    75  You **may** find it helpful to install the following technologies as you become
    76  more experienced with application and smart contract development. There's no
    77  requirement to install these when you first run the tutorial:
    78  
    79    * [**Node Version Manager**](https://github.com/creationix/nvm). NVM helps you
    80      easily switch between different versions of node -- it can be really helpful
    81      if you're working on multiple projects at the same time. Install NVM
    82      [here](https://github.com/creationix/nvm#installation).
    83  
    84  ## Download samples
    85  
    86  The commercial paper tutorial is one of the samples in the `fabric-samples`
    87  directory. Before you begin this tutorial, ensure that you have followed the
    88  instructions to install the Fabric [Prerequisites](../prereqs.html) and
    89  [Download the Samples, Binaries and Docker Images](../install.html).
    90  When you are finished, you will have cloned the `fabric-samples` repository that
    91  contains the tutorial scripts, smart contract, and application files.
    92  
    93  ![commercialpaper.download](./commercial_paper.diagram.2.png) *Download the
    94  `fabric-samples` GitHub repository to your local machine.*
    95  
    96  After downloading, feel free to examine the directory structure of `fabric-samples`:
    97  
    98  ```
    99  $ cd fabric-samples
   100  $ ls
   101  
   102  CODEOWNERS            basic-network			    first-network
   103  CODE_OF_CONDUCT.md	  chaincode			        high-throughput
   104  CONTRIBUTING.md	      chaincode-docker-devmode  interest_rate_swaps
   105  Jenkinsfile           ci                        off_chain_data
   106  LICENSE               ci.properties             scripts
   107  MAINTAINERS.md        commercial-paper          test-network
   108  README.md             docs
   109  SECURITY.md           fabcar
   110  ```
   111  
   112  Notice the `commercial-paper` directory -- that's where our sample is located!
   113  
   114  You've now completed the first stage of the tutorial! As you proceed, you'll
   115  open multiple command windows for different users and components. For example:
   116  
   117  * To show peer, orderer and CA log output from your network.
   118  * To approve the chaincode as an administrator from MagnetoCorp and as an
   119    administrator from DigiBank.
   120  * To run applications on behalf of Isabella and Balaji, who will use the smart
   121    contract to trade commercial paper with each other.
   122  
   123  We'll make it clear when you should run a command from particular command
   124  window; for example:
   125  
   126  ```
   127  (isabella)$ ls
   128  ```
   129  
   130  indicates that you should run the `ls` command from Isabella's window.
   131  
   132  ## Create the network
   133  
   134  This tutorial will deploy a smart contract using the Fabric test network.
   135  The test network consists of two peer organizations and an ordering organization.
   136  The two peer organizations operate one peer each, while the ordering organization
   137  operates a single node raft ordering service. We will also use the test network
   138  to create a single channel named `mychannel` that both peer organizations will
   139  be members of.
   140  
   141  ![commercialpaper.network](./commercial_paper.diagram.testnet.png)
   142  *The Fabric test network is comprised of two peer organizations, Org1 and Org2,
   143  each with one peer and its ledger database, an ordering node. Each of these
   144  components runs as a Docker container.*
   145  
   146  The two peers, the peer [ledgers](../ledger/ledger.html#world-state-database-options), the
   147  orderer and the CA each run in the their own Docker container. In production
   148  environments, organizations typically use existing CAs that are shared with
   149  other systems; they're not dedicated to the Fabric network.
   150  
   151  The two organizations of the test network allow us to interact with a blockchain
   152  ledger as two organizations that operate separate peers. In this tutorial,
   153  we will operate Org1 of the test network as DigiBank and Org2 as MagnetoCorp.
   154  
   155  You can start the test network and create the channel with a script provided in
   156  the commercial paper directory. Change to the `commercial-paper` directory in
   157  the `fabric-samples`:
   158  ```
   159  cd fabric-samples/commercial-paper
   160  ```
   161  Then use the script to start the test network:
   162  ```
   163  ./network-starter.sh
   164  ```
   165  If the command is successful, you will see the test network being created in your
   166  logs. You can use the `docker ps` command to see the Fabric nodes running on your
   167  local machine:
   168  ```
   169  $ docker ps
   170  
   171  CONTAINER ID        IMAGE                               COMMAND                  CREATED             STATUS              PORTS                                        NAMES
   172  321cc489b10f        hyperledger/fabric-peer:latest      "peer node start"        2 minutes ago       Up 2 minutes        0.0.0.0:7051->7051/tcp                       peer0.org1.example.com
   173  ad668671f95f        hyperledger/fabric-peer:latest      "peer node start"        2 minutes ago       Up 2 minutes        7051/tcp, 0.0.0.0:9051->9051/tcp             peer0.org2.example.com
   174  caadbe4d8592        hyperledger/fabric-couchdb          "tini -- /docker-ent…"   2 minutes ago       Up 2 minutes        4369/tcp, 9100/tcp, 0.0.0.0:7984->5984/tcp   couchdb1
   175  ebabe52903b8        hyperledger/fabric-couchdb          "tini -- /docker-ent…"   2 minutes ago       Up 2 minutes        4369/tcp, 9100/tcp, 0.0.0.0:5984->5984/tcp   couchdb0
   176  7c72711c6e18        hyperledger/fabric-orderer:latest   "orderer"                2 minutes ago       Up 2 minutes        0.0.0.0:7050->7050/tcp                       orderer.example.com
   177  ```
   178  
   179  See if you can map these containers to the nodes of the test network (you may
   180  need to horizontally scroll to locate the information):
   181  
   182  * The Org1 peer, `peer0.org1.example.com`, is running in container `321cc489b10f`
   183  * The Org2 peer, `peer0.org2.example.com`, is running in container `ad668671f95f`
   184  * The CouchDB database for the Org1 peer, `couchdb0`, is running in container `ebabe52903b8`
   185  * The CouchDB database for the Org2 peer, `couchdb1`, is running in container `caadbe4d8592`
   186  * The ordering node `orderer.example.com` is running in container `7c72711c6e18`
   187  
   188  These containers all form a [Docker network](https://docs.docker.com/network/)
   189  called `net_test`. You can view the network with the `docker network` command:
   190  
   191  ```
   192  $ docker network inspect net_test
   193  
   194      {
   195            "Name": "net_test",
   196            "Id": "b77b99d29e37677fac48b7ecd78383bdebf09ebdd6b00e87e3d9444252b1ce31",
   197            "Created": "2020-01-30T23:04:39.6157465Z",
   198            "Containers": {
   199                "321cc489b10ff46554d0b215da307d38daf35b68bbea635ae0ae3176c3ae0945": {
   200                    "Name": "peer0.org1.example.com",
   201                    "IPv4Address": "192.168.224.5/20",
   202                },
   203                "7c72711c6e18caf7bff4cf78c27efc9ef3b2359a749c926c8aba1beacfdb0211": {
   204                    "Name": "orderer.example.com",
   205                    "IPv4Address": "192.168.224.4/20",
   206                },
   207                "ad668671f95f351f0119320198e1d1e19ebbb0d75766c6c8b9bb7bd36ba506af": {
   208                    "Name": "peer0.org2.example.com",
   209                    "IPv4Address": "192.168.224.6/20",
   210                },
   211                "caadbe4d8592aa558fe14d07a424a9e04365620ede1143b6ce5902ce038c0851": {
   212                    "Name": "couchdb1",
   213                    "IPv4Address": "192.168.224.2/20",
   214                },
   215                "ebabe52903b8597d016dbc0d0ca4373ef75162d3400efbe6416975abafd08a8f": {
   216                    "Name": "couchdb0",
   217                    "IPv4Address": "192.168.224.3/20",
   218                }
   219            },
   220            "Labels": {}
   221        }
   222    ]
   223  
   224  ```
   225  See how the five containers use different IP addresses, while being part of a
   226  single Docker network. (We've abbreviated the output for clarity.)
   227  
   228  Because we are operating the test network as DigiBank and MagnetoCorp,
   229  `peer0.org1.example.com` will belong to the DigiBank organization while
   230  `peer0.org2.example.com` will be operated by MagnetoCorp. Now that the test
   231  network is up and running, we can refer our network as PaperNet from this point
   232  forward.
   233  
   234  To recap: you've downloaded the Hyperledger Fabric samples repository from
   235  GitHub and you've got the test network running on your local machine. Let's now
   236  start to play the role of MagnetoCorp, who wish to trade commercial paper.
   237  
   238  ## Monitor the network as MagnetoCorp
   239  
   240  The commercial paper tutorial allows you to act as two organizations by
   241  providing two separate folders for DigiBank and MagnetoCorp. The two folders
   242  contain the smart contracts and application files for each organization. Because
   243  the two organizations have different roles in the trading of the commercial paper,
   244  the application files are different for each organization. Open a new window in
   245  the `fabric-samples` repository and use the following command to change into
   246  the MagnetoCorp directory:
   247  ```
   248  cd commercial-paper/organization/magnetocorp
   249  ```
   250  The first thing we are going to do as MagnetoCorp is monitor the components
   251  of PaperNet. An administrator can view the aggregated output from a set
   252  of Docker containers using the `logspout` [tool](https://github.com/gliderlabs/logspout#logspout).
   253  The tool collects the different output streams into one place, making it easy
   254  to see what's happening from a single window. This can be really helpful for
   255  administrators when installing smart contracts or for developers when invoking
   256  smart contracts, for example.
   257  
   258  In the MagnetoCorp directory, run the the following command to run the
   259  `monitordocker.sh`  script and start the `logspout` tool for the containers
   260  associated with PaperNet running on `net_test`:
   261  ```
   262  (magnetocorp admin)$ ./configuration/cli/monitordocker.sh net_test
   263  ...
   264  latest: Pulling from gliderlabs/logspout
   265  4fe2ade4980c: Pull complete
   266  decca452f519: Pull complete
   267  (...)
   268  Starting monitoring on all containers on the network net_test
   269  b7f3586e5d0233de5a454df369b8eadab0613886fc9877529587345fc01a3582
   270  ```
   271  
   272  Note that you can pass a port number to the above command if the default port in `monitordocker.sh` is already in use.
   273  ```
   274  (magnetocorp admin)$ ./monitordocker.sh net_test <port_number>
   275  ```
   276  
   277  This window will now show output from the Docker containers for the remainder of the
   278  tutorial, so go ahead and open another command window. The next thing we will do is
   279  examine the smart contract that MagnetoCorp will use to issue to the commercial
   280  paper.
   281  
   282  ## Examine the commercial paper smart contract
   283  
   284  `issue`, `buy` and `redeem` are the three functions at the heart of the commercial
   285  paper smart contract. It is used by applications to submit transactions which
   286  correspondingly issue, buy and redeem commercial paper on the ledger. Our next
   287  task is to examine this smart contract.
   288  
   289  Open a new terminal in the `fabric-samples` directory and change into the
   290  MagnetoCorp folder to act as the MagnetoCorp developer.
   291  ```
   292  cd commercial-paper/organization/magnetocorp
   293  ```
   294  You can then view the smart contract in the `contract` directory using your chosen
   295  editor (VS Code in this tutorial):
   296  ```
   297  (magnetocorp developer)$ code contract
   298  ```
   299  
   300  In the `lib` directory of the folder, you'll see `papercontract.js` file -- this
   301  contains the commercial paper smart contract!
   302  
   303  ![commercialpaper.vscode1](./commercial_paper.vscode.papercontract.png) *An
   304  example code editor displaying the commercial paper smart contract in `papercontract.js`*
   305  
   306  `papercontract.js` is a JavaScript program designed to run in the node.js
   307  environment. Note the following key program lines:
   308  
   309  * `const { Contract, Context } = require('fabric-contract-api');`
   310  
   311    This statement brings into scope two key Hyperledger Fabric classes that will
   312    be used extensively by the smart contract  -- `Contract` and `Context`. You
   313    can learn more about these classes in the
   314    [`fabric-shim` JSDOCS](https://hyperledger.github.io/fabric-chaincode-node/).
   315  
   316  
   317  * `class CommercialPaperContract extends Contract {`
   318  
   319    This defines the smart contract class `CommercialPaperContract` based on the
   320    built-in Fabric `Contract` class.  The methods which implement the key
   321    transactions to `issue`, `buy` and `redeem` commercial paper are defined
   322    within this class.
   323  
   324  
   325  * `async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime...) {`
   326  
   327    This method defines the commercial paper `issue` transaction for PaperNet. The
   328    parameters that are passed to this method will be used to create the new
   329    commercial paper.
   330  
   331    Locate and examine the `buy` and `redeem` transactions within the smart
   332    contract.
   333  
   334  
   335  * `let paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime...);`
   336  
   337    Within the `issue` transaction, this statement creates a new commercial paper
   338    in memory using the `CommercialPaper` class with the supplied transaction
   339    inputs. Examine the `buy` and `redeem` transactions to see how they similarly
   340    use this class.
   341  
   342  
   343  * `await ctx.paperList.addPaper(paper);`
   344  
   345    This statement adds the new commercial paper to the ledger using
   346    `ctx.paperList`, an instance of a `PaperList` class that was created when the
   347    smart contract context `CommercialPaperContext` was initialized. Again,
   348    examine the `buy` and `redeem` methods to see how they use this class.
   349  
   350  
   351  * `return paper;`
   352  
   353    This statement returns a binary buffer as response from the `issue`
   354    transaction for processing by the caller of the smart contract.
   355  
   356  
   357  Feel free to examine other files in the `contract` directory to understand how
   358  the smart contract works, and read in detail how `papercontract.js` is
   359  designed in the smart contract [topic](../developapps/smartcontract.html).
   360  
   361  ## Deploy the smart contract to the channel
   362  
   363  Before `papercontract` can be invoked by applications, it must be installed onto
   364  the appropriate peer nodes of the test network and then defined on the channel
   365  using the [Fabric chaincode lifecycle](../chaincode4noah.html#chaincode-lifecycle). The Fabric chaincode
   366  lifecycle allows multiple organizations to agree to the parameters of a chaincode
   367  before the chainocde is deployed to a channel. As a result, we need to install
   368  and approve the chaincode as administrators of both MagnetoCorp and DigiBank.
   369  
   370  ![commercialpaper.install](./commercial_paper.diagram.install.png)  *A MagnetoCorp
   371  administrator installs a copy of the `papercontract` onto a MagnetoCorp peer.*
   372  
   373  Smart contracts are the focus of application development, and are contained
   374  within a Hyperledger Fabric artifact called [chaincode](../chaincode.html). One
   375  or more smart contracts can be defined within a single chaincode, and installing
   376  a chaincode will allow them to be consumed by the different organizations in
   377  PaperNet. It means that only administrators need to worry about chaincode;
   378  everyone else can think in terms of smart contracts.
   379  
   380  ### Install and approve the smart contract as MagnetoCorp
   381  
   382  We will first install and approve the smart contract as the MagnetoCorp admin. Make
   383  sure that you are operating from the `magnetocorp` folder, or navigate back to that
   384  folder using the following command:
   385  ```
   386  cd commercial-paper/organization/magnetocorp
   387  ```
   388  
   389  A MagnetoCorp administrator can interact with PaperNet using the `peer` CLI. However,
   390  the administrator needs to set certain environment variables in their command
   391  window to use the correct set of `peer` binaries, send commands to the address
   392  of the MagnetoCorp peer, and sign requests with the correct crypto material.
   393  
   394  You can use a script provided by the sample to set the environment variables in
   395  your command window. Run the following command in the `magnetocorp` directory:
   396  ```
   397  source magnetocorp.sh
   398  ```
   399  
   400  You will see the full list of environment variables printed in your window. We
   401  can now use this command window to interact with PaperNet as the MagnetoCorp
   402  administrator.
   403  
   404  The first step is to install the `papercontract` smart contract. The smart
   405  contract can be packaged into a chaincode using the
   406  `peer lifecycle chaincode package` command. In the MagnetoCorp administrator's
   407  command window, run the following command to create the chaincode package:
   408  ```
   409  (magnetocorp admin)$ peer lifecycle chaincode package cp.tar.gz --lang node --path ./contract --label cp_0
   410  ```
   411  The MagnetoCorp admin can now install the chaincode on the MagnetoCorp peer using
   412  the `peer lifecycle chaincode install` command:
   413  ```
   414  (magnetocorp admin)$ peer lifecycle chaincode install cp.tar.gz
   415  ```
   416  If the command is successful, you will see messages similar to the following
   417  printed in your terminal:
   418  ```
   419  2020-01-30 18:32:33.762 EST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 001 Installed remotely: response:<status:200 payload:"\nEcp_0:ffda93e26b183e231b7e9d5051e1ee7ca47fbf24f00a8376ec54120b1a2a335c\022\004cp_0" >
   420  2020-01-30 18:32:33.762 EST [cli.lifecycle.chaincode] submitInstallProposal -> INFO 002 Chaincode code package identifier: cp_0:ffda93e26b183e231b7e9d5051e1ee7ca47fbf24f00a8376ec54120b1a2a335c
   421  ```
   422  Because the MagnetoCorp admin has set `CORE_PEER_ADDRESS=localhost:9051` to
   423  target its commands to `peer0.org2.example.com`, the `INFO 001 Installed remotely...`
   424  indicates that `papercontract` has been successfully installed on this peer.
   425  
   426  After we install the smart contract, we need to approve the chaincode definition
   427  for `papercontract` as MagnetoCorp. The first step is to find the packageID of
   428  the chaincode we installed on our peer. We can query the packageID using the
   429  `peer lifecycle chaincode queryinstalled` command:
   430  ```
   431  peer lifecycle chaincode queryinstalled
   432  ```
   433  
   434  The command will return the same package identifier as the install command. You
   435  should see output similar to the following:
   436  ```
   437  Installed chaincodes on peer:
   438  Package ID: cp_0:ffda93e26b183e231b7e9d5051e1ee7ca47fbf24f00a8376ec54120b1a2a335c, Label: cp_0
   439  ```
   440  
   441  We will need the package ID in the next step, so we will save it as an environment
   442  variable. The package ID may not be the same for all users, so you need to
   443  complete this step using the package ID returned from your command window.
   444  ```
   445  export PACKAGE_ID=cp_0:ffda93e26b183e231b7e9d5051e1ee7ca47fbf24f00a8376ec54120b1a2a335c
   446  ```
   447  
   448  The admin can now approve the chaincode definition for MagnetoCorp using the
   449  `peer lifecycle chaincode approveformyorg` command:
   450  ```
   451  (magnetocorp admin)$ peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name papercontract -v 0 --package-id $PACKAGE_ID --sequence 1 --tls --cafile $ORDERER_CA
   452  ```
   453  
   454  One of the most important chaincode parameters that channel members need to
   455  agree to using the chaincode definition is the chaincode [endorsement policy](../endorsement-policies.html).
   456  The endorsement policy describes the set of organizations that must endorse
   457  (execute and sign) a transaction before it can be determined to be valid. By
   458  approving the `papercontract` chaincode without the ``--policy`` flag, the
   459  MagnetoCorp admin agrees to using the default endorsement policy, which requires a
   460  majority of organizations on the channel to endorse a transaction. All transactions,
   461  whether valid or invalid, will be recorded on the [ledger blockchain](../ledger/ledger.html#blockchain),
   462  but only valid transactions will update the [world state](../ledger/ledger.html#world-state).
   463  
   464  ### Install and approve the smart contract as DigiBank
   465  
   466  By default, the Fabric Chaincode lifecycle requires a majority of organizations
   467  on the channel to successfully commit the chaincode definition to the channel.
   468  This implies that we need to approve the `papernet` chaincode as both MagnetoCorp
   469  and DigiBank to get the required majority of 2 out of 2. Open a new terminal
   470  window in the `fabric-samples` and navigate to the older that contains the
   471  DigiBank smart contract and application files:
   472  ```
   473  (digibank admin)$ cd commercial-paper/organization/digibank/
   474  ```
   475  Use the script in the DigiBank folder to set the environment variables that will
   476  allow you to act as the DigiBank admin:
   477  ```
   478  source digibank.sh
   479  ```
   480  
   481  We can now install and approve `papercontract` as the DigiBank. Run the following
   482  command to package the chaincode:
   483  ```
   484  (digibank admin)$ peer lifecycle chaincode package cp.tar.gz --lang node --path ./contract --label cp_0
   485  ```
   486  The admin can now install the chaincode on the DigiBank peer:
   487  ```
   488  (digibank admin)$ peer lifecycle chaincode install cp.tar.gz
   489  ```
   490  We then need to query and save the packageID of the chaincode that was just
   491  installed:
   492  ```
   493  (digibank admin)$ peer lifecycle chaincode queryinstalled
   494  ```
   495  Save the package ID as an environment variable. Complete this step using the
   496  package ID returned from your console.
   497  ```
   498  export PACKAGE_ID=cp_0:ffda93e26b183e231b7e9d5051e1ee7ca47fbf24f00a8376ec54120b1a2a335c
   499  ```
   500  
   501  The Digibank admin can now approve the chaincode definition of `papercontract`:
   502  ```
   503  (digibank admin)$ peer lifecycle chaincode approveformyorg --orderer localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name papercontract -v 0 --package-id $PACKAGE_ID --sequence 1 --tls --cafile $ORDERER_CA
   504  ```
   505  
   506  ### Commit the chaincode definition to the channel
   507  
   508  Now that DigiBank and MagnetoCorp have both approved the `papernet` chaincode, we
   509  have the majority we need (2 out of 2) to commit the chaincode definition to the
   510  channel. Once the chaincode is successfully defined on the channel, the
   511  `CommercialPaper` smart contract inside the `papercontract` chaincode can be
   512  invoked by client applications on the channel. Since either organization can
   513  commit the chaincode to the channel, we will continue operating as the
   514  DigiBank admin:
   515  
   516  ![commercialpaper.commit](./commercial_paper.diagram.commit.png)  *After the DigiBank administrator commits the definition of the `papercontract` chaincode to the channel, a new Docker chaincode container will be created to run `papercontract` on both PaperNet peers*
   517  
   518  The DigiBank administrator uses the `peer lifecycle chaincode commit` command
   519  to commit the chaincode definition of `papercontract` to `mychannel`:
   520  ```
   521  (magnetocorp admin)$ peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --peerAddresses localhost:7051 --tlsRootCertFiles ${PEER0_ORG1_CA} --peerAddresses localhost:9051 --tlsRootCertFiles ${PEER0_ORG2_CA} --channelID mychannel --name papercontract -v 0 --sequence 1 --tls --cafile $ORDERER_CA --waitForEvent
   522  ```
   523  The chaincode container will start after the chaincode definition has been
   524  committed to the channel. You can use the the `docker ps` command to see
   525  `papercontract` container starting on both peers.
   526  
   527  ```
   528  (magnetocorp admin)$ docker ps
   529  
   530  CONTAINER ID        IMAGE                                                                                                                                                               COMMAND                  CREATED             STATUS              PORTS                                        NAMES
   531  d4ba9dc9c55f        dev-peer0.org1.example.com-cp_0-ebef35e7f1f25eea1dcc6fcad5019477cd7f434c6a5dcaf4e81744e282903535-05cf67c20543ee1c24cf7dfe74abce99785374db15b3bc1de2da372700c25608   "docker-entrypoint.s…"   30 seconds ago      Up 28 seconds                                                    dev-peer0.org1.example.com-cp_0-ebef35e7f1f25eea1dcc6fcad5019477cd7f434c6a5dcaf4e81744e282903535
   532  a944c0f8b6d6        dev-peer0.org2.example.com-cp_0-1487670371e56d107b5e980ce7f66172c89251ab21d484c7f988c02912ddeaec-1a147b6fd2a8bd2ae12db824fad8d08a811c30cc70bc5b6bc49a2cbebc2e71ee   "docker-entrypoint.s…"   31 seconds ago      Up 28 seconds                                                    dev-peer0.org2.example.com-cp_0-1487670371e56d107b5e980ce7f66172c89251ab21d484c7f988c02912ddeaec
   533  ```
   534  
   535  Notice that the containers are named to indicate the peer that started it, and
   536  the fact that it's running `papercontract` version `0`.
   537  
   538  Now that we have deployed the `papercontract` chaincode to the channel, we can
   539  use the MagnetoCorp application to issue the commercial paper. Let's take a
   540  moment to examine the application structure.
   541  
   542  ## Application structure
   543  
   544  The smart contract contained in `papercontract` is called by MagnetoCorp's
   545  application `issue.js`. Isabella uses this application to submit a transaction
   546  to the ledger which issues commercial paper `00001`. Let's quickly examine how
   547  the `issue` application works.
   548  
   549  ![commercialpaper.application](./commercial_paper.diagram.8.png) *A gateway
   550  allows an application to focus on transaction generation, submission and
   551  response. It coordinates transaction proposal, ordering and notification
   552  processing between the different network components.*
   553  
   554  Because the `issue` application submits transactions on behalf of Isabella, it
   555  starts by retrieving Isabella's X.509 certificate from her
   556  [wallet](../developapps/wallet.html), which might be stored on the local file
   557  system or a Hardware Security Module
   558  [HSM](https://en.wikipedia.org/wiki/Hardware_security_module). The `issue`
   559  application is then able to utilize the gateway to submit transactions on the
   560  channel. The Hyperledger Fabric SDK provides a
   561  [gateway](../developapps/gateway.html) abstraction so that applications can
   562  focus on application logic while delegating network interaction to the
   563  gateway. Gateways and wallets make it straightforward to write Hyperledger
   564  Fabric applications.
   565  
   566  So let's examine the `issue` application that Isabella is going to use. Open a
   567  separate terminal window for her, and in `fabric-samples` locate the MagnetoCorp
   568  `/application` folder:
   569  
   570  ```
   571  (isabella)$ cd commercial-paper/organization/magnetocorp/application/
   572  (isabella)$ ls
   573  
   574  addToWallet.js		issue.js		package.json
   575  ```
   576  
   577  `addToWallet.js` is the program that Isabella is going to use to load her
   578  identity into her wallet, and `issue.js` will use this identity to create
   579  commercial paper `00001` on behalf of MagnetoCorp by invoking `papercontract`.
   580  
   581  Change to the directory that contains MagnetoCorp's copy of the application
   582  `issue.js`, and use your code editor to examine it:
   583  
   584  ```
   585  (isabella)$ cd commercial-paper/organization/magnetocorp/application
   586  (isabella)$ code issue.js
   587  ```
   588  
   589  Examine this directory; it contains the issue application and all its
   590  dependencies.
   591  
   592  ![commercialpaper.vscode2](./commercial_paper.vscode.issue.png) *A code editor
   593  displaying the contents of the commercial paper application directory.*
   594  
   595  Note the following key program lines in `issue.js`:
   596  
   597  * `const { Wallets, Gateway } = require('fabric-network');`
   598  
   599    This statement brings two key Hyperledger Fabric SDK classes into scope --
   600    `Wallet` and `Gateway`.M
   601  
   602  
   603  * `const wallet = await Wallets.newFileSystemWallet('../identity/user/isabella/wallet');`
   604  
   605    This statement identifies that the application will use `isabella` wallet when
   606    it connects to the blockchain network channel. Because Isabella's X.509 certificate
   607    is in the local file system, the application creates a new `FileSystemWallet`. The
   608    application will select a particular identity within `isabella` wallet.
   609  
   610  
   611  * `await gateway.connect(connectionProfile, connectionOptions);`
   612  
   613    This line of code connects to the network using the gateway identified by
   614    `connectionProfile`, using the identity referred to in `ConnectionOptions`.
   615  
   616    See how `../gateway/networkConnection.yaml` and `User1@org1.example.com` are
   617    used for these values respectively.
   618  
   619  
   620  * `const network = await gateway.getNetwork('mychannel');`
   621  
   622    This connects the application to the network channel `mychannel`, where the
   623    `papercontract` was previously instantiated.
   624  
   625  
   626  * `const contract = await network.getContract('papercontract');`
   627  
   628    This statement gives the application access to the `papercontract` chaincode.
   629    Once an application has issued getContract, it can submit to any smart contract
   630    transaction implemented within the chaincode.
   631  
   632  * `const issueResponse = await contract.submitTransaction('issue', 'MagnetoCorp', '00001', ...);`
   633  
   634    This line of code submits the a transaction to the network using the `issue`
   635    transaction defined within the smart contract. `MagnetoCorp`, `00001`... are
   636    the values to be used by the `issue` transaction to create a new commercial
   637    paper.
   638  
   639  * `let paper = CommercialPaper.fromBuffer(issueResponse);`
   640  
   641    This statement processes the response from the `issue` transaction. The
   642    response needs to deserialized from a buffer into `paper`, a `CommercialPaper`
   643    object which can interpreted correctly by the application.
   644  
   645  
   646  Feel free to examine other files in the `/application` directory to understand
   647  how `issue.js` works, and read in detail how it is implemented in the
   648  application [topic](../developapps/application.html).
   649  
   650  ## Application dependencies
   651  
   652  The `issue.js` application is written in JavaScript and designed to run in the
   653  node.js environment that acts as a client to the PaperNet network.
   654  As is common practice, MagnetoCorp's application is built on many
   655  external node packages --- to improve quality and speed of development. Consider
   656  how `issue.js` includes the `js-yaml`
   657  [package](https://www.npmjs.com/package/js-yaml) to process the YAML gateway
   658  connection profile, or the `fabric-network`
   659  [package](https://www.npmjs.com/package/fabric-network) to access the `Gateway`
   660  and `Wallet` classes:
   661  
   662  ```JavaScript
   663  const yaml = require('js-yaml');
   664  const { Wallets, Gateway } = require('fabric-network');
   665  ```
   666  
   667  These packages have to be downloaded from [npm](https://www.npmjs.com/) to the
   668  local file system using the `npm install` command. By convention, packages must
   669  be installed into an application-relative `/node_modules` directory for use at
   670  runtime.
   671  
   672  Examine the `package.json` file to see how `issue.js` identifies the packages to
   673  download and their exact versions:
   674  
   675  ```json
   676    "dependencies": {
   677      "fabric-network": "~1.4.0",
   678      "fabric-client": "~1.4.0",
   679      "js-yaml": "^3.12.0"
   680    },
   681  ```
   682  
   683  **npm** versioning is very powerful; you can read more about it
   684  [here](https://docs.npmjs.com/getting-started/semantic-versioning).
   685  
   686  Let's install these packages with the `npm install` command -- this may take up
   687  to a minute to complete:
   688  
   689  ```
   690  (isabella)$ cd commercial-paper/organization/magnetocorp/application/
   691  (isabella)$ npm install
   692  
   693  (           ) extract:lodash: sill extract ansi-styles@3.2.1
   694  (...)
   695  added 738 packages in 46.701s
   696  ```
   697  
   698  See how this command has updated the directory:
   699  
   700  ```
   701  (isabella)$ ls
   702  
   703  addToWallet.js		node_modules	      	package.json
   704  issue.js	      	package-lock.json
   705  ```
   706  
   707  Examine the `node_modules` directory to see the packages that have been
   708  installed. There are lots, because `js-yaml` and `fabric-network` are themselves
   709  built on other npm packages! Helpfully, the `package-lock.json`
   710  [file](https://docs.npmjs.com/files/package-lock.json) identifies the exact
   711  versions installed, which can prove invaluable if you want to exactly reproduce
   712  environments; to test, diagnose problems or deliver proven applications for
   713  example.
   714  
   715  ## Wallet
   716  
   717  Isabella is almost ready to run `issue.js` to issue MagnetoCorp commercial paper
   718  `00001`; there's just one remaining task to perform! As `issue.js` acts on
   719  behalf of Isabella, and therefore MagnetoCorp, it will use identity from her
   720  [wallet](../developapps/wallet.html) that reflects these facts. We now need to
   721  perform this one-time activity of adding appropriate X.509 credentials to her
   722  wallet.
   723  
   724  In Isabella's terminal window, run the `addToWallet.js` program to add identity
   725  information to her wallet:
   726  
   727  ```
   728  (isabella)$ node addToWallet.js
   729  
   730  done
   731  ```
   732  
   733  `addToWallet.js` is a simple file-copying program which you can examine at your
   734  leisure. It moves an identity from the test network sample to Isabella's
   735  wallet. Let's focus on the result of this program --- the contents of
   736  the wallet which will be used to submit transactions to PaperNet:
   737  
   738  ```
   739  (isabella)$ ls ../identity/user/isabella/wallet/
   740  
   741  isabella.id
   742  ```
   743  
   744  Isabella can store multiple identities in her wallet, though in our example, she
   745  only uses one. The `wallet` folder contains an `isabella.id` file that provides
   746  the information that Isabella needs to connect to the network. Other identities
   747  used by Isabella would have their own file. You can open this file to see the
   748  identity information that `issue.js` will use on behalf of Isabella inside a JSON
   749  file. The output has been formatted for clarity.
   750  ```
   751  (isabella)$  cat ../identity/user/isabella/wallet/*
   752  
   753  {
   754    "credentials": {
   755      "certificate": "-----BEGIN CERTIFICATE-----\nMIICKTCCAdCgAwIBAgIQWKwvLG+sqeO3LwwQK6avZDAKBggqhkjOPQQDAjBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMi5leGFtcGxlLmNvbTAeFw0yMDAyMDQxOTA5MDBaFw0zMDAyMDExOTA5MDBa\nMGwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMQ8wDQYDVQQLEwZjbGllbnQxHzAdBgNVBAMMFlVzZXIxQG9y\nZzIuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT4TnTblx0k\ngfqX+NN7F76Me33VTq3K2NUWZRreoJzq6bAuvdDR+iFvVPKXbdORnVvRSATcXsYl\nt20yU7n/53dbo00wSzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADArBgNV\nHSMEJDAigCDOCdm4irsZFU3D6Hak4+84QRg1N43iwg8w1V6DRhgLyDAKBggqhkjO\nPQQDAgNHADBEAiBhzKix1KJcbUy9ey5ulWHRUMbqdVCNHe/mRtUdaJagIgIgYpbZ\nXf0CSiTXIWOJIsswN4Jp+ZxkJfFVmXndqKqz+VM=\n-----END CERTIFICATE-----\n",
   756      "privateKey": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQggs55vQg2oXi8gNi8\nNidE8Fy5zenohArDq3FGJD8cKU2hRANCAAT4TnTblx0kgfqX+NN7F76Me33VTq3K\n2NUWZRreoJzq6bAuvdDR+iFvVPKXbdORnVvRSATcXsYlt20yU7n/53db\n-----END PRIVATE KEY-----\n"
   757    },
   758    "mspId": "Org2MSP",
   759    "type": "X.509",
   760    "version": 1
   761  }
   762  ```
   763  
   764  In the file you can notice the following:
   765  
   766  * a `"privateKey":` used to sign transactions on Isabella's behalf, but not
   767    distributed outside of her immediate control.
   768  
   769  * a `"certificate":` which contains Isabella's public key and other X.509
   770    attributes added by the Certificate Authority at certificate creation. This
   771    certificate is distributed to the network so that different actors at different
   772    times can cryptographically verify information created by Isabella's private key.
   773  
   774  You can Learn more about certificates [here](../identity/identity.html#digital-certificates). In practice, the
   775  certificate file also contains some Fabric-specific metadata such as
   776  Isabella's organization and role -- read more in the [wallet](../developapps/wallet.html) topic.
   777  
   778  ## Issue application
   779  
   780  Isabella can now use `issue.js` to submit a transaction that will issue
   781  MagnetoCorp commercial paper `00001`:
   782  
   783  ```
   784  (isabella)$ node issue.js
   785  
   786  Connect to Fabric gateway.
   787  Use network channel: mychannel.
   788  Use org.papernet.commercialpaper smart contract.
   789  Submit commercial paper issue transaction.
   790  Process issue transaction response.{"class":"org.papernet.commercialpaper","key":"\"MagnetoCorp\":\"00001\"","currentState":1,"issuer":"MagnetoCorp","paperNumber":"00001","issueDateTime":"2020-05-31","maturityDateTime":"2020-11-30","faceValue":"5000000","owner":"MagnetoCorp"}
   791  MagnetoCorp commercial paper : 00001 successfully issued for value 5000000
   792  Transaction complete.
   793  Disconnect from Fabric gateway.
   794  Issue program complete.
   795  ```
   796  
   797  The `node` command initializes a node.js environment, and runs `issue.js`. We
   798  can see from the program output that MagnetoCorp commercial paper 00001 was
   799  issued with a face value of 5M USD.
   800  
   801  As you've seen, to achieve this, the application invokes the `issue` transaction
   802  defined in the `CommercialPaper` smart contract within `papercontract.js`. This
   803  had been installed and instantiated in the network by the MagnetoCorp
   804  administrator. It's the smart contract which interacts with the ledger via the
   805  Fabric APIs, most notably `putState()` and `getState()`, to represent the new
   806  commercial paper as a vector state within the world state. We'll see how this
   807  vector state is subsequently manipulated by the `buy` and `redeem` transactions
   808  also defined within the smart contract.
   809  
   810  All the time, the underlying Fabric SDK handles the transaction endorsement,
   811  ordering and notification process, making the application's logic
   812  straightforward; the SDK uses a [gateway](../developapps/gateway.html) to
   813  abstract away network details and
   814  [connectionOptions](../developapps/connectoptions.html) to declare more advanced
   815  processing strategies such as transaction retry.
   816  
   817  Let's now follow the lifecycle of MagnetoCorp 00001 by switching our emphasis
   818  to an employee of DigiBank, Balaji, who will buy the commercial paper using a
   819  DigiBank application.
   820  
   821  ## Digibank applications
   822  
   823  Balaji uses DigiBank's `buy` application to submit a transaction to the ledger
   824  which transfers ownership of commercial paper `00001` from MagnetoCorp to
   825  DigiBank. The `CommercialPaper` smart contract is the same as that used by
   826  MagnetoCorp's application, however the transaction is different this time --
   827  it's `buy` rather than `issue`. Let's examine how DigiBank's application works.
   828  
   829  Open a separate terminal window for Balaji. In `fabric-samples`, change to the
   830  DigiBank application directory that contains the application, `buy.js`, and open
   831  it with your editor:
   832  
   833  ```
   834  (balaji)$ cd commercial-paper/organization/digibank/application/
   835  (balaji)$ code buy.js
   836  ```
   837  
   838  As you can see, this directory contains both the `buy` and `redeem` applications
   839  that will be used by Balaji.
   840  
   841  
   842  ![commercialpaper.vscode3](./commercial_paper.diagram.12.png) *DigiBank's
   843  commercial paper directory containing the `buy.js` and `redeem.js`
   844  applications.*
   845  
   846  DigiBank's `buy.js` application is very similar in structure to MagnetoCorp's
   847  `issue.js` with two important differences:
   848  
   849  
   850    * **Identity**: the user is a DigiBank user `Balaji` rather than MagnetoCorp's
   851      `Isabella`
   852  
   853      ```JavaScript
   854      const wallet = await Wallets.newFileSystemWallet('../identity/user/balaji/wallet');
   855      ```
   856  
   857      See how the application uses the `balaji` wallet when it connects to the
   858      PaperNet network channel. `buy.js` selects a particular identity within
   859      `balaji` wallet.
   860  
   861  
   862    * **Transaction**: the invoked transaction is `buy` rather than `issue`
   863  
   864      ```JavaScript
   865      const buyResponse = await contract.submitTransaction('buy', 'MagnetoCorp', '00001', ...);
   866      ```
   867  
   868      A `buy` transaction is submitted with the values `MagnetoCorp`, `00001`, ...,
   869      that are used by the `CommercialPaper` smart contract class to transfer
   870      ownership of commercial paper `00001` to DigiBank.
   871  
   872  Feel free to examine other files in the `application` directory to understand
   873  how the application works, and read in detail how `buy.js` is implemented in
   874  the application [topic](../developapps/application.html).
   875  
   876  ## Run as DigiBank
   877  
   878  The DigiBank applications which buy and redeem commercial paper have a very
   879  similar structure to MagnetoCorp's issue application. Therefore, let’s install
   880  their dependencies and set up Balaji's wallet so that he can use these
   881  applications to buy and redeem commercial paper.
   882  
   883  Like MagnetoCorp, Digibank must the install the required application packages
   884  using the ``npm install`` command, and again, this make take a short time to
   885  complete.
   886  
   887  In the DigiBank administrator window, install the application dependencies:
   888  
   889  ```
   890  (digibank admin)$ cd commercial-paper/organization/digibank/application/
   891  (digibank admin)$ npm install
   892  
   893  (            ) extract:lodash: sill extract ansi-styles@3.2.1
   894  (...)
   895  added 738 packages in 46.701s
   896  ```
   897  
   898  In Balaji's command window, run the `addToWallet.js` program to add the identity
   899  to his wallet:
   900  
   901  ```
   902  (balaji)$ node addToWallet.js
   903  
   904  done
   905  ```
   906  
   907  The `addToWallet.js` program has added identity information for `balaji`, to his
   908  wallet, which will be used by `buy.js` and `redeem.js` to submit transactions to
   909  `PaperNet`.
   910  
   911  Like Isabella, Balaji can store multiple identities in his wallet, though in our
   912  example, he only uses one. His corresponding id file at
   913  `digibank/identity/user/balaji/wallet/balaji.id` is very similar Isabella's ---
   914  feel free to examine it.
   915  
   916  ## Buy application
   917  
   918  Balaji can now use `buy.js` to submit a transaction that will transfer ownership
   919  of MagnetoCorp commercial paper 00001 to DigiBank.
   920  
   921  Run the `buy` application in Balaji's window:
   922  
   923  ```
   924  (balaji)$ node buy.js
   925  
   926  Connect to Fabric gateway.
   927  Use network channel: mychannel.
   928  Use org.papernet.commercialpaper smart contract.
   929  Submit commercial paper buy transaction.
   930  Process buy transaction response.
   931  MagnetoCorp commercial paper : 00001 successfully purchased by DigiBank
   932  Transaction complete.
   933  Disconnect from Fabric gateway.
   934  Buy program complete.
   935  ```
   936  
   937  You can see the program output that MagnetoCorp commercial paper 00001 was
   938  successfully purchased by Balaji on behalf of DigiBank. `buy.js` invoked the
   939  `buy` transaction defined in the `CommercialPaper` smart contract which updated
   940  commercial paper `00001` within the world state using the `putState()` and
   941  `getState()` Fabric APIs. As you've seen, the application logic to buy and issue
   942  commercial paper is very similar, as is the smart contract logic.
   943  
   944  ## Redeem application
   945  
   946  The final transaction in the lifecycle of commercial paper 00001 is for
   947  DigiBank to redeem it with MagnetoCorp. Balaji uses `redeem.js` to submit a
   948  transaction to perform the redeem logic within the smart contract.
   949  
   950  Run the `redeem` transaction in Balaji's window:
   951  
   952  ```
   953  (balaji)$ node redeem.js
   954  
   955  Connect to Fabric gateway.
   956  Use network channel: mychannel.
   957  Use org.papernet.commercialpaper smart contract.
   958  Submit commercial paper redeem transaction.
   959  Process redeem transaction response.
   960  MagnetoCorp commercial paper : 00001 successfully redeemed with MagnetoCorp
   961  Transaction complete.
   962  Disconnect from Fabric gateway.
   963  Redeem program complete.
   964  ```
   965  
   966  Again, see how the commercial paper 00001 was successfully redeemed when
   967  `redeem.js` invoked the `redeem` transaction defined in `CommercialPaper`.
   968  Again, it updated commercial paper `00001` within the world state to reflect
   969  that the ownership returned to MagnetoCorp, the issuer of the paper.
   970  
   971  ## Clean up
   972  
   973  When you are finished using the Commercial Paper tutorial, you can use a script
   974  to clean up your environment. Use a command window to navigate back to the root
   975  directory of the commercial paper sample:
   976  ```
   977  cd fabric-samples/commercial-paper
   978  ```
   979  You can then bring down the network with the following command:
   980  ```
   981  ./network-clean.sh
   982  ```
   983  This command will bring down the peers, CouchDB containers, and ordering node of the network, in addition to the logspout tool. It will also remove the identities that we created for Isabella and Balaji. Note that all of the data on the ledger will be lost. If you want to go through the tutorial again, you will start from a clean initial state.
   984  
   985  ## Further reading
   986  
   987  To understand how applications and smart contracts shown in this tutorial work
   988  in more detail, you'll find it helpful to read
   989  [Developing Applications](../developapps/developing_applications.html). This
   990  topic will give you a fuller explanation of the commercial paper scenario, the
   991  PaperNet business network, its actors, and how the applications and smart
   992  contracts they use work in detail.
   993  
   994  Also feel free to use this sample to start creating your own applications and
   995  smart contracts!
   996  
   997  <!--- Licensed under Creative Commons Attribution 4.0 International License
   998  https://creativecommons.org/licenses/by/4.0/ -->