原文:
zh.annas-archive.org/md5/7f932e9670331dae388d1a76f72881d8
译者:飞龙
第十五章:构建以太坊区块链应用程序
在上一章中,我们回顾了智能合约的基本特性以及如何编写一个众筹智能合约示例。在我们将智能合约部署到区块链之后,我们需要编写 Web 应用程序来与智能合约交互。以太坊区块链通过调用智能合约函数和获取器提供了 web3 API。
在本章中,我们将涵盖以下主题:
-
什么是去中心化应用(DApp)
-
web3js 快速概述
-
设置以太坊开发环境
-
开发和测试 DApp
去中心化应用概述
去中心化应用(或DApp)是一种利用智能合约运行的应用程序。智能合约部署在以太坊虚拟机(EVM)上。它类似于客户端-服务器低层架构。一个 DApp 可以有一个前端(Web),通过 web3.js API 调用其后端(智能合约)。
以下结构是我们将为我们的众筹 DApp 构建的:
我们将为众筹 DApp 构建的结构
web3.js 快速概述
web3.js 是一个以太坊 JavaScript API,提供了一系列库来与本地或远程以太坊网络交互。web3js 与以太坊之间的连接是通过使用 HTTP 或 IPC 协议进行的。在下表中,我们快速审查了一些重要的 web3.js API 概念:
API 参考 | 描述 | 示例 |
---|---|---|
web3-eth | 该包提供了与以太坊区块链和智能合约交互的 API | getBalance , sendTransaction, coinbase , getBlockNumber, getAccounts |
web3-shh | 该包提供了与 whisper 协议进行广播交互的 API |
web3.shh.post({
symKeyID: identities[0],
topic: '0xffaadd11',
payload: '0xffffffdddddd1122'
}).then(h => console.log(`Message with hash ${h} was successfuly sent`))
|
web3-bzz | 该包提供了与以太坊疯蜂网络(Ethereum swarm)交互的 API,即去中心化文件存储平台 | web3.bzz.currentProvider``web3.bzz.download(bzzHash [, localpath]) |
---|---|---|
web3-utils | 该包提供了一组针对以太坊 DApp 和其他 web3.js 包的实用函数 | web3.utils.toWei(number [, unit])``web3.utils.isAddress(address) |
提供者
提供者抽象了与以太坊区块链进行通信的连接。它将向区块链发出查询并发送交易。web3 提供了 JsonRpcProvider
和 IpcProvider
,允许您连接到本地或远程以太坊节点,包括 Mainnet、Ropsten 测试网、Kovan 测试网、Rinkeby 测试网和自定义的远程过程调用(RPC),如 Ganache。下面是展示如何使用 web3 API 连接以太坊节点的代码片段。
var Web3 = require('web3');
var web3 = new Web3('http://localhost:8545');
// or
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
// change provider
web3.setProvider('ws://localhost:8546');
// or
web3.setProvider(new Web3.providers.WebsocketProvider('ws://localhost:8546'));
DApp 开发工具
有一些流行的区块链 Web 开发工具被开发者用于创建 DApp 项目的基本结构。以下部分列出了其中的一些。
Truffle
Truffle 是一个以太坊 DApp 端到端开发工具,提供了一个开发环境,用于编写、编译和部署测试智能合约和 DApps。您可以为前端编写 HTML、CSS 和 JavaScript;Solidity 用于智能合约,并使用 web3.js API 与 UI 和智能合约交互。Truffle Boxes 提供了有用的样板文件,其中包含了有用的模块、Solidity 合约和库、前端代码以及许多其他有用的文件。Truffle Boxes 帮助开发人员快速启动他们的 DApp 项目。
Truffle 命令行使用以下格式:
truffle [command] [options]
这里是命令行工具中常用的选项:
命令 | 描述 |
---|---|
compile | 编译 Solidity 合约文件。 |
console | 与部署的智能合约交互的命令行界面。 |
create | 此命令可帮助创建新的合约、新的迁移文件和基本测试。 |
debug | 在调试器会话中对特定交易进行实验。 |
deploy /migration | 将合约部署到区块链网络。 |
develop | 在本地开发环境中通过命令行与合约交互。 |
init | 从以太坊包注册表中安装包。 |
Ganache
Ganache 是一个私有的以太坊区块链环境,允许您模拟以太坊区块链,以便您可以在自己的私有区块链中与智能合约交互。以下是 Ganache 提供的一些功能:
-
显示区块链日志输出
-
提供高级挖掘控制
-
内置区块浏览器
-
以太坊区块链环境
-
Ganache 有一个桌面应用程序和一个命令行工具
这是 Ganache 桌面版的外观:
命令行使用以下格式:
ganache-cli <options>
这些是命令行工具的常用选项:
选项 | 描述 |
---|---|
-a 或 --accounts | 启动时要生成的账户数量。 |
-e 或 --defaultBalanceEther | 配置默认的测试账户以太币数量。默认值为 100 。 |
-b 或 --blockTime | 指定以秒为单位的区块时间作为挖掘间隔。如果未指定此选项,当调用事务时,Ganache 将立即挖掘新块。 |
-h 或 --host 或 --hostname | 指定要侦听的主机名。默认值为 127.0.0.1 。 |
-p 或 --port | 指定端口号。默认值为 8545 。 |
-g 或 --gasPrice | 以 Wei 指定气价(默认为 20000000000 )。 |
-l 或 --gasLimit | 区块气体限制(默认为 0x6691b7 )。 |
--debug | 为调试目的显示 VM 操作码。 |
-q 或 --quiet | 不记录任何日志运行 ganache-cli 。 |
设置以太坊开发环境
按照以下说明获取以太坊开发工具,并启动以太坊私有本地区块链环境(主要用于在本地区块链上运行/部署您的智能合约)。
安装 Truffle
打开命令行并运行以下命令:
npm install -g truffle
安装 Ganache
打开命令行并安装 Ganache 的命���行接口,如下所示:
npm install -g ganache-cli
创建一个 Truffle 项目
要初始化一个新的 DApp 项目,我们可以运行 truffle init
命令来初始化一个空的 Truffle 项目。这将创建 DApp 目录结构,包括应用程序、合约和测试以及 Truffle 配置。由于 Truffle Boxes 提供了许多可用的模板,在我们的 DApp 示例中,我们将使用宠物商店模板——一个 JavaScript UI 库的 JQuery 版本——来开发我们的众筹 DApp 示例。
创建一个名为Crowdfunding
的文件夹,打开命令行提示符,导航到Crowdfunding
文件夹,并运行以下命令:
truffle unbox pet-shop
项目结构如下:
我们在上一章中编写了众筹智能合约。让我们将CrowdFunding.sol
文件复制到Crowdfunding
下的 contracts 文件夹中。
启动 Ganache 环境
打开一个新的终端窗口并运行以下命令:
Ganache-cli
这将在端口8545
上运行Ganache-cli
,而且 Ganache 将为我们创建 10 个默认账户。每个账户默认拥有 100 以太币。你应该在控制台中看到类似这样的东西:
在我们的 Truffle 项目中,truffle.js
将7545
定义为默认端口号。我们需要将端口号更新为8545
以与 Ganache 的端口号匹配,如下所示:
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // Match any network id
}
}
};
部署智能合约
正如你可能已经注意到的,先前的命令创建了两个与迁移相关的文件,Migrations.sol
和1_initial_migration.js
。Migrations.sol
存储了对应于最后一个应用的“迁移”脚本的编号。当你添加一个新的合约并部署合约时,存储中的最后一个部署编号将增加。合约运行一次后,就不会再次运行。编号的约定是x_script_name.js
,x 从 1 开始,即1_initial_migration.js
。你的新合约通常会在以2_...
开始的脚本中。
在我们的情况下,我们将添加一个新的迁移合约以部署CrowdFunding
。让我们创建一个名为2_deploy_contracts.js
的文件。
CrowdFunding.sol
将构造函数定义如下:
constructor (address _owner, uint _minimumToRaise, uint _durationProjects,
string _name, string _website)
要部署合约,可以调用 truffle deploy 函数,带有可选的构造函数参数,deployer.deploy(contract, args..., options)
。
我们将使用由 Ganache 提供的第一个账户作为所有者账户,如下所示:
var CrowdFunding = artifacts.require("./CrowdFunding.sol");
module.exports = (deployer, network, accounts) => {
const ownerAddress = accounts[0];
deployer.deploy(CrowdFunding, ownerAddress, 30, 60, "smartchart", "smartchart.tech");
}
让我们将智能合约部署到我们的网络。运行truffle
命令,如下所示:
truffle migrate
以下截图显示了运行truffle migrate
命令的结果:
这样可以在本地 Ganache 区块链环境中部署我们的众筹智能合约。
要启动本地节点服务器,请运行以下命令,这将在我们的浏览器中打开宠物商店页面:
npm run dev
编写众筹去中心化应用
我们刚刚在我们的本地 Ganache 区块链环境上部署了我们的智能合约。现在,我们将开始编写 UI 代码,通过 RPC 调用触发智能合约函数。本章的源代码可在 bit.ly/2X8xPBL
上找到。
选择一个 web3 提供程序
当我们加载网页时,我们需要连接到一个 web3 提供程序。如果您已经安装了像 MetaMask 这样的提供程序,您可以使用您的正确提供程序选项,如下所示:
App.web3Provider = web3.currentProvider;
在我们的众筹示例中,为了简单起见,我们将直接连接到我们的本地 Ganache 服务器,如下所示:
App.web3Provider = new Web3.providers.HttpProvider('http://localhost:8545');
加载账户信息
要加载账户,我们定义一个内容为空的下拉菜单,如下所示:
<div class="form-group">
<label for="exampleFormControlSelect1">Accounts</label>
<select class="form-control" id="accts">
</select>
</div>
当我们加载页面时,我们将使用 web3.eth.accounts
获取所有 10 个默认账户。请注意,第一个账户的以太均衡为 99.84;这是因为我们使用第一个账户作为所有者账户来部署合约,并燃烧了一些以太作为交易费用,如下代码所示:
web3.eth.accounts.forEach( function(e){
$('#accts').append($('<option>', {
value:e,
text : e + " (" +web3.fromWei(web3.eth.getBalance(e), "ether") + " ether)"
}));
})
一旦账户加载完毕,将显示如下:
加载项目信息
在众筹中,我们定义了一个包含筹款信息的项目结构,如下所示:
struct Project {
address addr;
string name;
string website;
uint totalRaised;
uint minimumToRaise;
uint currentBalance;
uint deadline;
uint completeAt;
Status status;
}
让我们在 HTML 中定义一些相关信息,例如:
<table class="table table-hover table-striped">
<tbody>
<tr>
<th scope="row">address</th>
<td><span class="text-info" id="address"></span
</td>
</tr>
<tr>
<th scope="row">name</th>
<td><span class="text-info" id="name"></span></td>
</tr>
<tr>
<th scope="row">website</th>
<td><span class="text-info" id="website"></span></td>
</tr>
<tr>
<th scope="row">totalRaised</th>
<td><span class="text-info" id="totalRaised"></span></td>
…
</tbody>
</table>
CrowdFunding.deployed()
函数将创建一个代表由 CrowdFunding
管理的默认地址的 CrowdFunding
实例。这里的代码显示了我们如何显示项目信息:
App.contracts.CrowdFunding.deployed().then(function(instance) {
crowdFundingInstance = instance;
return crowdFundingInstance.project();
}).then(function(projectInfo) {
$("#address").text(projectInfo[0].toString());
$("#name").text(projectInfo[1]);
$("#website").text(projectInfo[2]);
$("#totalRaised").text(projectInfo[3].toString());
..
if(projectInfo[6].toString().length>0) {
var deadline = new
Date(Number(projectInfo[6].toString())*1000);
deadlineDate = moment(deadline).format("YYYY-MM-DD h:mm:ss");
$("#deadline").text(deadlineDate);
}
if(projectInfo[7].toString().length>0 && projectInfo[7].toString()!='0') {
console.log(projectInfo[7].toString());
var completeAt = new Date(Number(projectInfo[7].toString())*1000);
completeAtDate = moment(completeAt).format("YYYY-MM-DD h:mm:ss");
$("#completeAt").text(completeAtDate);
}
}).catch(function(error) {
..
});
结果将如下显示:
处理基金功能
要筹集资金,我们需要调用 fund 函数,在我们的众筹智能合约中定义。在我们的网页中,我们使用 HTML 范围输入滑块组件来贡献资金金额,如下所示:
<form id="fund-form" method="post" role="form" style="display: block;">
<div class="form-group row">
<div class="row">
<div class="col-lg-12">
<input type="range" name="ageInputName" id="ageInputId" value="0" min="1" max="100" oninput="ageOutputId.value = ageInputId.value">
<div style="display: inline;"><output name="ageOutputName" id="ageOutputId">0</output> <span>ether</span></div>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-lg-12">
<button type="button" id="fundBtn" class="btn btn-primary pull-left">Submit</button>
</div>
</div>
</div>
</form>
Crowdfunding fund
函数是一个可支付的回退函数;因此,我们需要从 UI 传递 msg.sender
和 msg.value
来调用它,如下所示。
function fund() public atStage(Status.Fundraising) payable {
contributions.push(
Contribution({
addr: msg.sender,
amount: msg.value
})
);
……
}
您可以按如下方式定义发送地址和值参数:
handleFund: function(event) {
event.preventDefault();
var fundVal = $('#ageOutputId').val();
var selectAcct = $('#accts').find(":selected").val();
$("#displayMsg").html("");
App.contracts.CrowdFunding.deployed().then(function(instance) {
return instance.fund({ from: selectAcct, value:web3.toWei(fundVal, "ether"), gas:3500000});
}).then(function(result) {
App.loadProject();
}).catch(function(err) {
console.error(err);
$("#displayMsg").html(err);
});
},
一旦我们收到结果返回,我们将调用 loadProject
函数刷新项目信息。我们可以看到当前余额基金增加了,如下截图所示:
checkGoalReached
一旦达到筹款目标,众筹所有者将通过运行 checkGoalReached
方法收集所有资金。
HTML 仅为一个简单的按钮,如下所示的代码:
<button type="button" id="checkGoal" class="btn btn-success">CheckGoal</button>
与 fund 函数类似,我们使用以下代码在 JavaScript 中调用智能合约:
instance.checkGoalReached({ from: selectAcct, gas:3500000});
这是详细的逻辑:
handleCheckGoal: function(event) {
event.preventDefault();
$("#displayMsg").html("");
var selectAcct = $('#accts').find(":selected").val();
App.contracts.CrowdFunding.deployed().then(function(instance) {
return instance.checkGoalReached({ from: selectAcct, gas:3500000});
}).then(function(result) {
App.loadProject();
}).catch(function(err) {
console.error(err);
$("#displayMsg").html(err);
});
},
结果将如下显示:
如果您跟随整个示例并运行了这一步,恭喜!您现在可以编写并运行一个众筹 DApp 了。
概要
在本章中,我们学习了 DApp 的基础知识,现在我们了解了 web3.js API。通过在本地以太坊环境中运行 Ganache,我们可以使用 Truffle 开发工具创建一个众筹项目并编写一个 DApp 组件。最后,我们部署并启动了众筹 DApp。在下一章中,我们将开始探索最受欢迎的企业区块链——Hyperledger Fabric。
第十六章:使用 Hyperledger Fabric 探索企业区块链应用
在前一章中,我们讨论了以太坊区块链。以太坊是一个公共区块链;任何人都可以阅读区块链数据并进行合法的更改。任何人都可以向链中写入一个新区块。以太坊是完全自治的,不受任何人控制。智能合约用 Solidity 语言编写,作为一个几乎图灵完备的语言,可以在以太坊虚拟机 (EVM)上执行各种交易。开发人员可以使用这些智能合约构建和部署去中心化应用 (DApps)。以太是以太坊中的加密货币,作为执行以太坊中的每个操作的燃料,包括执行智能合约、DApps、交易等。然而,这并不是构建区块链的唯一方法。
可以创建需要在区块链节点中建立访问控制层以读取区块链上受限信息的区块链。这将限制网络中能够参与共识机制中交易的参与者数量。这种区块链称为许可区块链。
下表显示了公共和许可区块链之间的区别:
无需许可 | 许可 |
---|
| 公共 | 每个人都可以阅读交易数据。每个人都可以验证区块中的交易。
-
速度: 差
-
一致性: 工作量证明
-
区块链: 比特币、以太坊
-
代币: 需要
| 每个人都可以阅读交易数据。只有预定义的用户可以验证交易。
-
速度: 良好
-
一致性: 工作量证明
-
区块链: 卡斯帕后的以太坊
-
代币: 需要
|
| 私有 | 只有预定义的用户可以读取交易数据。只有预定义的用户可以验证交易。
-
速度: 良好
-
一致性: 联邦拜占庭协议 (FBA)
-
代币: 不需要
只有预定义的用户可以读取交易数据。只有有权的用户可以验证交易。
-
速度: 良好
-
一致性: 实用拜占庭容错算法 (PBFT)
-
区块链: Hyperledger Fabric
-
代币: 不需要
|
Hyperledger Fabric 是一种私有的许可区块链之一。在本章中,我们将讨论 Hyperledger Fabric 区块链。
Hyperledger Fabric 是一种开源企业区块链技术。该项目最初由 IBM 和数字资产贡献。Hyperledger Fabric 是 Linux 基金会托管的区块链项目之一。Hyperledger Fabric 中的智能合约称为链码,它定义了 Fabric 应用的业务逻辑。模块化的架构设计使 Fabric 能够支持高度的机密性、恢复能力、灵活性和可扩展性。Fabric 中的组件,如共识和成员服务,可以插拔式部署。
在本章中,我们将涵盖以下主题:
-
发行声明
-
设置 Hyperledger Fabric 环境
-
编写链码
-
配置 Hyperledger Fabric
发行索赔
在本节中,我们将探讨并实施一个发行索赔的用例。
没有人希望有保险索赔,但当事情出错和发生事故时,这可能会导致财务损失。这些损失将由您的保险单承担。传统的保险理赔流程几十年来都保持不变,因为流程中存在许多关键问题,包括虚假索赔、欺诈检测、缓慢和繁琐的索赔处理、人为错误、不佳的客户体验以及再保险中信息流的低效。
使用区块链,分类账中的交易记录是不可变的,只有当所有各方都同意时,状态数据才能更新。区块链中的记录可以实时共享。这使得保险公司可以迅速行动,因为大部分用于索赔验证的必要信息可以在短时间内处理。保险公司可以跟踪区块链中资产数据的使用情况。文件工作可以被消除,客户可以通过网络应用提交索赔。
让我们看一下保险索赔流程,如下截图所示。为了演示目的,我们简化了索赔流程,因为在实际应用中可能会更加复杂:
对于上述流程,步骤如下:
-
被保险人向经纪人报告索赔
-
经纪人提供请求的信息
-
经纪人向发行人提交索赔
-
发行人确认索赔
-
发行人处理并批准索赔
配置 Hyperledger Fabric 环境
到目前为止,我们已经了解了 Hyperledger Fabric 的关键概念。在本节中,我们将建立一个 Hyperledger Fabric 开发环境。在继续进行安装步骤之前,让我们看一下 fabric 安装的先决条件。
安装先决条件
安装所需的开发工具的先决条件如下:
Ubuntu Linux 14.04 / 16.04 LTS(均为 64 位),或 macOS 10.12 | Docker Engine:版本 17.03 或更高 |
---|---|
Docker-Compose:版本 1.8 或更高 | Node:8.9 或更高(注意不支持 9 版本) |
npm:v5.x | git:2.9.x 或更高 |
Python:2.7.x |
我们将在开发环境中使用 Ubuntu。我们可以使用以下命令下载先决条件:
curl -O https://hyperledger.github.io/composer/latest/prereqs-ubuntu.sh
chmod u+x prereqs-ubuntu.sh
./prereqs-ubuntu.sh
它可能在执行过程中提示您输入密码,因为它在执行过程中使用了sudo
。
安装 Hyperledger Fabric
创建并转到名为insurance-claim
的项目文件夹,如下所示:
mkdir ~/insurance-claim && cd ~/insurance-claim
输入以下命令以安装 Hyperledger Fabric 特定于平台的二进制文件:
curl -sSL https://raw.githubusercontent.com/hyperledger/fabric/release-1.3/scripts/bootstrap.sh | bash
执行此命令后,它会在bin
文件夹中下载以下特定于平台的二进制文件,该文件夹位于fabric-samples
文件夹下。您可以将fabric-samples/bin
设置为PATH
变量,如下所示:
export PATH=<path to download location>/bin:$PATH
我们还从本书的代码文件中提供 bootstrap-hyperledger.sh
,您可以从 Packt 网站下载。一旦获得文件,您可以直接运行以下脚本,它将创建一个 bin 文件夹并将二进制文件下载到此文件夹中:
这些组件将成为我们的 Hyperledger Fabric 网络的一部分。
编写链码
Chaincode 类似于智能合约。它定义并执行由特定网络中的授权参与者调用的业务逻辑。链码是用 Go 或 Node.js 编写的。在我们的示例中,我们将使用 Go。
有许多 IDE 和工具支持 Golang。以下是一些与 Golang 配合得很好的流行 IDE。
开发工具
有各种工具支持 Go 开发。一些流行的 IDE 在以下部分列出。
LiteIDE
LiteIDE 是专为 Golang 设计的开源 Go IDE。有许多适用于 Go 开发人员的有用功能,包括可配置的代码编辑器、自定义构建命令、许多构建选项和 Golang 支持。
JetBrains Gogland
Gogland 拥有强大的内置自动完成引擎、错误检测、代码重构工具等等。
Visual Studio Code
您可以在 Visual Studio Code 中安装 Go 扩展。它提供代码提示和调试代码的能力。
在本章中,我们将使用 LiteIDE 来开发我们的链码。请按照官方 LiteIDE 安装指南设置您的本地 IDE 环境,该指南可从以下链接获取:
github.com/visualfc/liteide/blob/master/liteidex/deploy/welcome/en/install.md
.
Chaincode 关键概念和 API
Fabric 链码中有三个重要的函数:Init
、Invoke
、Query
。每个链码程序都必须实现链码接口,如下所示:
type Chaincode interface {
Init(stub ChaincodeStubInterface) pb.Response
Invoke(stub ChaincodeStubInterface) pb.Response
}
Init()
在应用程序初始化其内部数据以供其他链码函数使用时被调用。当链码接收到实例化或升级事务时,它将被触发。
当应用程序客户端提出更新或查询事务时,将调用Invoke()
函数。
当链码查询链码状态时,将调用Query()
。Hyperledger Fabric 使用 LevelDB(键值存储)作为存储 world;state 数据的默认数据库。您可以使用键来获取当前账本状态数据。查询函数通过传入键值读取链码状态的值。
shim 包提供了用于链码访问其状态变量、事务上下文和调用其他链码的 API。
ChaincodeStubInterface
是其中一个重要的接口。它提供了各种函数,让您可以查询、更新和删除账本中的资产。这些函数如下所示:
GetState(key string) ([]byte, error) | GetState 函数从账本中返回指定键的值。 |
---|---|
PutState(key string, value []byte) error | PutState 将指定的键和值放入交易的写集作为数据写提案 |
DelState(key string) error | DelState 记录了交易提案的写集中将要删除的指定键 |
定义发行理赔
让我们写一段链码。打开 LiteIDE 并创建一个名为claimcontract.go
的新文件,如下所示:
在保险理赔使用案例分析中,我们分析了发行理赔流程中的参与者。我们需要定义一个链码的参与者有三个:被保险人、经纪人和保险人,如下例所示:
type Insuree struct {
Id string `json:"id"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
SSN string `json:"ssn"`
PolicyNumber string `json:"policyNumber"`
}
在Insuree
中,我们定义了Id
、firstname
、LastName
、SSN
和policyNumber
。
在 Go 语言中,字段名的第一个字母可以是大写或小写。当我们需要一个导出字段对任何代码使用时,它需要是一个大写字母。你可以使用 JSON 包中的编码将数据解析为结构体,定义 JSON 中的字段名为firstName
,如下所示:
type Member struct {
Name string `json:"member_name"`
}
经纪人和保险人的数据模型类似,只是类型不同。我们将其定义如下:
type Company struct {
Id string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
}
在发行理赔流程中,Insuree
初始化理赔请求。理赔文件将在区块链中跟踪流程的每一步。它记录了所有必要的信息,包括状态、用户理赔描述、insueeId
、brokerId
、insurerId
、每个步骤的处理时间以及授权方输入的评论,如下例所示:
type Claim struct {
Id string `json:"id"` //the fieldtags are needed to keep case from bouncing around
Desc string `json:"desc"` //claim description
Status string `json:"status"` //status of claim
InsureeId string `json:"insureeId"` //InsureeId
BrokerId string `json:"brokerId"` //BrokerId
InsurerId string `json:"insurerId"` //InsurerId
Comment string `json:"comment"` //comment
ProcessAt string `json:"processAt"` //processAt
}
初始化链码
接下来,我们将实现Init
函数。Init()
允许链码初始化被保险人数据以开始理赔请求。在我们的情况下,我们将设置并注册被保险人的个人信息,如下所示:
func (c *ClaimContract) Init(stub shim.ChaincodeStubInterface) pb.Response {
args := stub.GetStringArgs()
if len(args) != 5 {
return shim.Error("Incorrect arguments. Expecting a key and a value")
}
insureeId := args[0]
firstName := args[1]
lastName := args[2]
ssn := args[3]
policyNumber := args[4]
insureeData := Insuree{
Id: insureeId,
FirstName: firstName,
LastName: lastName,
SSN: ssn,
PolicyNumber: policyNumber}
insureeBytes, _ := json.Marshal(insureeData)
err := stub.PutState(insureeId, insureeBytes)
if err != nil {
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
}
return shim.Success(nil)
}
ChaincodeStubInterface.GetStringArg
获取输入参数。它期望参数的长度应为 5。拥有所有必需的保险人数据后,我们构建保险人 JSON 数据,并将其编码为 JSON 字节字符串–json.Marshal(insureeData)
。然后,我们将键和值存储在分类账上。如果一切顺利,它将返回一个成功的peer.Response
对象给 Fabric 的client.c
。
调用链码
要触发调用函数,可以调用链码应用函数的名称,并将shim.ChaincodeStubInterface
作为签名传递。在保险理赔案例中,我们定义了几个函数来支持我们的用例,例如:
AddCompany
, ReportLost
, RequestedInfo
, SubmitClaim
, ConfirmClaimSubmission
, ApproveClaim
.
我们还定义了一个查询来跟踪当前理赔请求和getHistory
来获取所有历史理赔交易记录,如下所示:
func (c *ClaimContract) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
if function == "AddCompany" {
return c.AddCompany(stub, args)
} else if function == "ReportLost" {
return c.ReportLost(stub, args)
} else if function == "RequestedInfo" {
return c.RequestedInfo(stub, args)
} else if function == "SubmitClaim" {
return c.SubmitClaim(stub, args)
} else if function == "ConfirmClaimSubmission" {
return c.ConfirmClaimSubmission(stub, args)
} else if function == "ApproveClaim" {
return c.ApproveClaim(stub, args)
} else if function == "query" {
return c.query(stub, args)
} else if function == "getHistory" {
return c.getHistory(stub, args)
}
return shim.Error("Invalid function name")
}
AddCompany
AddCompany
类似于我们在初始化步骤添加被保险人的方式。链码可以通过此函数注册经纪人和保险人。公司类型可以是经纪人或保险人,如下所示:
func (c *ClaimContract) AddCompany(stub shim.ChaincodeStubInterface, args []string) pb.Response {
id := args[0]
name := args[1]
companyType := args[2]
companyData := Company{
Id: id,
Type: companyType,
Name: name}
companyBytes, _ := json.Marshal(companyData)
stub.PutState(id, companyBytes)
return shim.Success(companyBytes)
}
报告丢失
在此步骤中,投保人向经纪人报告丢失物品,并提供所有索赔信息。此函数还在processAt
字段记录当前系统处理时间。currentts.Format(2006-01-02 15:04:05)
是一个 Go 自定义格式;它将当前时间转换为 YYYY-MM-dd hh:mm:ss 格式,如以下示例所示:
func (c *ClaimContract) ReportLost(stub shim.ChaincodeStubInterface, args []string) pb.Response {
claimId := args[0]
desc := args[1]
insureeId := args[2]
brokerId := args[3]
currentts := time.Now()
processAt := currentts.Format("2006-01-02 15:04:05")
//initialized claim
claimData := Claim{
Id: claimId,
Desc: desc,
Status: "ReportLost",
InsureeId: insureeId,
BrokerId: brokerId,
InsurerId: "",
Comment: "",
ProcessAt: processAt}
claimBytes, _ := json.Marshal(claimData)
stub.PutState(claimId, claimBytes)
return shim.Success(claimBytes)
}
RequestedInfo
投保人报告损失后,下一步是经纪人返回RequestedInfo
,如下所示:
func (c *ClaimContract) RequestedInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response {
return c.UpdateClaim(stub, args, "RequestedInfo")
}
func (c *ClaimContract) UpdateClaim(stub shim.ChaincodeStubInterface, args []string, currentStatus string) pb.Response {
claimId := args[0]
comment := args[1]
claimBytes, err := stub.GetState(claimId)
claim := Claim{}
err = json.Unmarshal(claimBytes, &claim)
if err != nil {
return shim.Error(err.Error())
}
if currentStatus == "RequestedInfo" && claim.Status != "ReportLost" {
claim.Status = "Error"
fmt.Printf("Claim is not initialized yet")
return shim.Error(err.Error())
} else if currentStatus == "SubmitClaim" && claim.Status != "RequestedInfo" {
claim.Status = "Error"
fmt.Printf("Claim must be in RequestedInfo status")
return shim.Error(err.Error())
} else if currentStatus == "ConfirmClaimSubmission" && claim.Status != "SubmitClaim" {
claim.Status = "Error"
fmt.Printf("Claim must be in Submit Claim status")
return shim.Error(err.Error())
} else if currentStatus == "ApproveClaim" && claim.Status != "ConfirmClaimSubmission" {
claim.Status = "Error"
fmt.Printf("Claim must be in Confirm Claim Submission status")
return shim.Error(err.Error())
}
claim.Comment = comment
if currentStatus == "RequestedInfo" {
insurerId := args[2]
claim.InsurerId = insurerId
}
currentts := time.Now()
claim.ProcessAt = currentts.Format("2006-01-02 15:04:05")
claim.Status = currentStatus
claimBytes0, _ := json.Marshal(claim)
err = stub.PutState(claimId, claimBytes0)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(claimBytes0)
}
由于剩余的流程函数非常相似,我们将UpdateClaim
定义为一个通用函数,与剩余步骤共享。
UpdateClaim
函数首先从输入参数中获取claimId
和当前参与者评论。然后,它查询并从区块链中获取索赔以解码索赔数据,并将其转换为 JSON 字符串—json.Unmarshal(claimBytes, &claim)
。
在更新索赔内容之前,它将验证输入的索赔状态并确保其处于预期步骤上。如果一切顺利,我们将更新索赔状态、参与者评论和处理时间。
最后,我们使用claimId
作为键在账本上更新索赔数据。
提交索赔,确认提交索赔,批准索赔
提交、确认和批准索赔与RequestedInfo
非常相似,并且这些步骤由UpdateClaim
函数调用。只有评论、状态和处理时间值不同。
查询
查询是您从分类帐中读取数据的方式。查询函数用于查询链码的状态。由于我们将索赔数据存储在以claimId
为键的分类帐中,为了读取当前索赔,我们调用GetState
,传递claimId
作为键,如下所示:
func (c *ClaimContract) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
var ENIITY string
var err error
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expected ENIITY Name")
}
ENIITY = args[0]
Avalbytes, err := stub.GetState(ENIITY) if err != nil {
jsonResp := "{\"Error\":\"Failed to get state for " + ENIITY + "\"}"
return shim.Error(jsonResp)
}
if Avalbytes == nil {
jsonResp := "{\"Error\":\"Nil order for " + ENIITY + "\"}"
return shim.Error(jsonResp)
}
return shim.Success(Avalbytes)
}
getHistory
正如其名称所示,gethistory
函数读取一个键的所有历史值记录的索赔,以及TxId
和索赔值。
首先我们定义了AuditHistory
结构,其中包含TxId
和值。GetHistoryForKey
通过resultsIterator
返回结果列表,其中包含所有历史交易记录。我们遍历这些记录并将它们添加到一个AuditHistory
数组中。后来,我们将其转换为 JSON 字节并作为响应发送回来,如下所示:
func (c *ClaimContract) getHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response {
type AuditHistory struct {
TxId string `json:"txId"`
Value Claim `json:"value"`
}
var history []AuditHistory
var claim Claim
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
claimId := args[0]
fmt.Printf("- start getHistoryForClaim: %s\n", claimId)
// Get History
resultsIterator, err := stub.GetHistoryForKey(claimId)
if err != nil {
return shim.Error(err.Error())
}
defer resultsIterator.Close()
for resultsIterator.HasNext() {
historyData, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
var tx AuditHistory
tx.TxId = historyData.TxId
json.Unmarshal(historyData.Value, &claim)
tx.Value = claim //copy claim over
history = append(history, tx) //add this tx to the list
}
fmt.Printf("- getHistoryForClaim returning:\n%s", history)
//change to array of bytes
historyAsBytes, _ := json.Marshal(history) //convert to array of bytes
return shim.Success(historyAsBytes)
}
这涵盖了我们的发行索赔链码。我们将在下一节学习有关 Hyperledger Fabric 配置的信息。
配置 Hyperledger Fabric
保险索赔网络中有三个实体—投保人、经纪人和保险公司。所有这些参与者都将在 Fabric 中注册为对等节点。以下表格描述了三个对等角色和 MSP 信息:
用户 ID | 角色 | 组织 MSP ID |
---|---|---|
user_001 | 投保人 | Org1MSP |
broker_001 | 经纪人 | Org2MSP |
insurer_001 | 保险公司 | Org3MSP |
我们有一个保险人加入了具有 MSP ID org1 的组织,一个经纪人加入了具有 MSP ID org2 的组织,以及一个保险人加入了具有 MSP ID org3 的组织。为了引导 fabric 网络,我们需要首先为我们需要运行的三个组件生成加密材料。
生成证书
我们需要定义 crypto-config.yaml
并使用 cryptogen 工具为每个节点生成证书。Cryptogen 可在工具镜像中获得。crypto-config.yaml
包含以下信息:
-
OrdererOrgs:管理排序节点的组织定义
-
PeerOrgs:管理对等节点的组织定义
OrdererOrgs 包含关于集群中排序节点的以下信息:
-
名称:订单者的名称
-
域:订单者的域 URL;在我们的案例中,它是 ic.com
-
主机名:订单者的主机名
这里是一个例子:
OrdererOrgs:
- Name: Orderer
Domain: ic.com
Specs:
- Hostname: orderer
PeerOrgs 包含关于集群中对等节点的以下信息:
-
名称:组织的名称;我们有三个不同的组织:
Org1
、Org2
和Org3
-
模板计数:组织的节点数
-
用户计数:组织的用户数
这里是一个例子:
PeerOrgs:
# ---------------------------------------------------------------------------
# Org1
# ---------------------------------------------------------------------------
- Name: Org1
Domain: org1.ic.com
Template:
Count: 2
Users:
Count: 1
# ---------------------------------------------------------------------------
# Org2
# ---------------------------------------------------------------------------
- Name: Org2
Domain: org2.ic.com
Template:
Count: 2
Users:
Count: 1
# ---------------------------------------------------------------------------
# Org3
# ---------------------------------------------------------------------------
- Name: Org3
Domain: org3.ic.com
Template:
Count: 2
Users:
Count: 1
以下是用于生成加密材料的命令:
cryptogen generate --config=./crypto-config.yaml
运行 cryptogen 工具后,您应该在控制台上看到以下输出:
生成订单者创世区块
生成证书后,该过程的下一步是生成订单者创世区块。configtxgen
命令允许用户创建和检查通道配置。configtxgen
工具的输出主要由 configtx.yaml
的内容控制,如下所示:
Profiles:
ICOrgsOrdererGenesis:
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Consortiums:
InsuranceClaimConsortium:
Organizations:
- *Org1
- *Org2
- *Org3
ICOrgsChannel:
Consortium: InsuranceClaimConsortium
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
- *Org2
- *Org3
Organizations:
- &OrdererOrg
Name: OrdererOrg
ID: OrdererMSP
MSPDir: crypto-config/ordererOrganizations/ic.com/msp
- &Org1
Name: Org1MSP
ID: Org1MSP
MSPDir: crypto-config/peerOrganizations/org1.ic.com/msp
AnchorPeers:
- Host: peer0.org1.ic.com
Port: 7051
- &Org2
Name: Org2MSP
ID: Org2MSP
MSPDir: crypto-config/peerOrganizations/org2.ic.com/msp
AnchorPeers:
- Host: peer0.org2.ic.com
Port: 7051
- &Org3
Name: Org3MSP
ID: Org3MSP
MSPDir: crypto-config/peerOrganizations/org3.ic.com/msp
AnchorPeers:
- Host: peer0.org3.ic.com
Port: 7051
Orderer: &OrdererDefaults
OrdererType: solo
Addresses:
- orderer.ic.com:7050
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 10
AbsoluteMaxBytes: 20 MB
PreferredMaxBytes: 512 KB
Kafka:
Brokers:
- 127.0.0.1:9092
Organizations:
Application: &ApplicationDefaults
Organizations:
我们在 configtx
文件的 Organizations
部分定义了三个组织;我们指定了每个组织的名称、ID
、MSPDir
和 AnchorPeers
。MSPDir
描述了 cryptogen 生成的输出 MSP 目录。AnchorPeers
指向节点的主机和端口。它更新事务以便在不同组织的节点之间启用通信,并找到通道的所有活动参与者,如下所示:
configtxgen -profile ICOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
类似以下的输出将显示在控制台上:
生成通道配置事务
configtxgen
通过执行通道配置事务将通道创建事务写入 channel.tx
,如下所示:
configtxgen -profile ICOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID icchannel
类似以下的输出将显示在控制台上:
执行通道配置事务的输出
Hyperledger Fabric Docker 组合器配置文件概述
Hyperledger Fabric 利用 Docker compose 来定义 fabric 应用服务。docker-compose-cli.yaml
服务部分是定义所有对等服务和相关容器的地方。Hyperledger Fabric 的 first-network 提供了一个 .yaml
模板,帮助您快速开始从头创建 yaml 文件:
github.com/hyperledger/fabric-samples/tree/release-1.2/first-network
。
在 docker-compose-cli.yaml
中,我们定义了以下信息:
-
networks
:定义区块链网络名称。在我们的案例中,它是icn
。 -
services
:定义所有对等服务和相关 Docker 容器 -
cli
:定义了用于替代 SDK 客户端的 Cli 容器,并设置了 Docker compose 命令行行为的环境变量
这是网络和服务部分的示例配置:
networks:
icn:
services:
orderer.ic.com:
extends:
file: base/docker-compose-base.yaml
service: orderer.ic.com
container_name: orderer.ic.com
networks:
- icn
peer0.org1.ic.com:
container_name: peer0.org1.ic.com
extends:
file: base/docker-compose-base.yaml
service: peer0.org1.ic.com
networks:
- icn
如您所见,有一个文件扩展目录:base/docker-compose-base.yaml
。Docker compose 支持使用 extends 字段为单个服务共享公共配置。我们稍后会详细讨论 docker-compose-base.yaml
。
这是 cli
部分的配置示例:
cli:
container_name: cli
image: hyperledger/fabric-tools
tty: true
environment:
- GOPATH=/opt/gopath
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_LOGGING_LEVEL=DEBUG
- CORE_PEER_ID=cli
- CORE_PEER_ADDRESS=peer0.org1.ic.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.ic.com/peers/peer0.org1.ic.com/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.ic.com/peers/peer0.org1.ic.com/tls/server.key
- CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.ic.com/peers/peer0.org1.ic.com/tls/ca.crt
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.ic.com/users/Admin@org1.ic.com/msp
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: /bin/bash -c './scripts/script.sh ${CHANNEL_NAME} ${DELAY}; sleep $TIMEOUT'
#for mapping the directories that are being used in the environment configurations
volumes:
- /var/run/:/host/var/run/
- ./chaincode/:/opt/gopath/src/github.com/chaincode
- ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
- ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
- ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
depends_on:
- orderer.ic.com
- peer0.org1.ic.com
- peer0.org2.ic.com
- peer0.org3.ic.com
networks:
- icn
docker-compose
工具使用 docker-compose-cli.yaml
文件来初始化 fabric 运行时环境。以下是在使用 docker-compose-cli.yaml
文件时您将使用的一些最常见的命令:
TTY | TTY 基本上意味着 控制台,我们将其设置为 true。 |
---|---|
Image | 指向 fabric-tools 图像目录。 |
Environment | 指定环境变量,例如 GOPATH,由 cryptogen 工具生成的与 TLS 相关的文件位置。 |
working_dir | 设置了对等体的工作目录。 |
command | 指定容器启动时发出的命令。 |
volumes | 映射了在环境配置中使用的目录。 |
depends_on | 按依赖顺序启动服务。 |
然后生成四个 fabric-peer 事务节点容器,一个 fabric-order 订单节点容器和一个 fabric-tools cli 容器。
Fabric 项目目录结构
在我们的 Fabric 示例 first-network 中,项目结构类似于以下内容:
正如我们之前讨论的,docker-compose-cli.yaml
服务是从 base/docker-compose-base.yaml
继承的。有两个文件基目录:peer-base.yaml
和 docker-compose-base.yaml
。
Docker-compose-base.yaml
此文件包含基本配置,包括每个对等体和订单容器的环境和端口号。这定义了保险索赔网络的整体拓扑,如下:
services:
orderer.ic.com:
container_name: orderer.ic.com
image: hyperledger/fabric-orderer
environment:
- ORDERER_GENERAL_LOGLEVEL=debug
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_GENESISMETHOD=file
- ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
- ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
# enabled TLS
- ORDERER_GENERAL_TLS_ENABLED=true
- ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
- ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
- ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
command: orderer
volumes:
- ../channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
- ../crypto-config/ordererOrganizations/ic.com/orderers/orderer.ic.com/msp:/var/hyperledger/orderer/msp
- ../crypto-config/ordererOrganizations/ic.com/orderers/orderer.ic.com/tls/:/var/hyperledger/orderer/tls
ports:
- 7050:7050
peer0.org1.ic.com:
container_name: peer0.org1.ic.com
extends:
file: peer-base.yaml
service: peer-base
environment:
- CORE_PEER_ID=peer0.org1.ic.com
- CORE_PEER_ADDRESS=peer0.org1.ic.com:7051
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.ic.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
volumes:
- /var/run/:/host/var/run/
- ../crypto-config/peerOrganizations/org1.ic.com/peers/peer0.org1.ic.com/msp:/etc/hyperledger/fabric/msp
- ../crypto-config/peerOrganizations/org1.ic.com/peers/peer0.org1.ic.com/tls:/etc/hyperledger/fabric/tls
ports:
- 7051:7051
- 7053:7053
…..
Peer-base.yaml
这个文件为保险索赔 docker-compose-base.yaml
定义了对等网络配置,如下:
services:
peer-base:
image: hyperledger/fabric-peer
environment:
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_icn
- CORE_LOGGING_LEVEL=DEBUG
- CORE_PEER_TLS_ENABLED=true
- CORE_PEER_GOSSIP_USELEADERELECTION=true
- CORE_PEER_GOSSIP_ORGLEADER=false
- CORE_PEER_PROFILE_ENABLED=true
- CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt
- CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
- CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: peer node start
在对等体中的命令让对等体安装系统链码和其他配置。
我们了解了关键的 Hyperledger Fabric 配置文件的概述,所以让我们使用以下代码启动我们的保险索赔网络:
- CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key
- CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
command: peer node start
启动 Hyperledger Fabric 网络
现在,是时候启动我们的 Hyperledger Fabric 网络了。我们将使用 Docker 命令来初始化新的 Docker 组合:
docker-compose -f docker-compose-cli.yaml up
Docker 容器将触发在 docker-compose-cli.yaml
中定义的命令,如下所示:
command: /bin/bash -c './scripts/script.sh
script.sh
是一个包含一系列用于部署和测试命令的脚本。我们还在 utils.sh
中定义了一些业务特定的 shell 脚本函数。
创建一个通道
首先,我们需要创建一个通道以构建创世块。运行以下命令:
peer channel create -o orderer.ic.com:7050 -c icchannel -f ./channel-artifacts/channel.tx
这个命令从 channel.tx
中读取一个创世块,然后用于加入通道并创建 icchannel
通道。这是控制台上的结果:
控制台的输出,加入并创建通道
加入通道
在订购服务创建通道之后,我们可以将对等节点添加到通道中,如下所示:
peer channel join -b icchannel.block
这是控制台上的结果:
将对等节点添加到通道中
我们可以看到 peer0.org1
、peer0.org2
和 peer0.org3
加入了通道。
更新锚点
在我们开始与我们的发行索赔网络进行交互之前,我们需要完成的最后一个操作是更新锚定对等点。锚定对等点接收并广播事务更新给组织中的其他对等点。锚定对等点在网络中是可搜索的。因此,任何注册为锚定对等点的对等点都可以被订购对等点或任何其他对等点发现,例如:
peer channel update -f ./channel-artifacts/Org1MSPanchors.tx -c icchannel -o orderer.ic.com:7050 --tls true --cafile $ORDERER_CA
这是此步骤的控制台输出:
被订购对等节点或任何其他对等节点发现
安装链码
在之前的步骤之后,我们几乎可以使用我们的发行索赔区块链应用程序了。但是首先,我们需要在我们的网络上安装 claimcontract.go
链码,如下所示:
peer chaincode install -n iccc -v 1.0 -l golang -p github.com/chaincode/claimcontract
我们将看到前述命令的输出:
安装链码到我们的网络
实例化链码
在安装链码之后,我们需要实例化它。正如我们之前讨论的,我们将在 init()
链码中引入被保人。因此,我们需要传递必需的参数来创建一个被保人参与者,如下所示:
peer chaincode instantiate -o orderer.ic.com:7050 -C icchannel -n iccc -l golang -v 1.0 -c '{"Args":[ "user_001","John","Smith", "9999","4394497111/1"]}' -P "OR ('Org1MSP.member'
这是此步骤的输出:
创建一个被保人参与者
我们查询被保人以验证记录是否已在区块链中创建,如下所示:
peer chaincode query -C $CHANNEL_NAME -n iccc -c '{"Args":["query","user_001"]}'
从这个输出中我们可以看到,被保人(user_001
)已经被添加到我们的区块链中:
被保人添加到我们的区块链中
调用添加经纪人
让我们将一个经纪人加入到我们的保险索赔区块链中,如下所示:
peer chaincode invoke -o orderer.ic.com:7050 -C icchannel -n iccc -c '{"Args":["AddCompany","broker_001","BROKER","BNC Brokerage"]}'
这是结果:
将经纪人引入我们的保险索赔区块链
调用添加保险商
将最后一方保险商添加到保险索赔区块链中,如下所示:
peer chaincode invoke -o orderer.ic.com:7050 -C icchannel -n iccc -c '{"Args":["AddCompany","insurer_001","INSURER","Western Insurance"]}'
显示的输出如下:
将最后一方保险商添加到保险索赔区块链中
调用 ReportLost
所有参与者都已加入网络,现在是开始保险索赔流程的时候了。被保险人向经纪人报告索赔,以下是调用’ReportLost’链代码的命令。
peer chaincode invoke -o orderer.ic.com:7050 -C icchannel -n iccc -c '{"Args":["ReportLost","claim_001", "I was in Destiny shopping center and lost my IPhone 8", "user_001", "broker_001"]}
将显示以下输出:
被保险人向经纪人报告索赔
调用 RequestedInfo
经纪人提供请求的信息,如下所示:
peer chaincode invoke -o orderer.ic.com:7050 -C icchannel -n iccc -c '{"Args":["RequestedInfo","claim_001", "Broker processsed user John Smith report and sent Requested Info to user.", "insurer_001"]}'
将显示以下输出:
提供所请求的信息
调用 SubmitClaim
经纪人向发行人提交索赔,如下所示:
peer chaincode invoke -o orderer.ic.com:7050 -C icchannel -n iccc -c '{"Args":["SubmitClaim","claim_001", "Broker submitted a claim"]}'
将显示以下输出:
向发行人提交索赔
调用 ConfirmClaimSubmission
发行人确认索赔,如下所示:
peer chaincode invoke -o orderer.ic.com:7050 -C icchannel -n iccc -c '{"Args":["ConfirmClaimSubmission","claim_001", "Insurer received and confirmed a claim"]}'
将显示以下输出:
确认索赔
调用 ApproveClaim
发行人处理并批准索赔,如下所示:
peer chaincode invoke -o orderer.ic.com:7050 -C icchannel -n iccc -c '{"Args":["ApproveClaim","claim_001", "Insurer processed and approved the claim."]}'
将显示以下输出:
处理并批准索赔
查询索赔历史
在发行人批准索赔后,整个流程完成,我们可以使用 Fabric API 查询索赔的整个生命周期,如下所示:
peer chaincode query -C icchannel -n iccc -c '{"Args":["getHistory","claim_001"]}'
从此查询获得的输出中,我们可以看到索赔请求的整个 Fabric 交易历史。
测试执行结束。
端到端测试执行
我们已经完成了保险索赔流程的每一步。为了简化整个端到端应用程序流程,您可以导航到insurance-claim
文件夹,然后运行以下命令:
cd ~/insurance-claim
#change path if insurance-claim directory is different
export PATH=/home/ubuntu/insurance-claim/bin:$PATH
./icn.sh -m up
输出结果如下:
简化整个端到端应用程序流程
最终输出如下:
保险索赔端到端测试完成
摘要
在本章中,我们学习了 Hyperledger Fabric 的基础知识。在设置开发环境之后,我们为保险索赔用例编写了链代码。然后,我们学习了面料组合器配置。最后,我们对我们的保险索赔应用程序进行了端到端的面料测试执行。我们可以看到,使用 Hyperledger Fabric 实现保险索赔应用程序相当复杂。在下一章中,我们将学习如何使用 Hyperledger Composer 快速编写保险索赔应用程序。
第十七章:使用 Hyperledger Composer 实现业务网络
Hyperledger Composer 是一个高级工具集和框架,旨在快速构建和运行基于 Hyperledger Fabric 区块链的应用程序。
在前一章中我们了解了 Hyperledger Fabric,所以你已经知道开发基于 Fabric 的应用程序相当复杂,因为它需要处理许多网络级别的配置。
在本章中,我们将讨论以下主题:
-
Hyperledger Composer — 一个快速概述
-
设置 Hyperledger Composer 环境
-
分析业务场景
-
业务网络存档
-
实现业务交易功能
Hyperledger Composer — 一个快速概述
Hyperledger Composer 是一组基于 JavaScript 的高级工具集和框架,可以简化并快速构建和运行基于 Hyperledger Fabric 区块链的应用程序。业务所有者和开发人员可以通过 composer 工具快速创建智能合约和应用程序。Composer 工具生成一个 RESTful 端点,用于与 Fabric 通道交互。与使用 Golang 编写链码不同,Composer 使用模型语言为区块链网络生成业务网络存档(.BNA
)文件。
这是一个 Hyperledger Composer 解决方案架构的示例:
Hyperledger Composer 包含以下部分。
Yeoman 生成器
Yeoman 中的 npm 模块 generator-hyperledger-composer 用于创建 Hyperledger Composer 的模板。它支持并生成三种不同类型的模板:
-
CLI 应用程序
-
Angular 2 应用程序
-
骨架业务网络
你可以使用 Yeoman 生成的 angular 骨架连接到 Hyperledger Composer REST 服务器。
Composer REST 服务器
Composer 的 REST 服务器利用一个独立的 Node.js 进程,并从部署的 composer 业务网络公开一组 RESTful API 端点。这些生成的 API 可以与 fabric 链码进行交互。侧面的代码然后可以触发 创建、读取、更新、删除(CRUD)资产、参与者和交易。
LoopBack 连接器
LoopBack 连接器利用 Node.js 的 LoopBack 框架来为业务网络中定义的资产、参与者和交易暴露 GET/POST/PUT/DELETE 操作。
JavaScript SDK
JavaScript SDK API 用于与部署的业务网络交互。它由客户端和管理 API 组成。
客户端 API 提供了从客户端应用程序查询、创建、更新和删除资源(资产和参与者),以及提交交易的功能。
管理 API 用于部署业务网络。
Composer playground
Hyperledger Composer playground 是一个基于浏览器的界面,用于创建和测试业务网络。你可以使用 playground 来构建和测试你的业务网络。
Composer-cli
Composer-cli 是一个命令行工具,可以让你部署和管理业务网络。
以下是一些命令的列表:
命令 | 描述 |
---|---|
composer archive create | 创建业务网络存档文件(nba)的命令。 |
composer archive list | 验证业务网络存档的内容。 |
composer card create | 从个别组件创建业务网络卡。 |
composer card delete | 从个别组件中删除业务网络卡。 |
composer card list | 列出存储在本地钱包中的所有业务网络卡。 |
composer network deploy | 将业务网络存档从本地磁盘部署到 Hyperledger Fabric 网络。 |
composer network list | 列出业务网络卡的详细信息。 |
composer network ping | 测试已部署业务网络的连接。 |
设置 Hyperledger Composer 环境
我们刚刚审查了 Hyperledger Composer 解决方案体系结构。在本节中,我们将设置 Hyperledger 开发环境。
安装先决条件
在安装 composer 工具之前,请确保按照 Hyperledger Fabric 环境设置 - 安装先决条件一节来获取所需的先决条件。
安装开发环境
以下是开发环境安装命令:
- 安装 CLI 工具:
npm install -g composer-cli@0.20
- 安装
composer-rest-server
:
npm install -g composer-rest-server@0.20
- 安装 Hyperledger Composer 生成器:
npm install -g generator-hyperledger-composer@0.20
- 安装 Yeoman:
npm install -g yo
- 安装游乐场:
npm install -g composer-playground
- 安装 fabric 运行时:
下载并安装 composer 的 fabric 运行时如下:
mkdir ~/fabric-devserver && cd ~/fabric-devserver
curl -O https://raw.githubusercontent.com/hyperledger/composer- tools/master/packages/fabric-dev-servers/fabric-dev-servers.zip
unzip fabric-dev-servers.zip
export FABRIC_VERSION=hlfv12
./downloadFabric.sh
在这一步,你已经安装了典型的 composer 开发环境所需的一切。
分析业务场景
在第十六章,使用 Hyperledger Fabric 探索企业区块链应用程序中,我们讨论了对保险索赔的区块链用例。它包括以下步骤:
-
保险人向经纪人报告索赔
-
经纪人提供请求的信息
-
经纪人向发行者提交索赔
-
发行者确认索赔
-
发行者处理并批准索赔
在本章中,我们将使用相同的保险索赔用例,但也通过 Hyperledger Composer 构建端到端应用程序。
业务网络存档
Composer 业务由四种不同类型的文件组成:模型文件(.cto)、脚本文件(.js)、访问控制列表(ACL)文件(.acl)和查询文件(.qry)。
网络模型文件(.cto)
CTO 文件由以下元素组成:
元素 | 描述 |
---|---|
单个命名空间 | 定义 composer 模型命名空间;每个.cto 模型文件都需要一个命名空间。 |
资源 - 资产 | 可以在各方之间交换的任何有价值的东西。 |
资源 - 参与者 | 业务网络成员。 |
资源 - 枚举 | 由一组命名值组成的数据类型。 |
资源 - 概念 | 你想要建模的任何对象,而不是其他类型的对象。 |
资源 - 交易 | 定义区块链业务逻辑。 |
Resources - events | 区块链事务通知。 |
Import | 从其他命名空间导入资源。 |
Composer 模型语言,就像其他编程语言一样,具有包括 String、Double、Integer 等数据类型。
让我们看一些资产、参与者、交易和事件的示例。
IBM Bluemix 提供了一个无需安装的浏览器版本 Playground;我们可以使用这个工具快速进行原型设计。这是链接:composer-playground.mybluemix.net/
。
连接到基本样本网络。 Playground 将为您生成一些默认的示例资产、参与者、交易和事件,例如:
sample.cto
/**
* Sample business network definition.
*/
namespace org.example.basic
asset SampleAsset identified by assetId {
o String assetId
--> SampleParticipant owner
o String value
}
participant SampleParticipant identified by participantId {
o String participantId
o String firstName
o String lastName
}
transaction SampleTransaction {
--> SampleAsset asset
o String newValue
}
event SampleEvent {
--> SampleAsset asset
o String oldValue
o String newValue
}
在 sample.cto.SampleAsset
中定义了一个 namespace org.example.basic
,这是一个 Asset
类的示例。它定义了一个资产,其名称后跟着一个标识性的 field.o
String assetId
:SampleAsset
的一个字段。--> SampleParticipant owner
:字段指向 SampleParticipant
instance.SampleParticipant
是 Participant
类的一个示例,语法与 SampleAsset.SampleTransaction
是一个事务的示例 class.SampleEvent
是一个事件类的示例。
脚本文件 (.js)
我们在模型文件中定义了交易和事件,脚本文件实现了这些交易功能。注释中的装饰器用于用于事务处理所需的元数据注释函数,例如:
/**
* Sample transaction processor function.
* @param {org.example.basic.SampleTransaction} tx The sample transaction instance.
* @transaction
*/
async function sampleTransaction(tx) { // eslint-disable-line no-unused-vars
..
emit(event);
}
在 sampleTransaction
函数中,@param
标签后跟着触发事务处理器函数的事务的资源名称。 @transaction
将此函数标记为事务处理器函数。
访问控制列表 (ACL) 文件 (.acl)
ACL 文件定义了业务网络中参与者的权限,例如:
rule OwnerHasFullAccessToTheirAssets {
description: "Allow all participants full access to their assets"
participant(p): "org.example.basic.SampleParticipant"
operation: ALL
resource(r): "org.example.basic.SampleAsset"
condition: (r.owner.getIdentifier() === p.getIdentifier())
action: ALLOW
}
在前面的 ACL 示例中,指定参与者为 SampleParticipant
。任何注册为 SampleParticipant
的实例都可以对所有的 org.example.SampleAsset
实例执行 ALL
操作。此事务在 SampleAsset
的所有者与提交事务的参与者相同时触发。
查询文件 (.qry)
查询文件定义了用于返回关于区块链世界状态的数据的查询。查询语法与 SQL 语言非常相似,例如:
query queryName {
description: "Select SampleAsset by assetId "
statement:
SELECT org.example.basic.SampleAsset
WHERE (_$assetId = assetId)
}
设计业务模型
现在我们已经审查了基本的 Composer 模型语言和结构,是时候使用 Hyperledger Composer 实现一个保险理赔了。
为简单起见,我们将允许参与者在此示例中有权限读取和写入所有资源。删除与 ACL 相关的示例资源,并更新规则如下:
rule EverybodyCanReadEverything {
description: "Allow all participants read access to all resources"
participant: "**"
operation: READ
resource: "com.packt.quickstart.claim.*"
action: ALLOW
}
rule EverybodyCanSubmitTransactions {
description: "Allow all participants to submit transactions"
participant: "**"
operation: CREATE
resource: "**"
action: ALLOW
}
简化后的 ACL,我们开始按照以下方式处理我们的模型文件:
-
将
sample.cto
重命名为insurance-claim.cto
-
将命名空间更改为
com.packt.quickstart.claim
并删除其余代码 -
定义参与者和资产
我们在第十六章中编写了一个名为claimcontract.go
的链码,使用 Hyperledger Fabric 探索企业区块链应用程序,该链码定义了被保险人、经纪人、保险人和索赔的结构。我们可以类似于这个结构定义参与者和资产。如下所示,这非常简单:
namespace com.packt.quickstart.claim
participant Insuree identified by id {
o String id
o String firstName
o String lastName
o String ssn
o String policyNumber
}
participant Company identified by id {
o String id
o String type
o String name
}
asset Claim identified by id {
o String id
o String desc
o Integer status
o String insureeId
o String brokerId
o String insurerId
o String comment
o String processAt
}
- 定义交易和事件。通过使用
Init
函数,我们登记被保险人,如下所示:
transaction Init {
o String insureeId
o String firstName
o String lastName
o String ssn
o String policyNumber
}
event InitEvent {
--> Insuree insuree
}
-
Composer 的 JavaScript API 提供了用于创建资源(包括参与者)的 CRUD。对于保险人和经纪人,我们将使用这种方法。我们在进行测试时会更详细地解释这一点。
-
定义
ReportLost
:被保险人向经纪人报告索赔—这启动了一个索赔,如下所示:
transaction ReportLost {
o String claimId
o String desc
o String insureeId
o String brokerId
}
event ReportLostEvent {
--> Claim claim
}
- 定义
RequestedInfo
:经纪人提供请求的信息,如下所示:
transaction RequestedInfo {
--> Claim claim
}
event RequestedInfoEvent {
--> Claim claim
}
-
定义
SubmitClaim
:经纪人向发行人提交索赔。 -
定义
ConfirmClaimSubmission
:发行人确认索赔。 -
定义
ApproveClaim
:发行人处理并批准索赔。
步骤 8、9 和 10 是交易函数,与步骤 7 非常相似。
我们在模型文件中定义了所有的交易、参与者和资产。作为下一步,我们将实现模型文件中定义的交易。
实现业务交易函数
我们通过审查SampleTransaction
在前一节学习了如何实现交易函数。按照类似的方法,我们将实现一个保险索赔交易函数。将sample.js
重命名为logic.js
。
实现Init
函数,如下所示:
Init() function is used to register insuree person information.
/**
* Create the insuree
* @param {com.packt.quickstart.claim.Init} initalAppliation - the InitialApplication transaction
* @transaction
*/
async function Init(application) { // eslint-disable-line no-unused-vars
const factory = getFactory();
const namespace = 'com.packt.quickstart.claim';
const insuree = factory.newResource(namespace, 'Insuree', application.insureeId);
insuree.firstName = application.firstName;; insuree.lastName = application.lastName
insuree.ssn = application.ssn;;
insuree.policyNumber = application.policyNumber;;
const participantRegistry = await
getParticipantRegistry(insuree.getFullyQualifiedType());
await participantRegistry.add(insuree);
// emit event
const initEventEvent = factory.newEvent(namespace, 'InitEvent');
initEventEvent.insuree = insuree;
emit(initEventEvent);
}
实现ReportLost
,设置并创建索赔,如下所示:
/**
* insuree report lost item
* @param {com.packt.quickstart.claim.ReportLost} ReportLost - the ReportLost transaction
* @transaction
*/
async function ReportLost(request) {
const factory = getFactory();
const namespace = 'com.packt.quickstart.claim';
let claimId = request.claimId;
let desc = request.desc;
let insureeId = request.insureeId;
let brokerId = request.brokerId;
const claim = factory.newResource(namespace, 'Claim', claimId);
claim.desc = desc;
claim.status = "ReportLost";
claim.insureeId = insureeId;
claim.brokerId = brokerId;
claim.insurerId = "";
claim.comment = "";
claim.processAt = (new Date()).toString();
const claimRegistry = await getAssetRegistry(claim.getFullyQualifiedType());
await claimRegistry.add(claim);
// emit event
const reportLostEvent = factory.newEvent(namespace, 'ReportLostEvent');
reportLostEvent.claim = claim;
emit(reportLostEvent); }
实现RequestedInfo
以验证和更新索赔状态,如下所示:
/**
* broker send Requested Info to insuree
* @param {com.packt.quickstart.claim.RequestedInfo} RequestedInfo - the RequestedInfo transaction
* @transaction
*/
async function RequestedInfo(request) { // eslint-disable-line no-unused-vars
const factory = getFactory();
const namespace = 'com.packt.quickstart.claim';
let claim = request.claim;
if (claim.status !== 'ReportLost') {
throw new Error ('This claim should be in ReportLost status');
}
claim.status = 'RequestedInfo';
claim.processAt = (new Date()).toString();
const assetRegistry = await getAssetRegistry(request.claim.getFullyQualifiedType());
await assetRegistry.update(claim);
// emit event
const requestedInfoEventEvent = factory.newEvent(namespace, 'RequestedInfoEvent');
requestedInfoEventEvent.claim = claim;
emit(requestedInfoEventEvent); }
实现SubmitClaim
、ConfirmClaimSubmission
和ApproveClaim
。这些功能与RequestedInfo
类似。
在游乐场进行测试
我们刚刚在前一节中实现了所有的模型和逻辑文件,所以现在是测试我们的 composer 应用程序的时候了:
-
单击游乐场左下角的部署更改按钮。这将部署 composer 代码。
-
点击顶部导航栏上的测试链接。将弹出提交交易页面。从交易类型下拉菜单中选择
init
方法。输入 JSON 值,如下截图所示;输入数据与我们在第十六章中测试的相同,使用 Hyperledger Fabric 探索企业区块链应用程序。实例化面料链码步骤。提交交易,如下所示:
如果交易提交成功,我们将能够看到被保险人参与者已创建,例如:
- 现在,让我们入职经纪人和保险人。在参与者部分中点击公司,然后点击创建新参与者。输入经纪人数据,方式与我们在第十六章中的
chaincodeInvokeAddBroker
步骤相同,使用 Hyperledger Fabric 探索企业区块链应用程序。点击创建新,如下所示:
如果交易提交成功,这将入职经纪人。重复相同步骤以入职保险人,如下所示:
- 提交
ReportLost
,如下所示:
这是结果:
- 用以下结果测试
RequestedInfo
:
剩下的SubmitClaim
,ConfirmClaimSubmission
和ApproveClaim
步骤与RequestedInfo
非常相似。
部署业务网络
我们在游乐场中测试了 composer 应用程序,接下来我们将把它部署到区块链上:
-
创建一个名为
insurance-claim-network
的文件夹,并导航到该文件夹。 -
生成业务网络项目模板,如下所示:
yo hyperledger-composer:businessnetwork
它会提示几个问题。输入insurance-claim-network
作为网络名称,并选择空模板网络,如下截图所示:
这将生成一些具有默认模板的文件。替换com.packt.quickstart
的内容,如下所示:
用我们之前测试过的模型文件.claim.cto
。
创建一个名为lib
的新文件夹,在lib
文件夹下,将测试过的logic.js
复制到这里。
用被测试过的acl
文件替换permissions.acl
,如下所示:
- 启动 Hyperledger Fabric,如下所示:
cd ~/fabric-devservers
export FABRIC_VERSION=hlfv12
./startFabric.sh
./createPeerAdminCard.sh
这将创建PeerAdminCard
,如下截图所示:
- 生成业务网络档案。从
insurance-claim-network
目录中运行以下命令:
composer archive create -t dir -n
这将生成insurance-claim-network@0.0.1.bna
。
- 安装业务网络。从
insurance-claim-network
目录中运行以下命令:
composer network install --card PeerAdmin@hlfv1 --archiveFile
insurance-claim-network@0.0.1.bna
- 启动业务网络。从
insurance-claim-network
目录中运行以下命令:
composer network start --networkName insurance-claim-network --
networkVersion 0.0.1 --networkAdmin admin --networkAdminEnrollSecret
adminpw --card PeerAdmin@hlfv1 --file networkadmin.card
- 导入网络管理员卡。从
insurance-claim-network
目录中运行以下命令。这将会将insurance-claim-network
导入到网络中:
composer card import --file networkadmin.card
- 检查业务网络是否已成功部署。从
insurance-claim-network
目录中运行以下命令:
composer network ping --card admin@insurance-claim-network
结果应如下所示:
检查业务网络是否已成功部署
与 REST 服务器集成
我们刚刚在 fabric 网络中部署了insurance-claim-network
。下一步是构建一个保险索赔客户端 API,与网络中的智能合约函数进行交互。Hyperledger Composer REST 服务器可用于生成 REST API。REST 客户端可以调用这些端点函数,并与 Fabric 区块链上的业务网络链码交互。
生成 Hyperledger Composer REST API
运行以下命令生成 composer 服务器 API:
composer-rest-server
从业务网络卡中输入admin@insurance-claim-network
,如下截图所示:
输入业务网络卡
这将生成 REST API,将其暴露为http://serverIP:3000
和http://serverIP:3000/explorer
。
打开浏览 URL。你会看到生成的 REST 端点,如下所示:
让我们尝试多种方法来演示这些端点是如何与 fabric 网络进行交互。
从端点中选择init Post
方法,并提供 post JSON 数据,然后点击 Try it out! 按钮。JSON 数据示例如下所示:
{
"$class": "com.packt.quickstart.claim.Init",
"insureeId": "user-001",
"firstName": "John",
"lastName": "Smith",
"ssn": "9999",
"policyNumber": "string"
}
这是点击 Try it out! 按钮后显示的结果截图:
JSON 数据示例
API 将调用 fabric 网络中的Init
链码,并将响应返回给浏览器。
使用 post 方法选择一个公司来创建被保险人。输入以下 JSON 请求如下:
{
"$class": "com.packt.quickstart.claim.Init",
"insureeId": "user-001",
"firstName": "John",
"lastName": "Smith",
"ssn": "9999",
"policyNumber": "string"
}
你应该会看到一个成功的返回,类似于下面截图中展示的:
使用 post 方法选择一个公司来创建被保险人,输入以下 JSON 请求
从端点中选择ReportLost Post
方法,并提供 post JSON 数据,然后点击Try it out!:
{
"$class": "com.packt.quickstart.claim.ReportLost",
"claimId": "claim_001",
"desc": "I was in Destiny shopping center and lost my IPhone 8",
"insureeId": "user_001",
"brokerId": "broker_001"
}
您应该会从区块链中得到一个成功的响应。
要验证索赔是否成功在网络中创建,您可以选择索赔 get 方法并点击 Try it out! 您应该能够获取到索赔结果,如下所示:
验证网络中索赔是否成功创建
其他保险索赔端点 API 将会和我们探索过的很相似。
总结
我们已经到达本章末尾。在这一章中,我们概述了 Hyperledger Composer 并安装了相关工具。我们使用 composer 模型语言开发了同样的保险索赔用例,就像第十六章中所探索过的使用 Hyperledger Fabric 探索企业区块链应用,并将其部署到 fabric 网络中。最后,我们将应用与 composer REST 服务器集成,生成客户端 API,并从 web 中与这些 API 进行交互。
到这一步,你应该已经熟悉了 Hyperledger Composer 的工作方式。现在我们已经到了本章的结尾,我们已经了解了两种最流行的公共和企业级区块链。作为一名区块链开发者,你应该具备基本的区块链知识,以便能够编写你自己的区块链应用程序。在下一章中,我们将讨论各种真实世界的区块链使用案例。