(ubuntu)Hyperledger Composer – 基础安装部署测试(三)

作者:心有山海静而无边来源:CSDN原文链接:https://blog.csdn.net/qq_38591756/article/details/82950065?utm_source=copy著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

完善并部署您的区块链网络(第二部分)

1.设置环境

安装node.js

安装 Node.js 的最简单方法是使用 nvm,也就是 Node Version Manager。顾名思义,nvm 用于管理安装在您的计算机上的 Node 版本。nvm安装自行搜索。

安装完成后即可使用nvm安装node.js。

在命令行输入:

nvm install 8.11.1

来安装 Node.js 的8.11.1版本,版本可自行指定,此处使用8.11.1。

在等待安装完成后使用node -v验证node.js

安装node.js时会同时安装npm(Node Package Manager),npm版本也可以使用npm -v查看版本,此处使用的是5.8.0版本。

安装 Composer 命令行接口 (CLI)

您会使用该命令行接口 (CLI) 创建、部署和更新业务网络,以及执行与您的区块链网络相关的其他功能。

要安装Composer CLI,可到命令行输入此命令:

npm install -g –save composer-cli@0.19.1

通常在通过 npm 安装 Node.js 包时,仅能在已安装它的目录树内使用它。 指定 -g 选项会告诉 npm 对该包进行全局安装,这使得它可用于计算机上的所有 Node.js 项目。–save选项会同时安装相关依赖包。@0.19.1指定安装0.19.1版本,不指定则安装最新版。

验证 composer-cli 已正确安装。从命令行输入:composer -v命令,版本号将作为输出出现。

安装 Composer REST Server

npm install -g –save composer-rest-server@0.19.1

安装完成后使用

npm view composer-rest-server version

命令查看版本验证是否成功。

    需要注意的是,composer-cli和composer-rest-sercer默认依赖的grpc版本可能为1.12.*,但是在第三部分,及本文的四中,如果grpc为1.12.*会出现如下错误图所示错误,但是在1.10.1中正常:

    Error: Connection is not in the READY state


    因此需要手动修改grpc版本为1.10.1,具体操作如下:

    首先查看本地npm全局安装的包:

    npm list -g –depth 0


    删除多余以及版本不正确的包,命令如下,其余类似:

    npm uninstall -g grcp

    删除完成之后如下:


    然后安装指定版本grpc包:

    npm install -g –save grpc@1.10.1


    安装完成后找到npm安装的包的位置,本机为~/.nvm/versions/node/v8.11.1/lib/node_modules,在该目录下为所有npm安装的包,如下:

    打开composer-cli文件夹,查看composer-cli/node_modules/grpc目录下package.json文件,如果前两行版本为grpc@1.10.1则可跳过后面的步骤,否则返回到composer-cli目录下(切记不是composer-cli/node_modules/grpc目录),编辑其中的package.json文件,在其中dependencies标签下添加”grpc”: “1.10.1”,结果类似下图(json格式,请注意行末逗号):

    添加后保存并退出,然后在该目录执行:npm install命令,

    再次查看composer-cli/node_modules/grpc目录下package.json文件,版本已经变为1.10.1。

    对composer-rest-sercer包执行相同的操作,使composer-rest-sercer/node_modules/grpc目录下package.json文件中前两行版本也为grpc@1.10.1。

    修改完成后继续执行后面的操作或者在四之前进行修改。

安装 VSCode(非必须)

VSCode 是一个来自 Microsoft 的开源编辑器。执行 Hyperledger Composer 开发不需要安装 VSCode,但是Hyperledger Composer 有一个用于 VSCode 的扩展,您可以轻松地安装和启用它。它能够顺利集成 Git,还为 Composer 业务网络模型文件提供了语法突出显示功能。如果安装完成后可在插件管理中安装Hyperledger Composer扩展。

2.运行构建

在计算机上选择将使用Hyperledger Composer和网络模型的位置。例如,本文使用 ~/HyperledgerComposer 作为Composer根目录,并在使用的 Bash shell 中设置一个环境变量:

export COMPOSER_ROOT=~/HyperledgerComposer

如果是使用命令行设置,则每次重新链接都需要重新设置,因此也可以直接在配置文件中设置。

导航到您的 $COMPOSER_ROOT 目录(cd $COMPOSER_ROOT),即刚刚设置的composer根目录,然后输入此命令:

git clone https://github.com/makotogo/developerWorks.git

下载完成后,使用 Node.js 包管理器 (npm) 运行一次构建,然后执行代码中提供的单元测试。执行以下两条命令:

    cd $COMPOSER_ROOT/developerWorks/perishable-network
     
    npm install && npm test

以上两行命令首先导航到 perishable-network 代码所在的目录。然后运行 npm install,这会设置本地 Node.js 环境(即 perishable-network 的本地环境)。然后运行 npm test,在最后部分如果输出类似于以下内容,这表明测试成功完成:

    Payout: 1500

    Grower: farmer@email.com new balance: 4000

    Importer: supermarket@email.com new balance: -4000

          ✓ should apply penalty for min temperature violation (81ms)

    Adding temperature 11 to shipment SHIP_001

    Received at: Wed Nov 01 2017 10:58:12 GMT-0500 (CDT)

    Contract arrivalDateTime: Thu Nov 02 2017 10:58:12 GMT-0500 (CDT)

    Lowest temp reading: 1

    Highest temp reading: 11

    Min temp penalty: 0.2

    Max temp penalty: 0.30000000000000004

    Payout: 999.9999999999998

    Grower: farmer@email.com new balance: 5000

    Importer: supermarket@email.com new balance: -5000

          ✓ should apply penalty for max temperature violation (74ms)

      3 passing (1s)
 
3.完善Perishable Goods 业务网络

Perishable Goods 网络将对一个业务网络进行建模,其中包括:一个种植者、一个航运商和一个进口商。

修改网络定义

以下内容可以直接复制粘贴,修改后的解决方案应类似于 developerWorks/iot-perishable-network 目录中的解决方案。可以使用此模型作为参考。

要添加 GPS 传感器,需要对模型做一些更改。启动 VSCode,打开 Perishable Goods 网络的根目录$COMPOSER_ROOT/developerWorks/perishable-network。打开位于 models 目录中的名为 perishable.cto 的模型文件。

将一个表示罗盘上的主要位置的新 enum 添加到 enum ShipmentStatus 下方   :

    /**

     * Directions of the compass

     */

    enum CompassDirection {

      o N

      o S

      o E

      o W

    }

方向中的 N 表示北方、S 表示南方,等等。限制输入来表示一组 GPS 坐标的数据很重要,这个 enum 用于限制可输入模型中的值,以确保它们有效。

每次获得一个 GPS 读数,都会将它作为一个事务记录到区块链中,这意味着您需要向模型中添加一个针对它的事务。在 TemperatureReading 事务下方添加一个新的 GpsReading 事务:

    /**

     * A GPS reading for a shipment.E.g. received from a device

     * within a shipping container

     */

    transaction GpsReading extends ShipmentTransaction {

      o String readingTime

      o String readingDate

      o String latitude

      o CompassDirection latitudeDir

      o String longitude

      o CompassDirection longitudeDir

    }

在获取 GPS 读数时需要包含一些参数,以及经纬度。此信息是作为参数提供给事务的。

接下来,要让事务能够将 GPS 读数存储在区块链中,该信息需要包含在一个区块链资产中。 因为从船运集装箱获取的 GPS 读数在概念上是一批货物的一部分,所以将该读数添加到 Shipment 资产中是合情合理的(就像 TemperatureReading 一样)。将下面突出显示的行(第 7 行)添加到 Shipment 资产中:

    asset Shipment identified by shipmentId {

      o String shipmentId

      o ProductType type

      o ShipmentStatus status

      o Long unitCount

      o TemperatureReading[] temperatureReadings optional

      o GpsReading[] gpsReadings optional

      –> Contract contract

    }

最后,向模型添加两个事件:一个表示违反温度阈值,另一个表示集装箱船到达目标港口:

    /**

     * An event – when the temperature goes outside the agreed-upon boundaries

     */

    event TemperatureThresholdEvent {

      o String message

      o Double temperature

      –> Shipment shipment

    }

     

    /**

     * An event – when the ship arrives at the port

     */

    event ShipmentInPortEvent {

      o String message

      –> Shipment shipment

    }

 

添加链代码

现在已对 GPS 传感器和用于将 GPS 读数添加到模型的事务进行建模。现在需要编写 JavaScript 链代码来处理区块链的更新。打开 lib/logic.js。您需要对此文件执行一些更改。

首先,向 temperatureReading 函数添加代码来处理 TemperatureReading 事务。将该方法的全部内容替换为下面这个方法,为方便参考,突出显示了添加的行:

    function temperatureReading(temperatureReading) {

     

        var shipment = temperatureReading.shipment;

        var NS = “org.acme.shipping.perishable”;

        var contract = shipment.contract;

        var factory = getFactory();

        console.log(‘Adding temperature ‘ + temperatureReading.centigrade + ‘ to shipment ‘ + shipment.$identifier);

        if (shipment.temperatureReadings) {

            shipment.temperatureReadings.push(temperatureReading);

        } else {

            shipment.temperatureReadings = [temperatureReading];

        }

        if (temperatureReading.centigrade < contract.minTemperature ||

            temperatureReading.centigrade > contract.maxTemperature) {

            var temperatureEvent = factory.newEvent(NS, ‘TemperatureThresholdEvent’);

            temperatureEvent.shipment = shipment;

            temperatureEvent.temperature = temperatureReading.centigrade;

            temperatureEvent.message = ‘Temperature threshold violated! Emitting TemperatureEvent for shipment: ‘ + shipment.$identifier;

            emit(temperatureEvent);

        }

        return getAssetRegistry(NS + ‘.Shipment’)

            .then(function (shipmentRegistry) {

                // add the temp reading to the shipment

                return shipmentRegistry.update(shipment);

            });

    }

此代码参照合同规定检查了当前温度读数,如果超过了最低或最高温度限制,则会发出一个 TemperatureThresholdEvent 事件。

接下来,添加一个新函数来处理 GpsReading 事务。

备注:同时添加注释块很重要。它包含您将需要的两个重要注释(@param 和 @transaction)。

    /**

     * A GPS reading has been received for a shipment

     * @param {org.acme.shipping.perishable.GpsReading} gpsReading – the GpsReading transaction

     * @transaction

     */

    function gpsReading(gpsReading) {

        var factory = getFactory();

        var NS = “org.acme.shipping.perishable”;

        var shipment = gpsReading.shipment;

        var PORT_OF_NEW_YORK = ‘/LAT:40.6840N/LONG:74.0062W’;

        var latLong = ‘/LAT:’+ gpsReading.latitude + gpsReading.latitudeDir + ‘/LONG:’+

            gpsReading.longitude + gpsReading.longitudeDir;

        if (shipment.gpsReadings) {

            shipment.gpsReadings.push(gpsReading);

        } else {

            shipment.gpsReadings = [gpsReading];

        }

        if (latLong == PORT_OF_NEW_YORK) {

            var shipmentInPortEvent = factory.newEvent(NS, ‘ShipmentInPortEvent’);

            shipmentInPortEvent.shipment = shipment;

            var message = ‘Shipment has reached the destination port of ‘ + PORT_OF_NEW_YORK;

            shipmentInPortEvent.message = message;

            emit(shipmentInPortEvent);

        }

        return getAssetRegistry(NS + ‘.Shipment’)

        .then(function (shipmentRegistry) {

            // add the temp reading to the shipment

            return shipmentRegistry.update(shipment);

        });

    }

该事务的链代码将此 GPS 读数存储在 Shipment 资产中的 GpsReading 数组中。然后,它会检查此 GPS 读数是否与目标港口对应,如果是,则发出 ShipmentInPort 事件。最后,使用 Shipment 的当前状态更新区块链。

添加 Cucumber 功能测试

总结一下,在之前的构建中向模型添加了一个事务,以及两个新事件。现在需要对更改进行单元测试,确保它们工作正常。Hyperledger Composer 团队推荐使用 Cucumber 对 Composer 业务模型进行单元测试。

在 features 文件夹中创建一个名为 iot-perishable.feature 的新文件,并在 VSCode 中打开它。文中将简单解释一下您需要添加的每一节。在“使用 Cucumber 执行单元测试”小节中,我将更全面地解释 Cucumber。但是首先,让我们执行一些相关工作。

首先,告诉 Cucumber 您想测试的特性,以及需要在每个单元测试之前执行的任何后台(设置)。将以下代码添加到您的空 iot-perishable.feature 文件中。

    Feature: IoT Perishable Network
     
        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},
            {“$class”:”org.acme.shipping.perishable.Shipper”, “email”:”shipper@email.com”, “address”:{“$class”:”org.acme.shipping.perishable.Address”, “country”:”Panama”}, “accountBalance”:0}
            ]
            “””
            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  |
            When I submit the following transactions of type org.acme.shipping.perishable.TemperatureReading
                | shipment | centigrade |
                | SHIP_001 | 4          |
                | SHIP_001 | 5          |
                | SHIP_001 | 10         |

现在需要添加一些称为场景 (Scenario) 的单元测试。将场景添加到 iot-perishable.feature 文件中的 Background 块下方。

    Scenario: When the temperature range is within the agreed-upon boundaries
        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”:2500},
        {“$class”:”org.acme.shipping.perishable.Importer”, “email”:”supermarket@email.com”, “address”:{“$class”:”org.acme.shipping.perishable.Address”, “country”:”UK”}, “accountBalance”:-2500},
        {“$class”:”org.acme.shipping.perishable.Shipper”, “email”:”shipper@email.com”, “address”:{“$class”:”org.acme.shipping.perishable.Address”, “country”:”Panama”}, “accountBalance”:0}
        ]
        “””

此场景确保在货物集装箱中的温度在协商的限制内时,为种植者支付了足额的费用。

现在添加一个场景:超出了最低温度阈值(2 摄氏度) 2 度。

    Scenario: When the low/min temperature threshold is breached by 2 degrees C
        Given I submit the following transaction of type org.acme.shipping.perishable.TemperatureReading
            | shipment | centigrade |
            | SHIP_001 | 0          |
     
        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”:500},
        {“$class”:”org.acme.shipping.perishable.Importer”, “email”:”supermarket@email.com”, “address”:{“$class”:”org.acme.shipping.perishable.Address”, “country”:”UK”}, “accountBalance”:-500},
        {“$class”:”org.acme.shipping.perishable.Shipper”, “email”:”shipper@email.com”, “address”:{“$class”:”org.acme.shipping.perishable.Address”, “country”:”Panama”}, “accountBalance”:0}
        ]
        “””

在此场景中,最低温度下降到 0 摄氏度,针对低于阈值的每摄氏度,向种植者收取罚金。

现在添加一个场景:超出最高温度阈值 2 摄氏度。

    Scenario: When the hi/max temperature threshold is breached by 2 degrees C
        Given I submit the following transaction of type org.acme.shipping.perishable.TemperatureReading
            | shipment | centigrade |
            | SHIP_001 | 12          |
     
        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},
        {“$class”:”org.acme.shipping.perishable.Shipper”, “email”:”shipper@email.com”, “address”:{“$class”:”org.acme.shipping.perishable.Address”, “country”:”Panama”}, “accountBalance”:0}
        ]
        “””

在此场景中,最高温度超过了 10 摄氏度,针对高于阈值的每摄氏度,向种植者收取罚金。

最后,向模型中添加 3 个场景(两个 TemperatureThresholdEvent 和一个 ShipmentInPortEvent),如下所示。

    Scenario: Test TemperatureThresholdEvent is emitted when the min temperature threshold is violated
        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 TemperatureThresholdEvent is emitted when the max temperature threshold is violated
        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 ShipmentInPortEvent is emitted when GpsReading indicates arrival at destination port
        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 |

最后,需要修改 package.json,使它看起来类似下面的清单。突出显示了需要修改(第 18 行)和添加(第 41 和 42 行)的行。

    {
      “engines”: {
        “composer”: “^0.15.0”
      },
      “name”: “perishable-network”,
      “version”: “0.1.11”,
      “description”: “Shipping Perishable Goods Business Network”,
      “networkImage”: “https://github.com/makotogo/developerWorks/perishable-network/networkimage.svg”,
      “networkImageanimated”: “https://github.com/makotogo/developerWorks/perishable-network/networkimageanimated.svg”,
      “scripts”: {
        “prepublish”: “mkdirp ./dist && composer archive create  –sourceType dir –sourceName .-a ./dist/perishable-network.bna”,
        “pretest”: “npm run lint”,
        “lint”: “eslint .”,
        “postlint”: “npm run licchk”,
        “licchk”: “license-check”,
        “postlicchk”: “npm run doc”,
        “doc”: “jsdoc –pedantic –recurse -c jsdoc.json”,
        “test”: “mocha -t 0 –recursive && cucumber-js”,
        “deploy”: “./scripts/deploy.sh”
      },
      “repository”: {
        “type”: “git”,
        “url”: “https://github.com/makotogo/developerWorks.git”
      },
      “keywords”: [
        “shipping”,
        “goods”,
        “perishable”,
        “composer”,
        “composer-network”,
        “iot”
      ],
      “author”: “Hyperledger Composer”,
      “license”: “Apache-2.0”,
      “devDependencies”: {
        “browserfs”: “^1.2.0”,
        “chai”: “^3.5.0”,
        “composer-admin”: “^0.14.0-0”,
        “composer-cli”: “^0.14.0-0”,
        “composer-client”: “^0.14.0-0”,
        “composer-connector-embedded”: “^0.14.0-0”,
        “composer-cucumber-steps”: “^0.14.0-0”,
        “cucumber”: “^2.2.0”,
        “eslint”: “^3.6.1”,
        “istanbul”: “^0.4.5”,
        “jsdoc”: “^3.4.1”,
        “license-check”: “^1.1.5”,
        “mkdirp”: “^0.5.1”,
        “mocha”: “^3.2.0”,
        “moment”: “^2.17.1”
      },
      “license-check-config”: {
        “src”: [
          “**/*.js”,
          “!./coverage/**/*”,
          “!./node_modules/**/*”,
          “!./out/**/*”,
          “!./scripts/**/*”
        ],
        “path”: “header.txt”,
        “blocking”: true,
        “logInfo”: false,
        “logError”: true
      }
    }

请注意,向 package.json 添加了两个新 Node 模块(上面的第 41 和 42 行)。要安装它们,需要在命令行(当前目录)运行npm install命令。

 运行单元测试

现在,从命令行执行 npm test 来运行单元测试。将会产生许多输出,但这一次,除了 Mocha 测试之外,您还会看到每个 Cucumber 特性场景的一个输出块。Cucumber 功能测试的开头类似于下面这段代码。输出的最后几行类似于下面这段代码,这表明单元测试通过了。

      ✔ When 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 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 |

    6 scenarios (6 passed)

    44 steps (44 passed)

    0m02.788s

4.将改后的网络部署到 IBM Cloud

此部分以及更深入地探索如何使用 Cucumber 执行单元测试参考链接中相应部分

章发布只为分享区块链技术内容,版权归原作者所有,观点仅代表作者本人,绝不代表区块链兄弟赞同其观点或证实其描述。

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享