Hyperledger Composer basics, Part 3

Deploy locally, interact with, and extend your blockchain network

Exploring more Hyperledger Composer tools, UIs, and security

Content series:

This content is part # of # in the series: Hyperledger Composer basics, Part 3

Stay tuned for additional content in this series.

This content is part of the series:Hyperledger Composer basics, Part 3

Stay tuned for additional content in this series.

This tutorial builds on Part 1, where you learned how to model and test a
simple business network in a local version of the Hyperledger Composer
Playground. In Part 2, you saw how to model an IoT GPS sensor in
a shipping container by adding GPS readings to the Shipment asset, and
modify the chaincode to send an alert when the Shipment reaches its
destination port. Then you deployed the sample Perishable Goods network to the IBM Cloud in the Composer Online
Playground.

Now, in this third and final part, you’ll install Hyperledger Fabric on
your computer, and deploy the business network archive (BNA) to an
instance of Hyperledger Fabric running on your machine (referred to as
your local Hyperledger Fabric). You’ll install more tools and
generate a Loopback-based REST interface that you can use to interact with
the sample network blockchain application.

Part 3 also includes a detailed discussion of Hyperledger Composer security
concepts. This part concludes with steps to pull it all together and
extend the iot-perishable-network to create a more “real
world” version of the Perishable Goods network.

Prerequisites

I’ll assume that you’ve worked through Part 2, and that you have the following software
installed on your computer:

Deploy the network to your local Hyperledger
Fabric

Hyperledger Fabric is a framework for building blockchain applications for
business purposes, and as you have already learned, Hyperledger Composer
is a companion tool that makes building blockchain applications that run
on Hyperledger Fabric easier. Up to now, you have used Composer Playground
in “browser-only” mode (in Part 1) and the Composer Online playground in
the IBM Cloud (Part 2).

Now, you’ll install and run the Hyperledger Fabric on your computer, and
use the Composer Command Line Interface (CLI) to interact with it.

First, create a directory on your computer where you want to do your local
Hyperledger Composer development. For this tutorial, I’ll refer to this
location as $COMPOSER_ROOT in code, and in prose, I’ll refer
to it as your Composer root directory. This directory can be
anywhere you want, but I recommend you create it just off your home
folder, and always set the COMPOSER_ROOT environment variable
to that directory in the current shell, since that’s how I’ll refer to
that location throughout this tutorial.

Now download the fabric-dev-servers.zip distribution file to
your Composer root directory using the curl command, and
unzip it. The sequence of commands looks like this (assuming
~/HyperledgerComposer as the root directory for this example):

                mkdir ~/HyperledgerComposer
                export COMPOSER_ROOT=~/HyperledgerComposer
                cd $COMPOSER_ROOT
                mkdir fabric-dev-servers && cd fabric-dev-servers
                curl -O http://bit.ly/2AWYSFl
                unzip fabric-dev-servers.zip

Note: before you continue, make sure Docker is
running.

The first time you run Hyperledger Fabric, execute this sequence of
commands:

                cd $COMPOSER_ROOT/fabric-dev-servers
                ./downloadFabric.sh
                ./startFabric.sh
                ./createPeerAdminCard.sh

It will take several minutes to run the first command, which pulls all of
the necessary Docker images. The second command starts the local
Hyperledger Fabric. The last command issues an ID card for the
fabric admin, which is PeerAdmin. Don’t worry about that for
now; I’ll cover ID cards later in the tutorial. The output of the
createPeerAdminCard.sh script looks like this:

                $ ./createPeerAdminCard.sh 
                Development only script for Hyperledger Fabric control
                Running 'createPeerAdminCard.sh'
                FABRIC_VERSION is unset, assuming hlfv1
                FABRIC_START_TIMEOUT is unset, assuming 15 (seconds)
                
                Using composer-cli at v0.15.0
                Successfully created business network card to /tmp/PeerAdmin@hlfv1.card
                
                Command succeeded
                
                Successfully imported business network card: PeerAdmin@hlfv1
                
                Command succeeded
                
                Hyperledger Composer PeerAdmin card has been imported
                The following Business Network Cards are available:
                
                
                ┌─────────────────┬───────────┬─────────┐
                │ CardName        │ UserId    │ Network │
                ├─────────────────┼───────────┼─────────┤
                │ PeerAdmin@hlfv1 │ PeerAdmin │         │
                └─────────────────┴───────────┴─────────┘
                
                Issue composer card list --name <CardName>  to get details of the card
                
                Command succeeded

Make a note of the card name, because you will need it to execute all of
the CLI commands in this tutorial.

You only need to run the createPeerAdminCard.sh script one
time. From now on, whenever you start Hyperledger Fabric, you just run the
startFabric.sh script like this:

                cd $COMPOSER_ROOT/fabric-dev-servers
                ./startFabric.sh

When the script finishes, Hyperledger Fabric is running on your computer.
To shut down the local Hyperledger Fabric, run the
stopFabric.sh script.

For the purposes of the tutorial, I suggest you leave Hyperledger Fabric up
for now.

Deploy to Hyperledger Fabric

In Part 2, you cloned the developerWorks project from GitHub
and modified the Perishable Goods network. I’ve provided a finished
version of that network in the
developerWorks/iot-perishable-network directory. You’ll use
that network for the rest of this tutorial.

To deploy the iot-perishable-network to your local Fabric, use
the Composer CLI and execute this sequence of commands:

                cd $COMPOSER_ROOT/developerWorks/iot-perishable-network
                composer network deploy -a dist/iot-perishable-network.bna -A admin -S adminpw -c PeerAdmin@hlfv1 -f networkadmin.card
                composer card import --file networkadmin.card

The composer network deploy command deploys the specified
network archive (dist/iot-perishable-network.bna) to the
local Hyperledger Fabric, using the PeerAdmin card you created earlier
when you ran the createPeerAdminCard.sh script to
authenticate. When the deployment is finished, an ID card is
issued for the network administrator, whose credentials
— that is, userid and password (or secret) — are stored in
the networkadmin.card file.

The output looks like this:

                $ composer network deploy -a dist/iot-perishable-network.bna -A admin -S adminpw -c PeerAdmin@hlfv1 -f networkadmin.card
                Deploying business network from archive: dist/iot-perishable-network.bna
                Business network definition:
                	Identifier: iot-perishable-network@0.1.12
                	Description: Shipping Perishable Goods Business Network
                
                ✔ Deploying business network definition. This may take a minute...
                Successfully created business network card to networkadmin.card
                
                Command succeeded

The composer card import command tells Hyperledger Composer to
import the specified card file so it can be used later to authenticate the
user whose credentials are stored in the card. In this case, the card is
for the network admin. I’ll talk more about cards later on. The output
looks like this:

                $ composer card import --file networkadmin.card 
                Successfully imported business network card: admin@iot-perishable-network
                
                Command succeeded

Interact with the network via the
Composer REST interface

You’ve deployed the iot-perishable-network to the local
Hyperledger Fabric, but how do you see what’s there? How do you interact
with it? Hyperledger Composer provides a tool called
composer-rest-server that generates a Loopback-based REST
interface to access your network.

Install the Composer REST interface
generator

First, you need to install the composer-rest-server loopback
generator. Go to a command line (Ubuntu) or open a Terminal window (MacOS)
and enter this command:

npm install -g composer-rest-server

When the installation finishes, verify that
composer-rest-server was installed correctly by running
composer-rest-server -v:

                $ composer-rest-server -v
                v0.15.0

Generate the REST interface

Make sure the Hyperledger Fabric is running and the
iot-perishable-network is deployed before generating the REST
interface (or there will be nothing to connect to). From the command line,
run the composer-rest-server command. It will prompt you for
input. You’ll see something like this:

Listing 1. Starting the REST server
                $ composer-rest-server
                ? Enter the name of the business network card to use: admin@iot-perishable-network
                ? Specify if you want namespaces in the generated REST API: always use namespaces
                ? Specify if you want to enable authentication for the REST API using Passport: No
                ? Specify if you want to enable event publication over WebSockets: No
                ? Specify if you want to enable TLS security for the REST API: No
                
                To restart the REST server using the same options, issue the following command:
                   composer-rest-server -c admin@iot-perishable-network -n always
                
                Discovering types from business network definition ...
                Discovered types from business network definition
                Generating schemas for all types in business network definition ...
                Generated schemas for all types in business network definition
                Adding schemas for all types to Loopback ...
                Added schemas for all types to Loopback
                Web server listening at: http://localhost:3000
                Browse your REST API at http://localhost:3000/explorer

The REST server needs to know how to authenticate with Hyperledger Fabric
so it can communicate with the business network. You provide an ID card
for that (line 2) such as the admin@iot-perishable-network ID
card from earlier in the tutorial.

Namespaces help avoid name collisions. In the
iot-perishable-network, this is not a big deal since there’s
only a handful of assets, participants, and transactions. I think it’s a
good idea to always use namespaces (line 3). Testing real-world networks
with lots of participants, assets, and transactions is hard enough; using
namespaces will help you avoid headaches related to name collisions.

When using the REST interface for testing, don’t worry about authentication (line 4), events and WebSockets (line 5), or TLS security (line 6) for now. However, if you decide to deploy
the REST server as part of your production blockchain solution, make sure
to check out the links here or in the Related
topics
at the end of this tutorial.

The next time you want to start the REST server with those same options,
just use the command shown (line 9) and skip the interview!

To use the REST interface, open a browser and point it to the address shown
on line 18 in Listing 1. You’ll see something like
this:

Figure 1. The REST interface

The REST interface

The interface is fairly intuitive. To work with an object, click it, and
when it expands, you’ll see the REST methods you can invoke (for example,
/GET, /POST, and so on). To invoke the
SetupDemo transaction (which instantiates the business
model), click the line that contains SetupDemo, which then
expands to the POST method. Click the Try it
out!
button to invoke the transaction. If it succeeds, you’ll
see an HTTP 200 response code. Then you can navigate through the model and
see the various objects like the Contract, shown in Figure 2.

Figure 2. The REST interface showing the Contract
object

The REST interface showing the Contract object

In the video I’ll show you how to use the REST
interface in detail, so be sure to check that out.

Secure the REST interface

This is beyond the scope of this tutorial, but if you plan to run the REST
interface in production, you need a strategy to deal with it. An entire
tutorial could be done on this topic alone!
Fortunately there are resources on the Hyperledger Composer website to
help. Here are some links to the Composer docs:

For your convenience, these links are also listed in the Related topics at the end of the tutorial
as well.

Video: Using the Composer REST interface

Set up Hyperledger Composer
security

There are two levels of security with Hyperledger Composer:

  • Hyperledger Fabric administrator
  • Business network administrator

The administrator for your local Hyperledger Fabric is the Peer
Administrator (or PeerAdmin for short), which you created when you
installed the local Hyperledger Fabric. Every business network should have
an administrator as well, which is created when the network is deployed by
the Hyperledger Fabric administrator. Authentication for both is handled
using ID Cards, which you’ll learn about next.

An ID card (or card for short) is a collection of files that
contains all the information necessary to allow a participant to connect
to a business network. The card is referred to as an identity.
Before it can be used, it must be issued to the user, allowing
him or her to be authenticated and authorized to use the network. Cards
are a very handy way of securing access to a Hyperledger Fabric network.
Rather than keeping up with a password (called a secret in
Hyperledger Composer terminology), you import the card into a collection
of cards in the Hyperledger Fabric called a wallet. From that
point on, you can just reference the card to authenticate that
identity.

The general form for specifying a card is: userid@network,
where userid is the user’s unique id, and the
network is the network to which the user is
authenticated.

To handle the two levels of Hyperledger Composer security, you need a card
for at least: (1) the PeerAdmin and (2) the business network admin.

PeerAdmin

The PeerAdmin card is a special ID card used to administer the
local Hyperledger Fabric. In a development installation, such as the one
on your computer, the PeerAdmin ID card is created when you install the
local Hyperledger Fabric.

The form for a PeerAdmin card for a Hyperledger Fabric v1.0
network is PeerAdmin@hlfv1. You already used this card
earlier in the tutorial when you deployed the
iot-perishable-network to your local Hyperledger Fabric.

In general, the PeerAdmin is a special role reserved for functions such
as:

  • Deploying business networks
  • Creating, issuing, and revoking ID cards for business network
    admins

As a developer, in a production Hyperledger Fabric installation, you would
not have access to the PeerAdmin card. Instead the
Hyperledger Fabric administrator would deploy your business network,
create ID cards, and so on. When developing and testing blockchain
networks using your local Hyperledger Fabric, you will use the PeerAdmin
ID card to perform these functions.

Business network admin

When the PeerAdmin deploys your network to the Hyperledger Fabric, an ID
card is issued to the business network administrator, and then this card
is used whenever the business network administrator needs to do anything
with the business network, such as using the Composer Command Line
Interface (which you’ll use shortly).

Remember the admin@iot-perishable-network card from earlier?
That was the business network admin card issued by the
PeerAdmin (that is, the local Hyperledger Fabric
administrator) when the iot-perishable-network was
deployed.

In general, the business network admin is a special role reserved for
functions such as:

  • Updating the running business network
  • Querying the various registries (participant, identity, and so
    forth)
  • Creating, issuing, and revoking ID cards for participants in the
    business network

That’s right, the admin ID card can also be used to issue other ID cards
for specific participants (I’ll show you how to do that later in the
tutorial), so that all participants have their own ID cards. Access to the
network can be controlled by these cards.

Speaking of access control, Composer implements the concept of role-based
security through permissions baked right into its architecture to handle
both authentication and authorization. A participant’s access to resources
is controlled based on the identity that has been issued to that
participant.

In this section, you’ll see how to set up access control rules to lock down
resources in your network by participant through the Access Control List
(ACL) file called permissions.acl (at the time of this
writing, the file must have this name). Here are the
highlights:

  • Access is applied to either grant or deny
    based on resources that match the rule. By default, if a
    resource matches no rule, then access to it is denied.
  • The file is processed top-down so that the first rule that either
    grants or denies access to a particular resource is in effect and
    cannot be overridden by a subsequent rule.
  • The format of a rule is fairly intuitive. The rule
    keyword indicates the start of a rule, followed by a rule name, which
    must be unique.
  • The rule consists of a set of name/value pairs that define the rule’s
    properties.

Listing 2 shows the permissions.acl file from the
iot-perishable-network business network.

Listing 2. permissions.acl from the
iot-perishable-network
                /**
                 * Sample access control list.
                 */
                rule Default {
                    description: "Allow all participants access to all resources"
                    participant: "ANY"
                    operation: ALL
                    resource: "org.acme.shipping.perishable.*"
                    action: ALLOW
                }
                
                rule SystemACL {
                  description:  "System ACL to permit all access"
                  participant: "org.hyperledger.composer.system.Participant"
                  operation: ALL
                  resource: "org.hyperledger.composer.system.**"
                  action: ALLOW
                }
                
                rule NetworkAdminUser {
                    description: "Grant business network administrators full access to user resources"
                    participant: "org.hyperledger.composer.system.NetworkAdmin"
                    operation: ALL
                    resource: "**"
                    action: ALLOW
                }
                
                rule NetworkAdminSystem {
                    description: "Grant business network administrators full access to system resources"
                    participant: "org.hyperledger.composer.system.NetworkAdmin"
                    operation: ALL
                    resource: "org.hyperledger.composer.system.**"
                    action: ALLOW
                }

The general format for each property is
property:
"MATCH_EXPRESSION"
. The properties are:

description— a human-readable name for the rule in double quotes. Example:
description: "This is a description"

participant— fully qualified name of the Participant to which access is
granted or denied, surrounded by double quotes. Multiple participants may
be specified in the MATCH_EXPRESSION through the use of the
single asterisk (*) wildcard to indicate “all”, double
asterisk (**) to indicate recursion within a namespace, or
ANY, which matches all participants in all namespaces.

Examples:

  • participant: "org.acme.shipping.perishable.Grower"— Apply the rule to
    org.acme.shipping.perishable.Grower only.
  • participant: "org.acme.shipping.perishable.*"— Apply the rule to all participants in the
    org.acme.shipping.perishable namespace.
  • participant: "org.acme.shipping.**"— Apply the rule to all participants in the
    org.acme.shipping namespace, and recursively any
    namespaces beneath it.
  • participant: "ANY"— Apply the rule to all participants in all namespaces.

operation— the MATCH_EXPRESSION may be one or more of
CREATE, READ, UPDATE,
DELETE, or ALL. Multiple values may be separated
by commas (for example, CREATE,READ will grant both
CREATE and READ access to the resource). Use
ALL by itself to indicate the rule applies to all
operations.

Examples:

  • operation: ALL— Apply the rule to all CRUD operations.
  • operation: CREATE— Apply the rule only to the CREATE operation
  • operation: READ,UPDATE— Apply the rule to READ and UPDATE
    operations.

resource— defines the “thing” to which the rule applies. A resource can be
any class (that is, an asset, participant, or transaction) from the
business model. Through the use of wildcards, multiple classes can also be
specified, in the same way as for participant above.

Examples:

  • resource: "org.acme.shipping.perishable.TemperatureReading"— The rule applies only to the
    org.acme.shipping.perishable.TemperatureReading
    transaction.
  • resource: "org.acme.shipping.perishable.*"— The rule applies to all classes in the
    org.acme.shipping.perishable namespace.
  • resource: "org.acme.shipping.**"— Apply the rule to all resources in the
    org.acme.shipping namespace, and recursively to any
    namespaces beneath it.

action— the action that applies when the rule fires. One of:
ALLOW to grant access, or DENY to deny the
specified participant(s) access to the resource(s).

Examples:

  • action: ALLOW— Allow access to the specified resource(s).
  • action: DENY— Deny access to the specified resource(s).

Note: If your network has no permissions.acl, then access is
granted to all participants (that is, access is wide open — there
is no resource-level security).

You will see how to use rules later in the tutorial. Also, the Composer ACL documentation has lots of examples, so be sure to
check that out.

Issue a new ID – Playground

ID cards can be kind of abstract, and difficult to visualize. So let’s look
at one in Playground (in fact, you already have, you just might not have
realized it).

Start the Hyperledger Composer Playground and import the
iot-perishable-network (don’t forget to create the BNA by
running npm install if you haven’t already). You imported a
network in Part 2, so check that out if you need a refresher.

Make sure to invoke the SetupDemo transaction to create the
participants and store them in the participant registry. Why? Because you
cannot issue an ID card for a participant that is not in the participant
registry. In order to work with the classes from the business model, you
must instantiate the model, and that’s what
SetupDemo does for you. Please review the “Test the business
network” section from Part 1 of this tutorial series if you want more information on
working with business models.

Once the model is instantiated, in the upper right corner where you see
admin, click to expand the drop-down and select
ID Registry. On the next screen, choose Issue
new ID
and then enter grower1 as the ID
Name, and select farmer@email.com from the drop-down. See
Figure 3.

Figure 3. Issue a new ID card in Playground

Issue a new ID Card in Playground

Click the Create New button to issue the ID. Once the
grower1 ID card has been issued, it will show up in
the ID registry. See Figure 4.

Figure 4. New grower1 ID card in Playground

New grower1 ID card in Playground

I’ll show you how to issue ID cards using the Composer CLI later in the
tutorial.

Interact with the network via the Composer CLI

I showed you how to install the Composer Command Line Interface (CLI) in
Part 2, and you used it earlier in this tutorial to deploy the
iot-perishable-network and import the
admin@iot-perishable-network card. Now you will use the CLI
to interact with the iot-perishable-network business network.
The CLI is really handy because it lends itself to use in scripts, and
it’s easy to use, as you’ll see. To use the CLI, you’ll need to go to the
command line (Ubuntu) or open a terminal window (MacOS).

Ping the network

Once you’ve deployed the iot-perishable-network network, you
can send it a ping, which doesn’t have any side effects. It’s a
safe way to just see if your network is up and running. Enter this
command:

composer network ping --card admin@iot-perishable-network

If the ping subcommand succeeds, you will see output like
this:

                $ composer network ping --card admin@iot-perishable-network
                The connection to the network was successfully tested: iot-perishable-network
                	version: 0.15.0
                	participant: org.hyperledger.composer.system.NetworkAdmin#admin
                
                Command succeeded

Invoke the SetupDemo transaction

The SetupDemo transaction is used to instantiate the model.
Enter this command:

                composer transaction submit --card admin@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.SetupDemo"}'

The output looks like this:

                $ composer transaction submit --card admin@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.SetupDemo"}'
                Transaction Submitted.
                
                Command succeeded

Invoke the TemperatureReading transaction

The TemperatureReading transaction is invoked by the IoT
sensor in the shipping container each time a temperature reading is taken
and needs to be recorded in the blockchain. Enter this command:

                composer transaction submit --card admin@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 0, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'

The output looks like this:

                $ composer transaction submit --card admin@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 0, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Invoke the ShipmentReceived transaction

The ShipmentReceived transaction is invoked by the Importer
when the shipment is received. Enter this command:

                composer transaction submit --card admin@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.ShipmentReceived", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'

The output looks like this:

                $ composer transaction submit --card admin@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.ShipmentReceived", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Update the network

This is handy whenever you make a change during development and you need to
update the deployed network. After you run npm install to
rebuild the BNA, enter this command:

                composer network update -a dist/iot-perishable-network.bna --card admin@iot-perishable-network

The output looks like this:

                $ composer network update -a dist/iot-perishable-network.bna --card admin@iot-perishable-network
                Deploying business network from archive: dist/iot-perishable-network.bna
                Business network definition:
                	Identifier: iot-perishable-network@0.1.12
                	Description: Shipping Perishable Goods Business Network
                
                ✔ Updating business network definition. This may take a few seconds...
                Successfully created business network card to undefined
                
                Command succeeded

Be sure to check out the CLI documentation for a complete reference of commands, examples,
and lots more.

Everything you’ve learned so far has been building to this point: You can
now modify the iot-perishable-network into a more realistic
blockchain application. Here’s what you will do in this section:

  • Make changes to the network model:
    • Add new participants to represent the IoT sensors in the
      shipping container
    • Add new transactions that more realistically model a shipping
      workflow
    • Add new events that are broadcast when certain points in the
      workflow are reached
  • Add chaincode for the new transactions
  • Modify the network’s permissions
  • Add Cucumber feature tests to unit test your changes
  • Build the network and run the unit tests
  • Deploy the network to your local Hyperledger Fabric
  • Issue IDs for the Grower, Temperature and GPS sensors, Shipper, and
    Importer
  • Run transactions through the CLI

When you’re finished modifying the iot-perishable-network, you
will have learned the basic skills needed to use Hyperledger Composer, you
will be ready to tackle a real-world blockchain application project, you
will be the envy of all your friends, and tales of your Hyperledger
Composer and Fabric prowess will spread far and wide (okay, I may be
embellishing a bit with those last two).

If you get stuck at any point and need a little help, be sure to check out
the solution code, which is in a project called
iot-perishable-network-advanced that you have already cloned
to your computer when you cloned the developerWorks project from GitHub.

Note: You are welcome to use whatever editor you’re comfortable with, but I
will use VSCode (which you installed in Part 2) for all the changes in
this section, and the instructions below will reflect that. If you’re not
using VSCode, please adapt accordingly.

To make the changes listed above, you’ll start with the model. Open the
models/perishable.cto model file in VSCode and make the
changes below. Note: I have omitted comments from the model snippets below
only to save space in this tutorial. If you look at
iot-perishable-network-advanced, you’ll see that I have added
comments for each model element I added, and I recommend you do the
same.

First, modify the ShipmentReceived transaction to add an
optional property called receivedDateTime, and then add three
new transactions that extend ShipmentTransaction to update
the blockchain (ledger) when a shipment (1) has been packed, (2) has been
picked up for loading, and (3) has been loaded onto the container
ship:

                transaction ShipmentReceived extends ShipmentTransaction {
                  o DateTime receivedDateTime optional
                }
                
                transaction ShipmentPacked extends ShipmentTransaction {
                }
                
                transaction ShipmentPickup extends ShipmentTransaction {
                }
                
                transaction ShipmentLoaded extends ShipmentTransaction {
                }

Next, modify the Shipment asset to add four new properties for
the shipment-related transactions so that when these transactions run,
they are stored in the blockchain with the Shipment. The
highlighted lines below are the ones you need to add (lines 9-12).

                asset Shipment identified by shipmentId {
                  o String shipmentId
                  o ProductType type
                  o ShipmentStatus status
                  o Long unitCount
                  --> Contract contract
                  o TemperatureReading[] temperatureReadings optional
                  o GpsReading[] gpsReadings optional
                  o ShipmentPacked shipmentPacked optional
                  o ShipmentPickup shipmentPickup optional
                  o ShipmentLoaded shipmentLoaded optional
                  o ShipmentReceived shipmentReceived optional
                }

Now add an abstract participant to represent an IoT device that is
identified by a String property called deviceId, and add two
subclasses of it: one to represent a temperature sensor, and another to
represent a GPS sensor. These participants will update the blockchain with
their respective readings. Devices are network participants? That’s right;
a participant in a blockchain network does not have to be a human
being.

                abstract participant IoTDevice identified by deviceId {
                  o String deviceId
                }
                
                participant TemperatureSensor extends IoTDevice {
                }
                
                participant GpsSensor extends IoTDevice {
                }

Finally, in a real blockchain application, events would be emitted at each
important point in the workflow: when a shipment has been packed, when it
has been picked up, and so forth. Add four new events to represent these
points in the shipment workflow.

                event ShipmentPackedEvent {
                  o String message
                  --> Shipment shipment
                }
                
                event ShipmentPickupEvent {
                  o String message
                  --> Shipment shipment
                }
                
                event ShipmentLoadedEvent {
                  o String message
                  --> Shipment shipment
                }
                
                event ShipmentReceivedEvent {
                  o String message
                  --> Shipment shipment
                }

That’s it for the model changes. Next, you need to make a few modifications
to the existing JavaScript transactions, and add chaincode for the new
transactions.

Add transaction chaincode

Until now, all of the chaincode for the networks you’ve worked with has
been contained in lib/logic.js. Obviously, a real-world
blockchain application could have lots of transactions, causing
logic.js to become cluttered, and eventually difficult to
maintain.

So long as all of your transaction code is in the lib
directory of your model, Hyperledger Composer has no problem resolving the
function calls. To illustrate this, you will make a few changes to
logic.js and add a few more JavaScript source files.

In VSCode, right-click the lib directory and choose
New File, and then enter
instantiateModelForTesting.js as the file name. Now paste in
the following header, or the “license check” step of the build will fail,
giving you an error message when you build the code later:

Listing 3. The Apache 2.0 license header. Make sure to paste this into
every new JavaScript file you create in your
project
.
                /*
                 * Licensed under the Apache License, Version 2.0 (the "License");
                 * you may not use this file except in compliance with the License.
                 * You may obtain a copy of the License at
                 *
                 * http://bit.ly/2AWY5Vb
                 *
                 * Unless required by applicable law or agreed to in writing, software
                 * distributed under the License is distributed on an "AS IS" BASIS,
                 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
                 * See the License for the specific language governing permissions and
                 * limitations under the License.
                 */

Now add the following lines of code to instantiate a TemperatureSensor and
GpsSensor as part of the testing setup. Add just the highlighted lines.
I’ve included surrounding lines of existing code for context, so you can
locate them in the code:

                .
                .
                // create the shipper
                var shipper = factory.newResource(NS, 'Shipper', 'shipper@email.com');
                var shipperAddress = factory.newConcept(NS, 'Address');
                shipperAddress.country = 'Panama';
                shipper.address = shipperAddress;
                shipper.accountBalance = 0;
            
                // create the Temperature sensor
                var temperatureSensor = factory.newResource(NS, 'TemperatureSensor', 'SENSOR_TEMP001');
                // create the GPS sensor
                var gpsSensor = factory.newResource(NS, 'GpsSensor', 'SENSOR_GPS001');
                
                // create the contract
                var contract = factory.newResource(NS, 'Contract', 'CON_001');
                contract.grower = factory.newRelationship(NS, 'Grower', 'farmer@email.com');
                contract.importer = factory.newRelationship(NS, 'Importer', 'supermarket@email.com');
                .
                .
                .then(function() {
                    return getParticipantRegistry(NS + '.Shipper');
                })
                .then(function(shipperRegistry) {
                    // add the shippers
                    return shipperRegistry.addAll([shipper]);
                })
                .then(function() {
                    return getParticipantRegistry(NS + '.TemperatureSensor');
                })
                .then(function(temperatureSensorRegistry) {
                    // add the temperature sensors
                    return temperatureSensorRegistry.addAll([temperatureSensor]);
                })
                .then(function() {
                    return getParticipantRegistry(NS + '.GpsSensor');
                })
                .then(function(gpsSensorRegistry) {
                    // add the GPS sensors
                    return gpsSensorRegistry.addAll([gpsSensor]);
                })
                .then(function() {
                    return getAssetRegistry(NS + '.Contract');
                })
                .then(function(contractRegistry) {
                    // add the contracts
                    return contractRegistry.addAll([contract]);
                })
                .
                .

Now open logic.js, select the entire function body of
setupDemo, and then cut it from logic.js and
paste it into the new file. Finally, rename the function to
instantiateModelForTesting.

Next, in logic.js, rename the payOut function to
receiveShipment so that it more closely matches the
transaction in the model. Then add the following code to emit the
ShipmentReceivedEvent just after the block of code that
computes the payout. The highlighted lines are the ones you should modify
and/or add (modify line 6, add lines 17-27). I’ve included surrounding
lines of existing code for context.

                /**
                 * A shipment has been received by an importer
                 * @param {org.acme.shipping.perishable.ShipmentReceived} shipmentReceived - the ShipmentReceived transaction
                 * @transaction
                 */
                function receiveShipment(shipmentReceived) {
                    .
                    .
                    //console.log('Payout: ' + payOut);
                    contract.grower.accountBalance += payOut;
                    contract.importer.accountBalance -= payOut;
                
                    //console.log('Grower: ' + contract.grower.$identifier + ' new balance: ' + contract.grower.accountBalance);
                    //console.log('Importer: ' + contract.importer.$identifier + ' new balance: ' + contract.importer.accountBalance);
                
                    var NS = 'org.acme.shipping.perishable';
                    // Store the ShipmentReceived transaction with the Shipment asset it belongs to
                    shipment.shipmentReceived = shipmentReceived;
                
                    var factory = getFactory();
                    var shipmentReceivedEvent = factory.newEvent(NS, 'ShipmentReceivedEvent');
                    var message = 'Shipment ' + shipment.$identifier + ' received';
                    //console.log(message);
                    shipmentReceivedEvent.message = message;
                    shipmentReceivedEvent.shipment = shipment;
                    emit(shipmentReceivedEvent);
                
                    return getParticipantRegistry('org.acme.shipping.perishable.Grower')
                        .then(function (growerRegistry) {
                            // update the grower's balance
                            return growerRegistry.update(contract.grower);
                        })
                    .
                    .

Now you’ll need to add JavaScript chaincode for the three new transactions
you added to the model, and you’ll create a new JavaScript source file for
that. In VSCode, right-click the lib directory and choose
New File, and then enter shipment.js as
the file name. Add the functions below to this file. Make sure to
paste in the Apache 2.0 comment header at the top of the file. See
Listing 3.

Transaction: ShipmentPacked
chaincode

First, add a function to handle the ShipmentPacked
transaction. When the Grower participant (or one of its authorized agents)
packs the shipment for pickup and transport, it invokes the
ShipmentPacked blockchain transaction to record this fact in
the ledger.

When this transaction is invoked, a ShipmentPackedEvent is
emitted ,notifying any interested parties of this, and then the chaincode
updates the ledger.

Name the new function packShipment using the “verb/object”
function naming idiom. In JavaScript code, the comments shown below are
mandatory for Hyperledger Composer to recognize the function as a
transaction.

Copy all of the code below and paste it into
shipment.js.

                /**
                 * ShipmentPacked transaction - invoked when the Shipment is packed and ready for pickup.
                 * 
                 * @param {org.acme.shipping.perishable.ShipmentPacked} shipmentPacked - the ShipmentPacked transaction
                 * @transaction
                 */
                function packShipment(shipmentPacked) {
                    var shipment = shipmentPacked.shipment;
                    var NS = 'org.acme.shipping.perishable';
                    var contract = shipment.contract;
                    var factory = getFactory();
                
                    // Add the ShipmentPacked transaction to the ledger (via the Shipment asset)
                    shipment.shipmentPacked = shipmentPacked;
                
                    // Create the message
                    var message = 'Shipment packed for shipment ' + shipment.$identifier;
                
                    // Log it to the JavaScript console
                    //console.log(message);
                
                    // Emit a notification telling subscribed listeners that the shipment has been packed
                    var shipmentPackedEvent = factory.newEvent(NS, 'ShipmentPackedEvent');
                    shipmentPackedEvent.shipment = shipment;
                    shipmentPackedEvent.message = message;
                    emit(shipmentPackedEvent);
                
                    // Update the Asset Registry
                    return getAssetRegistry(NS + '.Shipment')
                        .then(function (shipmentRegistry) {
                            // add the temp reading to the shipment
                            return shipmentRegistry.update(shipment);
                        });
                }

Transaction: ShipmentPickup
chaincode

Next, when the Shipper participant (or one of its authorized agents) picks
up the packed shipment for transport, it invokes the
ShipmentPickup blockchain transaction to record this fact in
the ledger. Add a function to handle the ShipmentPickup
transaction called pickupShipment.

When this transaction is invoked, a ShipmentPickupEvent is
emitted, notifying any interested parties of this, and then the chaincode
updates the ledger.

Copy all of the code below and paste it into shipment.js.

                /**
                 * ShipmentPickup - invoked when the Shipment has been picked up from the packer.
                 * 
                 * @param {org.acme.shipping.perishable.ShipmentPickup} shipmentPickup - the ShipmentPickup transaction
                 * @transaction
                 */
                function pickupShipment(shipmentPickup) {
                    var shipment = shipmentPickup.shipment;
                    var NS = 'org.acme.shipping.perishable';
                    var contract = shipment.contract;
                    var factory = getFactory();
                
                    // Add the ShipmentPacked transaction to the ledger (via the Shipment asset)
                    shipment.shipmentPickup = shipmentPickup;
                
                    // Create the message
                    var message = 'Shipment picked up for shipment ' + shipment.$identifier;
                
                    // Log it to the JavaScript console
                    //console.log(message);
                
                    // Emit a notification telling subscribed listeners that the shipment has been packed
                    var shipmentPickupEvent = factory.newEvent(NS, 'ShipmentPickupEvent');
                    shipmentPickupEvent.shipment = shipment;
                    shipmentPickupEvent.message = message;
                    emit(shipmentPickupEvent);
                
                    // Update the Asset Registry
                    return getAssetRegistry(NS + '.Shipment')
                        .then(function (shipmentRegistry) {
                            // add the temp reading to the shipment
                            return shipmentRegistry.update(shipment);
                        });
                }

Transaction: ShipmentLoaded
chaincode

Finally, when the Shipper participant (or one of its authorized agents)
loads the shipment onto the container ship, it invokes the
ShipmentLoaded blockchain transaction to record this fact in
the ledger. Add a function to handle the ShipmentLoaded event
called loadShipment.

When this transaction is invoked, a ShipmentLoadedEvent is
emitted, notifying any interested parties of this, and then the chaincode
updates the ledger.

Copy all of the code below and paste it into shipment.js.

                /**
                 * ShipmentLoaded - invoked when the Shipment has been loaded onto the container ship.
                 * 
                 * @param {org.acme.shipping.perishable.ShipmentLoaded} shipmentLoaded - the ShipmentLoaded transaction
                 * @transaction
                 */
                function loadShipment(shipmentLoaded) {
                    var shipment = shipmentLoaded.shipment;
                    var NS = 'org.acme.shipping.perishable';
                    var contract = shipment.contract;
                    var factory = getFactory();
                
                    // Add the ShipmentPacked transaction to the ledger (via the Shipment asset)
                    shipment.shipmentLoaded = shipmentLoaded;
                
                    // Create the message
                    var message = 'Shipment loaded for shipment ' + shipment.$identifier;
                
                    // Log it to the JavaScript console
                    //console.log(message);
                
                    // Emit a notification telling subscribed listeners that the shipment has been packed
                    var shipmentLoadedEvent = factory.newEvent(NS, 'ShipmentLoadedEvent');
                    shipmentLoadedEvent.shipment = shipment;
                    shipmentLoadedEvent.message = message;
                    emit(shipmentLoadedEvent);
                
                    // Update the Asset Registry
                    return getAssetRegistry(NS + '.Shipment')
                        .then(function (shipmentRegistry) {
                            // add the temp reading to the shipment
                            return shipmentRegistry.update(shipment);
                        });
                }

Change access control permissions

In a real blockchain application, it is not ideal to grant any participant
permission to do anything they want to. For example, a Grower shouldn’t be
able to invoke transactions that make sense only for a Shipper, and a GPS
sensor shouldn’t be allowed to record temperature readings in the
blockchain.

So how do you prevent that from happening? In other words, how do you build
your network such that you control access to its resources? Through the
Access Control List (ACL) file.

You’ve already seen it in Parts 1 and 2 of this series, but I just sort of
glossed over it, and promised to cover it in more detail at a later point.
Well, that time has come.

It’s time to talk about access control, which begins with
permissions.acl. You saw this file earlier, but it’s time to
put that knowledge to use. You’ll modify the
iot-perishable-network to ensure that resources can be
accessed only by certain participants that make sense to access that
resource. As an illustration, I’ve summarized the access control settings
that make sense for the iot-perishable-network in Table
1.

You may notice the rule names and think they look a bit odd. I like to
encode as much information in a name as possible without making it
ridiculously long, or completely nonsensical. Here’s my thinking: the rule
is named Participant_Operation_Resource. So
Grower_R_Grower means, “Grant the Grower participant READ
access to Grower instances”.

Table 1. Participant access control — The type of
access is in parentheses: R = READ, U = UPDATE, C = CREATE
Participant Participants
accessed
Assets accessed Transactions
accessed
Grower Grower (R) Shipment (RU), Contract (RU) ShipmentPacked (C)
Shipper Shipper (R) Shipment (RU), Contract (RU) ShipmentPickup, ShipmentLoaded (C)
Importer Importer (R), Grower (RU) Shipment (RU), Contract ShipmentReceived (C)
TemperatureSensor NONE Shipment (RU), Contract (RU) TemperatureReading (C)
GpsSensor NONE Shipment (RU), Contract (RU) GpsReading (C)

The rules are codified below. Open permissions.acl (in the
root of the network project), delete its current contents, and replace
then with the listings that follow. The entire listing is very long, so
I’ll talk through it one section at a time.

The first two rules grant permission to the Hyperledger Composer system
Participant and NetworkAdmin participants to
access everything in the org.hyperledger.composer.system
namespace, and every class in the network, respectively.

Copy all of the code below and paste it into
permissions.acl.

                /**
                 * System and Network Admin access rules
                 */
                rule SystemACL {
                  description:  "System ACL to permit all access"
                  participant: "org.hyperledger.composer.system.Participant"
                  operation: ALL
                  resource: "org.hyperledger.composer.system.**"
                  action: ALLOW
                }
                
                rule NetworkAdminUser {
                    description: "Grant business network administrators full access to user resources"
                    participant: "org.hyperledger.composer.system.NetworkAdmin"
                    operation: ALL
                    resource: "**"
                    action: ALLOW
                }

The next set of rules grants permission for the participants in the
iot-perishable-network to access other participants
(including themselves). This access varies as it makes sense for the
business functions of the network. For example, the Grower can only access
the Grower class, but since the Importer executes the
ShipmentReceived transaction (which updates the Importer’s
and Grower’s account balances), the Importer needs READ and UPDATE access
to both Importer and Grower.

Copy all of the code below and paste it into
permissions.acl.

                /**
                 * Rules for Participant registry access
                 */
                rule Grower_R_Grower {
                    description: "Grant Growers access to Grower resources"
                    participant: "org.acme.shipping.perishable.Grower"
                    operation: READ
                    resource: "org.acme.shipping.perishable.Grower"
                    action: ALLOW
                }
                
                rule Shipper_R_Shipper {
                    description: "Grant Shippers access to Shipper resources"
                    participant: "org.acme.shipping.perishable.Shipper"
                    operation: READ
                    resource: "org.acme.shipping.perishable.Shipper"
                    action: ALLOW
                }
                
                rule Importer_RU_Importer {
                    description: "Grant Importers access to Importer resources"
                    participant: "org.acme.shipping.perishable.Importer"
                    operation: READ,UPDATE
                    resource: "org.acme.shipping.perishable.Importer"
                    action: ALLOW
                }
                
                rule Importer_RU_Grower {
                    description: "Grant Importers access to Grower participant"
                    participant: "org.acme.shipping.perishable.Importer"
                    operation: READ,UPDATE
                    resource: "org.acme.shipping.perishable.Grower"
                    action: ALLOW
                }

Next are the rules for access to the network’s assets. In this case, I want
to grant access to the Shipment and Contract resources to all
participants.

Copy all of the code below and paste it into
permissions.acl.

                /**
                 * Rules for Asset registry access
                 */
                rule ALL_RU_Shipment {
                    description: "Grant All Participants in org.acme.shipping.perishable namespace READ/UPDATE access to Shipment assets"
                    participant: "org.acme.shipping.perishable.*"
                    operation: READ,UPDATE
                    resource: "org.acme.shipping.perishable.Shipment"
                    action: ALLOW
                }
                
                rule ALL_RU_Contract {
                    description: "Grant All Participants in org.acme.shipping.perishable namespace READ/UPDATE access to Contract assets"
                    participant: "org.acme.shipping.perishable.*"
                    operation: READ,UPDATE
                    resource: "org.acme.shipping.perishable.Contract"
                    action: ALLOW
                }

Next are the rules for invoking transactions (which require CREATE access).
As you can see, these are on a case-by-case basis as it makes sense. For
example, the Grower participant does not need to access the Shipper
transactions (and vice versa).

Copy all of the code below and paste it into
permissions.acl.

                /**
                 * Rules for Transaction invocations
                 */
                rule Grower_C_ShipmentPacked {
                    description: "Grant Growers access to invoke ShipmentPacked transaction"
                    participant: "org.acme.shipping.perishable.Grower"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.ShipmentPacked"
                    action: ALLOW
                }
                
                rule Shipper_C_ShipmentPickup {
                    description: "Grant Shippers access to invoke ShipmentPickup transaction"
                    participant: "org.acme.shipping.perishable.Shipper"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.ShipmentPickup"
                    action: ALLOW
                }
                
                rule Shipper_C_ShipmentLoaded {
                    description: "Grant Shippers access to invoke ShipmentLoaded transaction"
                    participant: "org.acme.shipping.perishable.Shipper"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.ShipmentLoaded"
                    action: ALLOW
                }
                
                rule GpsSensor_C_GpsReading {
                    description: "Grant IoT GPS Sensor devices full access to the appropriate transactions"
                    participant: "org.acme.shipping.perishable.GpsSensor"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.GpsReading"
                    action: ALLOW
                }
                
                rule TemperatureSensor_C_TemperatureReading {
                    description: "Grant IoT Temperature Sensor devices full access to the appropriate transactions"
                    participant: "org.acme.shipping.perishable.TemperatureSensor"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.TemperatureReading"
                    action: ALLOW
                }
                
                rule Importer_C_ShipmentReceived {
                    description: "Grant Importers access to invoke the ShipmentReceived transaction"
                    participant: "org.acme.shipping.perishable.Importer"
                    operation: CREATE
                    resource: "org.acme.shipping.perishable.ShipmentReceived"
                    action: ALLOW
                }

The last rule says (in effect), “If there is some resource that we did not
explicitly grant access to above, deny access to it.” This is currently
Hyperledger Composer’s default behavior, but if that changes in a
subsequent release, this rule makes sure the network behaves like this
regardless of the default behavior.

Copy all of the code below and paste it into
permissions.acl.

                /**
                 * Make sure all resources are locked down by default.
                 * If permissions need to be granted to certain resources, that should happen
                 * above this rule. Anything not explicitly specified gets locked down.
                 */
                rule Default {
                    description: "Deny all participants access to all resources"
                    participant: "ANY"
                    operation: ALL
                    resource: "org.acme.shipping.perishable.*"
                    action: DENY
                }

That’s all for permissions.acl. Save the file, and get ready
to unit test these permissions.

Wait, unit test permissions? That’s right. All of the permissions in
permissions.acl can be unit tested using Cucumber. I
introduced you to Cucumber in Part 2, and now you’ll use Cucumber to unit
test the new transaction logic and the ACL rules you added to
permissions.acl in the previous section.

The cool thing about Cucumber (pun intended) is that its syntax (Gherkin)
is intuitive, so I don’t need to explain a lot of the tests you’re about
to see.

Basic test scenarios

In VSCode, open iot-perishable.feature, then delete its
contents, and replace them with the listings below as you work through
this section.

First, you will set up the feature test as shown below. Notice on lines
14-16 that the test issues identities (that is, ID cards) for the
specified participants. These identities will be used later in the unit
test to test the security permissions you added to
permissions.acl in the previous section.

Copy all of the code below and paste it into
iot-perishable.feature.

                Feature: Basic Test Scenarios
                    Background:
                        Given I have deployed the business network definition ..
                        And I have added the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"supermarket@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":0}
                        ]
                        """
                        And I have added the following participants of type org.acme.shipping.perishable.TemperatureSensor
                            | deviceId |
                            | TEMP_001 |
                        And I have issued the participant org.acme.shipping.perishable.Grower#grower@email.com with the identity grower1
                        And I have issued the participant org.acme.shipping.perishable.Importer#supermarket@email.com with the identity importer1
                        And I have issued the participant org.acme.shipping.perishable.TemperatureSensor#TEMP_001 with the identity sensor_temp1
                        And I have added the following asset of type org.acme.shipping.perishable.Contract
                            | contractId | grower           | shipper               | importer           | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                            | CON_001    | grower@email.com | supermarket@email.com | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              | 
                        And I have added the following asset of type org.acme.shipping.perishable.Shipment
                            | shipmentId | type    | status     | unitCount | contract |
                            | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                        And I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 4          |
                            | SHIP_001 | 5          |
                            | SHIP_001 | 10         |
                        When I use the identity importer1

Notice line 28 in the listing above. I recommend you always put a “default
identity” in your Cucumber tests to make sure your security permissions
are always being tested (see the sidebar). You can always override this
with a subsequent “When I use the identity xyz” in a specific scenario
test if you like, as you’ll see in this section.

The first scenario you’ll run is when there are no temperature readings
outside the agreed-upon range. Notice on line 2 that this test will be run
as the identity importer1, which is an Importer.

Copy all of the code below and paste it into
iot-perishable.feature.

                    Scenario: When the temperature range is within the agreed-upon boundaries
                        When I use the identity importer1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentReceived
                            | shipment |
                            | SHIP_001 |
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":2500},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"supermarket@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":-2500}
                        ]
                        """

The next scenario tests the case when there is a temperature reading that
is below the agreed-upon threshold. The payout amount should be less
(because of the low temperature penalty in the contract).

Notice that the test overrides the default identity, and is run first as
sensor_temp1, which is a TemperatureSensor
participant (line 2), then the current identity is switched to
importer1, which is an Importer (line 6). If the test
is not run this way, it will attempt to execute the
TemperatureReading transaction as importer1
(which was set in the Background section), which does not have permission
to invoke that transaction. Then the identity must be switched back or the
ShipmentReceived transaction call will fail because a
TemperatureSensor participant does not have permission to invoke that
transaction.

Copy all of the code below and paste it into
iot-perishable.feature.

                    Scenario: When the low/min temperature threshold is breached by 2 degrees C
                        When I use the identity sensor_temp1
                        And I submit the following transaction of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 0          |
                        Then I use the identity importer1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentReceived
                            | shipment |
                            | SHIP_001 |
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":500},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"supermarket@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":-500}
                        ]
                        """

The next scenario tests the case when there is a temperature reading that
is above the threshold. Like the previous scenario, this one must also be
run using two separate identities.

Copy all of the code below and paste it into
iot-perishable.feature.

                    Scenario: When the hi/max temperature threshold is breached by 2 degrees C
                        When I use the identity sensor_temp1
                        And I submit the following transaction of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 12          |
                        Then I use the identity importer1
                        When I submit the following transaction of type org.acme.shipping.perishable.ShipmentReceived
                            | shipment |
                            | SHIP_001 |
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":1500},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"supermarket@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":-1500}
                        ]
                        """

Finally, the ShipmentReceived transaction must be run as an
Importer (the only participant that has permission to invoke it), and when
it does an event is emitted. The identity is set for this scenario in the
Background section.

Copy all of the code below and paste it into
iot-perishable.feature.

                    Scenario: When shipment is received a ShipmentReceivedEvent should be broadcast
                        When I submit the following transaction of type org.acme.shipping.perishable.ShipmentReceived
                            | shipment |
                            | SHIP_001 |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentReceivedEvent
                            | message                    | shipment |
                            | Shipment SHIP_001 received | SHIP_001 |

That’s all for iot-perishable.feature. Go ahead and save the
file.

Tests for IoT devices

The remaining feature tests work essentially the same as the ones you just
saw, so I won’t bore you with a repeated explanation. As you complete this
section, just follow the directions, and paste the listings as a whole
into the new .feature files.

In VSCode, click on the features directory and choose
New File, and call the file
sensors.feature. When the file opens in the editor window,
copy and paste the following listing into the new empty file:

Copy all of the code below and paste it into
sensors.feature.

                Feature: Tests related to IoT Devices
                
                    Background:
                        Given I have deployed the business network definition ..
                        And I have added the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Shipper", "email":"shipper@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"Panama"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Importer", "email":"importer@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"UK"}, "accountBalance":0}
                        ]
                        """
                        And I have added the following participants of type org.acme.shipping.perishable.TemperatureSensor
                            | deviceId |
                            | TEMP_001 |
                        And I have added the following participant of type org.acme.shipping.perishable.GpsSensor
                            | deviceId |
                            | GPS_001  |
                        And I have issued the participant org.acme.shipping.perishable.Grower#grower@email.com with the identity grower1
                        And I have issued the participant org.acme.shipping.perishable.Shipper#shipper@email.com with the identity shipper1
                        And I have issued the participant org.acme.shipping.perishable.TemperatureSensor#TEMP_001 with the identity sensor_temp1
                        And I have issued the participant org.acme.shipping.perishable.GpsSensor#GPS_001 with the identity sensor_gps1
                        And I have added the following asset of type org.acme.shipping.perishable.Contract
                            | contractId | grower           | shipper               | importer              | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                            | CON_001    | grower@email.com | shipper@email.com     | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              | 
                        And I have added the following asset of type org.acme.shipping.perishable.Shipment
                            | shipmentId | type    | status     | unitCount | contract |
                            | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                
                    Scenario: Test TemperatureThresholdEvent is emitted when the max temperature threshold is violated
                        When I use the identity sensor_temp1
                        When I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 11         |
                        
                        Then I should have received the following event of type org.acme.shipping.perishable.TemperatureThresholdEvent
                            | message                                                                          | temperature | shipment |
                            | Temperature threshold violated! Emitting TemperatureEvent for shipment: SHIP_001 | 11          | SHIP_001 |    
                
                    Scenario: Test TemperatureThresholdEvent is emitted when the min temperature threshold is violated
                        When I use the identity sensor_temp1
                        When I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 0          |
                        
                        Then I should have received the following event of type org.acme.shipping.perishable.TemperatureThresholdEvent
                            | message                                                                          | temperature | shipment |
                            | Temperature threshold violated! Emitting TemperatureEvent for shipment: SHIP_001 | 0           | SHIP_001 |
                    
                    Scenario: Test ShipmentInPortEvent is emitted when GpsReading indicates arrival at destination port
                        When I use the identity sensor_gps1
                        When I submit the following transaction of type org.acme.shipping.perishable.GpsReading
                            | shipment | readingTime | readingDate | latitude | latitudeDir | longitude | longitudeDir |
                            | SHIP_001 | 120000      | 20171025    | 40.6840  | N           | 74.0062   | W            |
                
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentInPortEvent
                            | message                                                                  | shipment |
                            | Shipment has reached the destination port of /LAT:40.6840N/LONG:74.0062W | SHIP_001 |
                
                    Scenario: GpsSensor sensor_gps1 can invoke GpsReading transaction
                        When I use the identity sensor_gps1
                        When I submit the following transaction of type org.acme.shipping.perishable.GpsReading
                            | shipment | readingTime | readingDate | latitude | latitudeDir | longitude | longitudeDir |
                            | SHIP_001 | 120000      | 20171025    | 40.6840  | N           | 74.0062   | W            |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentInPortEvent
                            | message                                                                  | shipment |
                            | Shipment has reached the destination port of /LAT:40.6840N/LONG:74.0062W | SHIP_001 |
                    
                    Scenario: Temperature Sensor cannot invoke GpsReading transaction
                        When I use the identity sensor_temp1
                        When I submit the following transaction of type org.acme.shipping.perishable.GpsReading
                            | shipment | readingTime | readingDate | latitude | latitudeDir | longitude | longitudeDir |
                            | SHIP_001 | 120000      | 20171025    | 40.6840  | N           | 74.0062   | W            |
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/
                
                    Scenario: Gps Sensor cannot invoke TemperatureReading transaction
                        When I use the identity sensor_gps1
                        When I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 11         |        
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/
                
                    Scenario: Grower cannot invoke TemperatureReading transaction
                        When I use the identity sensor_gps1
                        When I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                            | shipment | centigrade |
                            | SHIP_001 | 11         |        
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/

Now save this file.

Tests related to Growers

In VSCode, click the features directory, choose New
File
, and call the file grower.feature. When the
file opens in the editor window, copy and paste the following listing into
the new empty file:

Copy all of the code below and paste it into
grower.feature.

                Feature: Tests related to Growers
                
                    Background:
                        Given I have deployed the business network definition ..
                        And I have added the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Shipper", "email":"shipper@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"Paname"}, "accountBalance":0}
                        ]
                        """
                        And I have issued the participant org.acme.shipping.perishable.Grower#grower@email.com with the identity grower1
                        And I have issued the participant org.acme.shipping.perishable.Shipper#shipper@email.com with the identity shipper1
                        And I have added the following asset of type org.acme.shipping.perishable.Contract
                            | contractId | grower           | shipper               | importer              | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                            | CON_001    | grower@email.com | shipper@email.com     | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              | 
                        And I have added the following asset of type org.acme.shipping.perishable.Shipment
                            | shipmentId | type    | status     | unitCount | contract |
                            | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                        When I use the identity grower1
                
                    Scenario: grower1 can read Grower assets
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0}
                        ]
                        """
                    
                    Scenario: grower1 invokes the ShipmentPacked transaction
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentPacked
                            | shipment |
                            | SHIP_001 |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentPackedEvent
                            | message                               | shipment |
                            | Shipment packed for shipment SHIP_001 | SHIP_001 |
                
                    Scenario: shipper1 cannot read Grower assets
                        When I use the identity shipper1
                        And I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0}
                        ]
                        """
                        Then I should get an error matching /Object with ID .* does not exist/
                    
                    Scenario: shipper1 cannot invoke the ShipmentPacked transaction
                        When I use the identity shipper1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentPacked
                            | shipment |
                            | SHIP_001 |
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/

Now save this file. Notice the default identity in use (highlighted line).
You need to set an identity for a specific scenario only if it needs a
different identity.

In VSCode, click the features directory, choose New
File
, and call the file shipper.feature. When
the file opens in the editor window, copy and paste the following listing
into the new empty file:

Copy all of the code below and paste it into
shipper.feature.

                Feature: Tests related to Shippers
                
                    Background:
                        Given I have deployed the business network definition ..
                        And I have added the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Grower", "email":"grower@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"USA"}, "accountBalance":0},
                        {"$class":"org.acme.shipping.perishable.Shipper", "email":"shipper@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"Paname"}, "accountBalance":0}
                        ]
                        """
                        And I have issued the participant org.acme.shipping.perishable.Grower#grower@email.com with the identity grower1
                        And I have issued the participant org.acme.shipping.perishable.Shipper#shipper@email.com with the identity shipper1
                        And I have added the following asset of type org.acme.shipping.perishable.Contract
                            | contractId | grower           | shipper               | importer              | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                            | CON_001    | grower@email.com | shipper@email.com     | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              | 
                        And I have added the following asset of type org.acme.shipping.perishable.Shipment
                            | shipmentId | type    | status     | unitCount | contract |
                            | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                        When I use the identity shipper1
                
                    Scenario: shipper1 can read Shipper assets
                        Then I should have the following participants
                        """
                        [
                        {"$class":"org.acme.shipping.perishable.Shipper", "email":"shipper@email.com", "address":{"$class":"org.acme.shipping.perishable.Address", "country":"Paname"}, "accountBalance":0}
                        ]
                        """
                    
                    Scenario: shipper1 invokes the ShipmentPickup transaction
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentPickup
                            | shipment |
                            | SHIP_001 |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentPickupEvent
                            | message                                  | shipment |
                            | Shipment picked up for shipment SHIP_001 | SHIP_001 |
                
                    Scenario: shipper1 invokes the ShipmentLoaded transaction
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentLoaded
                            | shipment |
                            | SHIP_001 |
                        Then I should have received the following event of type org.acme.shipping.perishable.ShipmentLoadedEvent
                            | message                               | shipment |
                            | Shipment loaded for shipment SHIP_001 | SHIP_001 |
                
                    Scenario: grower1 cannot invoke the ShipmentPickup transaction
                        When I use the identity grower1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentPickup
                            | shipment |
                            | SHIP_001 |
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/
                
                    Scenario: grower1 cannot invoke the ShipmentPickup transaction
                        When I use the identity grower1
                        And I submit the following transaction of type org.acme.shipping.perishable.ShipmentLoaded
                            | shipment |
                            | SHIP_001 |
                        Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/

Now save this file.

That’s it for the Cucumber feature tests.

Build and unit test the network

Now it’s time to build and unit test your network. Go to the command line
(Ubuntu) or open a terminal window (MacOS) and enter this command:

npm install && npm test

You have run these commands before in Part 2 of this tutorial series.
You’ll see a lot of output, but when the unit tests have run, you should
see this:

                  ✔ And I have issued the participant org.acme.shipping.perishable.Shipper#shipper@email.com with the identity shipper1
                  ✔ And I have added the following asset of type org.acme.shipping.perishable.Contract
                      | contractId | grower           | shipper           | importer              | arrivalDateTime  | unitPrice | minTemperature | maxTemperature | minPenaltyFactor | maxPenaltyFactor |
                      | CON_001    | grower@email.com | shipper@email.com | supermarket@email.com | 10/26/2018 00:00 | 0.5       | 2              | 10             | 0.2              | 0.1              |
                  ✔ And I have added the following asset of type org.acme.shipping.perishable.Shipment
                      | shipmentId | type    | status     | unitCount | contract |
                      | SHIP_001   | BANANAS | IN_TRANSIT | 5000      | CON_001  |
                  ✔ When I use the identity shipper1
                  ✔ When I use the identity grower1
                  ✔ And I submit the following transaction of type org.acme.shipping.perishable.ShipmentLoaded
                      | shipment |
                      | SHIP_001 |
                  ✔ Then I should get an error matching /Participant .* does not have 'CREATE' access to resource/
                
                20 scenarios (20 passed)
                229 steps (229 passed)
                0m09.497s

Lines 15-16 above show the 20 scenarios (consisting of 229 steps) have all
run without error. You’re now ready to deploy the network!

Deploy your changes to the network

If you’ve been following along with the tutorial, you have already deployed
the iot-perishable-network to the local Hyperledger Fabric
and instantiated the model. Unfortunately, you need to add a
TemperatureSensor and GpsSensor participants to the participant registry.
You can do this through the CLI, but I want to show you how to tear down
and clean up your local Hyperledger Fabric, which is something you need to
do from time to time.

When you tear down the Hyperledger Fabric using the procedure I’ll outlined
below, it removes the network and any crypto materials you’ve generated so
far. Now, you never want to do this in production, for obvious reasons,
but this is development, so it’s something you’re going to need to know
how to do.

Navigate to your $COMPOSER_ROOT directory and enter the
./teardownFabric.sh command. You’ll see output like this:

                $ ./teardownFabric.sh 
                Development only script for Hyperledger Fabric control
                Running 'teardownFabric.sh'
                .
                .                
                # Shut down the Docker containers for the system tests.
                cd "${DIR}"/composer
                ARCH=$ARCH docker-compose -f docker-compose.yml kill && docker-compose -f docker-compose.yml down
                Killing peer0.org1.example.com ... done
                Killing ca.org1.example.com    ... done
                Killing orderer.example.com    ... done
                Killing couchdb                ... done
                WARNING: The ARCH variable is not set. Defaulting to a blank string.
                Removing peer0.org1.example.com ... done
                Removing ca.org1.example.com    ... done
                Removing orderer.example.com    ... done
                Removing couchdb                ... done
                Removing network composer_default
                .
                .                
                # Your system is now clean

Now wipe out the ~/.composer directory, which will delete all
the cards and crypto materials you’ve generated so far. Then run the
createPeerAdmin.sh script again:

                $ rm -Rf ~/.composer
                $ ./createPeerAdminCard.sh 
                Development only script for Hyperledger Fabric control
                Running 'createPeerAdminCard.sh'
                FABRIC_VERSION is unset, assuming hlfv1
                FABRIC_START_TIMEOUT is unset, assuming 15 (seconds)
                
                Using composer-cli at v0.15.0
                Successfully created business network card to /tmp/PeerAdmin@hlfv1.card
                
                Command succeeded
                
                Successfully imported business network card: PeerAdmin@hlfv1
                
                Command succeeded
                
                Hyperledger Composer PeerAdmin card has been imported
                The following Business Network Cards are available:
                
                
                ┌─────────────────┬───────────┬─────────┐
                │ CardName        │ UserId    │ Network │
                ├─────────────────┼───────────┼─────────┤
                │ PeerAdmin@hlfv1 │ PeerAdmin │         │
                └─────────────────┴───────────┴─────────┘
                
                Issue composer card list --name <CardName>  to get details of the card
                
                Command succeeded

Next, start the local Hyperledger Fabric by running the
startFabric.sh script, and then navigate to the
iot-perishable-network directory, deploy the network, and
import the admin@iot-perishable-network card again:

                $ cd $COMPOSER_ROOT/fabric-tools
                $./startFabric.sh
                .
                .
                $ composer network deploy -a dist/iot-perishable-network.bna -A admin -S adminpw -c PeerAdmin@hlfv1 -f networkadmin.card
                Deploying business network from archive: dist/iot-perishable-network.bna
                Business network definition:
                	Identifier: iot-perishable-network@0.1.12
                	Description: Shipping Perishable Goods Business Network
                
                ✔ Deploying business network definition. This may take a minute...
                Successfully created business network card to networkadmin.card
                
                Command succeeded
                
                $ composer card import --file networkadmin.card 
                Successfully imported business network card: admin@iot-perishable-network
                
                Command succeeded

Finally, you need to execute the SetupDemo transaction to
instantiate the model, or there will be no participants for which to issue
ID cards!

                $ composer transaction submit --card admin@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.SetupDemo"}'
                Transaction Submitted.
                
                Command succeeded

From now on, if you make changes to the network (so long as it does not
involve modifications to any of the entries in the blockchain), you just
need to update it:

                $ composer network update -a dist/iot-perishable-network.bna --card admin@iot-perishable-network
                Deploying business network from archive: dist/iot-perishable-network.bna
                Business network definition:
                    Identifier: iot-perishable-network@0.1.12
                    Description: Shipping Perishable Goods Business Network
                
                ✔ Updating business network definition. This may take a few seconds...
                Successfully created business network card to undefined
                
                Command succeeded

Now it’s time to issue some IDs. You did this in Playground earlier. Now
you will use the CLI to issue ID cards for the participants shown in Table
2.

Table 2. Participant Identities to be issued
Participant Identity ID card file name
Grower grower1 grower1.card
Shipper shipper1 shipper1.card
Importer importer1 importer1.card
TemperatureSensor sensor_temp1 sensor_temp1.card
GpsSensor sensor_gps1 sensor_gps1.card

In the CLI section, you invoked the SetupDemo transaction,
which instantiated the network. Remember, you cannot issue an ID for a
participant that is not in the Participant Registry.

To issue an ID card for a participant, first execute the
composer identity issue command, specifying the card file,
and then import the card file into the local wallet. Use the
admin@iot-perishable-network card to authenticate when you
execute the composer identity issue command.

The general format for the command for the
iot-perishable-network
is this:

                composer identity issue --card admin@iot-perishable-network --file ID_CARD_FILE --newUserId IDENTITY --participantId 'resource:org.acme.shipping.perishable.PARTICIPANT#PARTICIPANT_ID'

Where:

ID_CARD_FILE— is the file name where the ID card will be stored (see Table
2).

IDENTITY— is the identity that is to be issued (see Table 2).

PARTICIPANT_CLASS— is the participant class (for example, Grower).

PARTICIPANT_ID— is the ID of the participant when it was instantiated in the
registry (for example, farmer@email.com).

ID Card: Grower

                $ composer identity issue --card admin@iot-perishable-network --file grower1.card --newUserId grower1 --participantId 'resource:org.acme.shipping.perishable.Grower#farmer@email.com'
                
                Command succeeded

                $ composer card import --file grower1.card 
                Successfully imported business network card: grower1@iot-perishable-network
                
                Command succeeded

ID Card: Shipper

                $ composer identity issue --card admin@iot-perishable-network --file shipper1.card --newUserId shipper1 --participantId 'resource:org.acme.shipping.perishable.Shipper#shipper@email.com'
                
                Command succeeded
                
                Ix:~/HyperledgerComposer/developerWorks/iot-perishable-network sperry$ composer card import --file shipper1.card 
                Successfully imported business network card: shipper1@iot-perishable-network
                
                Command succeeded

ID Card: Importer

                $ composer identity issue --card admin@iot-perishable-network --file importer1.card --newUserId importer1 --participantId 'resource:org.acme.shipping.perishable.Importer#supermarket@email.com'
                
                Command succeeded
                
                Ix:~/HyperledgerComposer/developerWorks/iot-perishable-network sperry$ composer card import --file importer1.card 
                Successfully imported business network card: importer1@iot-perishable-network
                
                Command succeeded

ID Card: TemperatureSensor

                $ composer identity issue --card admin@iot-perishable-network --file sensor_temp1.card --newUserId sensor_temp1 --participantId 'resource:org.acme.shipping.perishable.TemperatureSensor#SENSOR_TEMP001'
                
                Command succeeded

                $ composer card import --file sensor_temp1.card 
                Successfully imported business network card: sensor_temp1@iot-perishable-network
                
                Command succeeded

ID Card: GpsSensor

                $ composer identity issue --card admin@iot-perishable-network --file sensor_gps1.card --newUserId sensor_gps1 --participantId 'resource:org.acme.shipping.perishable.GpsSensor#SENSOR_GPS001'
                
                Command succeeded
                
                Ix:~/HyperledgerComposer/developerWorks/iot-perishable-network sperry$ composer card import --file sensor_gps1.card 
                Successfully imported business network card: sensor_gps1@iot-perishable-network
                
                Command succeeded

Now that you’ve issued ID cards for all the participants in the network,
and imported those cards into your local Hyperledger Fabric wallet, you
can simulate the workflow of the IoT Perishable Goods business network
from the command line using the CLI.

To see the cards you’ve issued, run the composer card list
command:

                $ composer card list
                The following Business Network Cards are available:
                
                
                ┌─────────────────────────────────────┬──────────────┬────────────────────────┐
                │ CardName                            │ UserId       │ Network                │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ admin@iot-perishable-network        │ admin        │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ importer1@iot-perishable-network    │ importer1    │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ grower1@iot-perishable-network      │ grower1      │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ sensor_temp1@iot-perishable-network │ sensor_temp1 │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ sensor_gps1@iot-perishable-network  │ sensor_gps1  │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ shipper1@iot-perishable-network     │ shipper1     │ iot-perishable-network │
                ├─────────────────────────────────────┼──────────────┼────────────────────────┤
                │ PeerAdmin@hlfv1                     │ PeerAdmin    │                        │
                └─────────────────────────────────────┴──────────────┴────────────────────────┘
                
                Issue composer card list --name <CardName>  to get details of the card
                
                Command succeeded

The general format for the composer transaction submit command
is:

                composer transaction submit --card CARD_NAME -d 'DATA'

Where:

CARD_NAME— is the name of the card to use (see Table 2).

DATA is a JSON object containing the transaction data. You can
copy the data format for any transaction from Playground, remove the
newlines, and replace the specific data values (yes, that’s how I came up
with the data for the transactions you’ll see in this section).

Submit transactions

In this section you’ll submit the following transactions through the
CLI:

  • ShipmentPacked
  • ShipmentPickup
  • ShipmentLoaded
  • TemperatureReading
  • GpsReading
  • ShipmentReceived

Each of these transactions corresponds to a step in the workflow of moving
perishable goods from the Grower to the Importer.

Submit a ShipmentPacked transaction, using the grower1 ID
card to authenticate and authorize with the network:

                $ composer transaction submit --card grower1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentPacked", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

This transaction succeeded because the Grower participant has permission to
execute the ShipmentPacked transaction. Just for fun, try to
execute the transaction using the shipper1 ID card:

                $ composer transaction submit --card shipper1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentPacked", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Error: Error trying invoke business network. Error: chaincode error (status: 500, message: Error: Participant 'org.acme.shipping.perishable.Shipper#shipper@email.com' does not have 'CREATE' access to resource 'org.acme.shipping.perishable.ShipmentPacked#8db39906c73c0821021489834f9e5fb37f29bab4253840cb756038b77da4dc00')
                Command failed

The Shipper participant does not have access to the
ShipmentPacked transaction, so the attempt fails, showing the
permissions you coded earlier in the tutorial are working just as expected
(of course, you knew that when you ran the unit test, but it’s always nice
to see it in action).

The second step in the workflow of getting a shipment of perishable goods
from the Grower to the Importer is to pick up the packed shipment, which
is handled by the Shipper:

                $ composer transaction submit --card shipper1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentPickup", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Once the shipment has been picked up, the Shipper loads it onto the
container ship and executes the ShipmentLoaded transaction to
record this in the ledger:

                $ composer transaction submit --card shipper1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentLoaded", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

Along the way, the TemperatureSensor participant is taking readings inside
the cargo container and recording them in the ledger. Simulate a few of
those like this:

                $ composer transaction submit --card sensor_temp1@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 2, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded
                $ composer transaction submit --card sensor_temp1@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 3, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded
                
                Ix:~/HyperledgerComposer/developerWorks/iot-perishable-network sperry$ composer transaction submit --card sensor_temp1@iot-perishable-network -d '{ "$class": "org.acme.shipping.perishable.TemperatureReading", "centigrade": 11, "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

That was three TemperatureReading transactions, with the last
one at 11C, which is 1 degree above the contract threshold, so the high
temperature penalty will be in effect when the shipment is received.

Now submit a GPS transaction indicating the container ship has reached its
destination, which is the Port of New York/New Jersey:

                $ composer transaction submit --card sensor_gps1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.GpsReading", "readingTime": "2200", "readingDate": "20171118", "latitude": "40.6840", "latitudeDir": "N", "longitude": "74.0062", "longitudeDir": "W", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}' 
                Transaction Submitted.
                
                Command succeeded

The final step in the workflow is for the Importer to receive the shipment
and invoke the ShipmentReceived transaction to record this in
the blockchain:

                $ composer transaction submit --card importer1@iot-perishable-network -d '{"$class": "org.acme.shipping.perishable.ShipmentReceived", "shipment": "resource:org.acme.shipping.perishable.Shipment#SHIP_001"}'
                Transaction Submitted.
                
                Command succeeded

That doesn’t seem very exciting, does it? Go ahead and fire up the REST
server:

                $ composer-rest-server
                ? Enter the name of the business network card to use: admin@iot-perishable-network
                ? Specify if you want namespaces in the generated REST API: always use namespaces
                ? Specify if you want to enable authentication for the REST API using Passport: No
                ? Specify if you want to enable event publication over WebSockets: No
                ? Specify if you want to enable TLS security for the REST API: No
                Discovered types from business network definition
                Generating schemas for all types in business network definition ...
                Generated schemas for all types in business network definition
                Adding schemas for all types to Loopback ...
                Added schemas for all types to Loopback
                Web server listening at: http://localhost:3000
                Browse your REST API at http://localhost:3000/explorer                
                To restart the REST server using the same options, issue the following command:
                   composer-rest-server -c admin@iot-perishable-network -n always
                
                Discovering types from business network definition ...

Now point your browser to localhost:3000, locate the Shipment
asset, and execute the /Get method. You will see all of the
transactions you submitted, recorded in the blockchain as part of the
Shipment asset. Figure 5 shows what you should see in the response body of
the /Get request.

Figure 5. Shipment asset showing transactions entered
through the CLI

Shipment asset showing transactions entered                     through the CLI

There’s quite a bit of data, so I could only show a little of it in Figure
5, but go ahead, fire up the REST server for yourself and scroll through
the Shipment asset and see the results for yourself.

Video: Wrap-up demo

All the stuff from the previous section, plus Playground (where
applicable), is wrapped up in this video:

Conclusion to Part 3

This tutorial covered a lot of ground. First, you installed, started, and
then deployed the iot-perishable-network to a local instance
of Hyperledger Fabric.

Then you installed and ran the REST interface, which you can use to access
the network. I hope you had a chance to watch the video, where I walk
through that in more detail. If not, make sure and check that out.

After that, you saw how access control works in Hyperledger Composer,
including ACL rules and where they live in the network’s source code.

Then you worked with the Composer Command Line Interface (CLI) to ping the
running network, execute the SetupDemo and other
transactions, and update the network as you make changes in
development.

Finally, the coup de grace: you modified the
iot-perishable-network to transform it into a more real-world
blockchain application, write Cucumber feature tests, issue IDs for all
participants, and execute every transaction through the CLI.

At this point, you should have everything you need to start developing your
own blockchain applications using Hyperledger Composer. Good luck!


Downloadable resources



#awvi,#blockchain

via IBM developerWorks : Cloud computing https://ibm.co/2cihRPX

December 13, 2017 at 04:00PM