利用Hyperledger Fabric开发你的第一个区块链应用

原文地址:www.xuanzhangjiong.top/2019/04/16/…javascript

做者:TopJohnhtml

利用Hyperledger Fabric开发你的第一个区块链应用

本文示例源于fabric-samples中的fabcar github.com/hyperledger…java

在这个例子中,咱们经过一个简单的示例程序来了解Fabric应用是如何运行的。在这个例子中使用的应用程序和智能合约(链码)统称为FabCar。这个例子很好地提供了一个开始用于理解Hyperledger Fabric。在这里,你将学会如何开发一个应用程序和智能合约来查询和更新帐本,如何利用CA来生成一个应用程序须要的用于和区块链交互的X.509证书。node

咱们使用应用程序SDk来执行智能合约中的查询更新帐本的操做,这些操做在智能合约中借助底层接口实现。git

咱们将经过3个步骤来进行讲解:github

  1. 搭建开发环境。咱们的应用程序须要和网络交互,所以咱们须要一个智能合约和应用程序使用的基础网络。

  1. 学习一个简单的智能合约,FabCar。咱们使用JavaScript开发智能合约。咱们经过查看智能合约来学习应用程序如何使用智能合约发送交易,如何使用智能合约来查询和更新帐本。docker

  2. 使用FabCar开发一个简单的应用程序。咱们的应用程序会使用FabCar智能合约来查询及更新帐本上的汽车资产。咱们将进入应用程序的代码中去了解如何建立交易,包括查询一辆汽车的信息,查询一批汽车的信息以及建立一辆汽车。typescript

设置区块链网络

注意:下面的部分须要进入你克隆到本地的fabric-samples仓库的first-network子目录。shell

若是你已经学习了Building Your First Network,你应该已经下载了fabric-samples并且已经运行起了一个网络。在你进行本教程以前,你须要中止这个网络:数据库

./byfn.sh down
复制代码

若是你以前运行过这个教程,使用下面的命令关掉全部中止或者运行的容器。注意,这将关掉全部的容器,不管是否和Fabric有关。

docker rm -f $(docker ps -aq)
docker rmi -f $(docker images | grep fabcar | awk '{print $3}')
复制代码

若是你没有这个网络和应用相关的开发环境和构件,请访问 Prerequisites页面,确保你的机器安装了必要的依赖。

接下来,若是你尚未这样作的话,请浏览 Install Samples, Binaries and Docker Images页面,跟着上面的操做进行。当你克隆了fabric-samples仓库,下载了最新的稳定版Fabric镜像和相关工具以后回到教程。

若是你使用的是Mac OS和Mojava,你须要安装Xcode

启动网络

下面的部分须要进入fabric-samples仓库的fabcar子目录。

使用startFabric.sh来启动你的网络。这个命令将启动一个区块链网络,这个网络由peer节点、排序节点、证书受权服务等组成。同时也将安装和初始化javascript版本的FabCar智能合约,咱们的应用程序将经过它来操做帐本。咱们将经过本教程学习更过关于这些组件的内容。

./startFabric.sh javascript
复制代码

如今,咱们已经运行起来了一个示例网络,还安装和初始化了FabCar智能合约。为了运行咱们的应用程序,咱们须要安装一些依赖,同时让咱们看一下它们是如何工做的。

安装应用程序

注意:下边的章节须要进入你克隆到本地的fabric-samples仓库的fabcar/javascript子目录。 下面的命令来安装应用程序所需的Fabric有关的依赖。大概将话费1分钟左右的时间:

npm install
复制代码

这个指令用于安装应用程序所需的依赖,这些依赖被定义在package.json中。其中最重要的是fabric-network类;它使得应用程序可使用身份、钱包和链接到通道的网关,以及提交交易和等待通知。本教程也将使用fabric-ca-client类来注册用户以及他们的受权证书,生成一个fabric-network使用的合法的身份。

一旦npm install执行成功,运行应用程序所需的一切就准备好了。在这个教程中,你将主要使用fabcar/javascript目录下的JavaScript文件来操做应用程序。让咱们来了解一下里面有哪些文件:

ls
复制代码

你将看到下列文件:

enrollAdmin.js  node_modules       package.json  registerUser.js
invoke.js       package-lock.json  query.js      wallet
复制代码

里面也有一些其余编程语言的文件,好比fabcar/typescript目录中。当你使用过JavaScript示例以后-其实都是相似的。

若是你在使用Mac OS并且运行的是Mojava你须要[安装Xcode](https://hyperledger-fabric.readthedocs.io/en/latest/tutorial/installxcode.html)

登记管理员用户

下面的部分涉及执行和CA服务器通信的过程。你在执行下面的程序的时候,打开一个终端执行docker logs -f ca.example.com来查看CA的日志,会是十分有帮助的。

当咱们建立网络的时候,一个叫admin的用户已经被受权服务器(CA)建立为登记员。咱们第一步要作的是使用enroll.js程序为admin生成私钥,公钥和x.509证书。这个程序使用一个证书签名请求 (CSR)--先在本地生成私钥和公钥,而后把公钥发送到CA,CA会发布一个应用程序使用的证书。这三个凭证会保存在钱包中,以便于咱们以管理员的身份使用CA。

接下来咱们会注册和登记一个新的应用程序用户,咱们将使用这个用户来经过应用程序和区块链进行交互。

让咱们登记一个admin用户:

node enrollAdmin.js
复制代码

这个命令将CA管理员证书保存在wallet目录。

注册和登记user1

如今咱们在钱包里放了管理员的证书,咱们能够登记一个新用户--user1--用这个用户来查询和更新帐本:

node registerUser.js
复制代码

和登记管理员相似,这个程序使用了CSR来登记user1并把它的证书保存到admin所在的钱包中。如今咱们有了2个独立的用户--adminuser1--它们都将用于咱们的应用程序。

接下来是帐本交互时间...

查询帐本

区块链网络中的每一个节点都拥有一个帐本的副本,应用程序能够经过执行智能合约查询帐本上的最新舒徐来实现查询帐本操做,将结果返回给应用程序。

这是一个如何查询的简单阐述:

应用程序使用查询从ledger读取数据。最多见的就是查询当前帐本中的最新值--世界状态。世界状态是一个键值对的集合,应用程序能够根据一个键或者多个键来查询数据。并且,当键值对是以JSON形式存在的时候,世界状态能够经过配置使用数据库(例如CouchDB)来支持富查询。这个特性对于查询匹配特定的键的值是颇有帮助的,好比查询一我的的全部汽车。

首先,让咱们使用query.js程序来查询帐本上的全部汽车。这个程序使用咱们的第二个身份--user1--来操做帐本。

node query.js
复制代码

输出结果以下:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is:
[{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},
{"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},
{"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},
{"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},
{"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},
{"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},
{"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},
{"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},
{"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},
{"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]
复制代码

让咱们近距离看一下这个程序。使用文本编辑器(如atom或者visual studio)打开query.js

应用程序开始的时候就从fabric-network模块引入了两个关键的类FileSystemWalletGateway。这两个类将用于定位钱包中user1的身份,而且使用这个身份链接网络:

const { FileSystemWallet, Gateway } = require('fabric-network');
复制代码

应用程序使用网关链接网络:

const gateway = new Gateway();
await gateway.connect(ccp, { wallet, identity: 'user1' });
复制代码

这段代码建立了一个新的网关,而后经过它来让应用程序链接网络。cpp描述了网关经过wallet中的user1来链接网络。打开 ../../basic-network/connection.json来查看cpp是如何解析一个JSON文件的:

const ccpPath = path.resolve(__dirname, '..', '..', 'basic-network', 'connection.json');
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
const ccp = JSON.parse(ccpJSON);
复制代码

若是你想了解更多关于链接配置文件的结构以及它是怎么定义网络的,请查阅 the connection profile topic

一个网络能够被拆分红不少个通道,代码中下一个很重要的地方是将应用程序链接到特定的通道mychannel上:

在这个通道中,咱们能够经过fabcar智能合约来和帐本进行交互:

const contract = network.getContract('fabcar');
复制代码

fabcar中有许多不一样的交易,咱们的应用程序先使用queryAllCars交易来查询帐本的世界状态:

const result = await contract.evaluateTransaction('queryAllCars');
复制代码

evaluateTransaction方法呈现了一种和区块链网络中的智能合约交互的最简单的方法。它只是根据配置文件中的定义链接一个节点,而后向节点发送请求,在节点内执行该请求。智能合约查询了节点帐本上的全部汽车,而后把结果返回给应用程序。此次交互并无更新帐本。

FabCar智能合约

让咱们看一看FabCar智能合约里的交易。进入fabric-samples下的子目录chaincode/fabcar/javascript/lib,而后用你的编辑器打开fabcar.js

看一下咱们的智能合约是如何经过Contract类来定义的:

class FabCar extends Contract {...
复制代码

在这个类结构中,你将看到定义了如下交易: initLedgerqueryCarqueryAllCarscreateCarchangeCarOwner。例如:

async queryCar(ctx, carNumber) {...}
async queryAllCars(ctx) {...}
复制代码

让咱们更进一步看一下 queryAllCars ,看一下它是怎么和帐本交互的。

async queryAllCars(ctx) {

  const startKey = 'CAR0';
  const endKey = 'CAR999';

  const iterator = await ctx.stub.getStateByRange(startKey, endKey);
复制代码

这段代码定义了 queryAllCars 将要从帐本获取的汽车的范围。从 CAR0 到 CAR999 的每一辆车 -- 一共 1000 辆车,假定每一个键都被合适地锚定了 -- 将会做为查询结果被返回。 代码中剩下的部分,经过迭代将查询结果打包成 JSON 并返回给应用。

下边将展现应用程序如何调用智能合约中的不一样交易。每个交易都使用一组 API 好比 getStateByRange 来和帐本进行交互。了解更多API请阅读detail

你能够看到咱们的queryAllCars交易,还有另外一个叫作createCar。咱们稍后将在教程中使用他们来更新帐本,和添加新的区块。

可是在那以前,返回到query程序,更改evaluateTransaction的请求来查询为CAR4query程序如今以下:

const result = await contract.evaluateTransaction('queryCar', 'CAR4');
复制代码

保存程序,而后返回到fabcar/javascript目录。如今,再次运行query程序:

node query.js
复制代码

你应该会看到以下所示:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is:
{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}
复制代码

若是你查看一下以前queryAllCars的交易结果,你会看到CAR4Adriana黑色 Tesla model S,也就是这里返回的结果,是同样的。

咱们可使用queryCar交易来查询任意汽车,使用它的键(好比CAR0)获得车辆的制造商、型号、颜色和车主等相关信息。

很是好。如今你应该已经了解了智能合约中基础的查询交易,也手动修改了查询程序中的参数。

是时候进行更新帐本了。

更新帐本

如今咱们已经完成一些帐本的查询操做,添加了一些代码,咱们已经准备好更新帐本了。有很 的更新操做咱们能够作,可是咱们从建立一辆新车开始。

从一个应用程序的角度来讲,更新一个帐本很简单。应用程序向区块链网络提交一个交易, 当交易被验证和提交后,应用程序会收到一个交易成功的提醒。可是在底层,区块链网络中各组件中不一样的共识程序协同工做,来保证帐本的每个更新提案都是合法的,并且有一个你们一致承认的顺序。

上图中,咱们能够看到完成这项工做的主要组件。同时,多个节点中每个节点都拥有一份帐本的副本,并可选的拥有一份智能合约的副本,网络中也有一个排序服务。排序服务保证网络中交易的一致性;它也将链接到网络中不一样的应用程序的交易以定义好的顺序生成区块。

咱们对帐本的的第一个更新是建立一辆新车。咱们有一个单独的程序叫作invoke.js,用来更新帐本。和查询同样,使用一个编辑器打开程序定位到咱们构建和提交交易到网络的代码段:

await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');
复制代码

看一下应用程序如何调用智能合约的交易createCar来建立一辆车主为Tom的黑色Honda Accord汽车。咱们使用CAR12做为这里的键,这也说明了咱们没必要使用连续的键。

保存并运行程序:

node invoke.js
复制代码

若是执行成功,你将看到相似输出:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
2018-12-11T14:11:40.935Z - info: [TransactionEventHandler]: _strategySuccess: strategy success for transaction "9076cd4279a71ecf99665aed0ed3590a25bba040fa6b4dd6d010f42bb26ff5d1"
Transaction has been submitted
复制代码

注意inovke程序使用的是submitTransactionAPI和区块链网络交互的,而不是evaluateTransaction

await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');
复制代码

submitTransactionevaluateTransaction要复杂的多。不仅是和单个节点交互,SDK将把submitTransaction提案发送到区块链网络中每个必要的组织的节点。每个节点都将根据这个提案执行请求的智能合约,并生成一个该节点签名的交易响应并返回给SDK 。SDK将全部通过签名的交易响应收集到一个交易中,这个交易将会被发送到排序节点。排序节点搜集并排序每一个应用的交易,并把这些交易放入到一个交易区块。而后排序节点将这些区块分发到网络中的节点,每一笔交易都会在节点中进行验证和提交。最后,SDK会后到提醒,并把控制权返回给应用程序。

submitTransaction也会包括一个监听器用于确保交易已经被校验和提交到帐本里了。应用程序须要利用监听器或者使用submitTransaction接口,它内部已经实现了监听器。若是没有监听器,你可能没法肯定交易是否被排序校验以及提交。

应用程序中的这些工做由submitTransaction完成!应用程序、智能合约、节点和排序服务一块儿工做来保证网络中帐本一致性的程序被称为共识。

为了查看这个被写入帐本的交易,返回到query.js并将参数CAR4更改成CAR12

换句话说就是将:

const result = await contract.evaluateTransaction('queryCar', 'CAR4');
复制代码

改成:

const result = await contract.evaluateTransaction('queryCar', 'CAR12');
复制代码

再次保存,而后查询:

node query.js
复制代码

将返回:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is:
{"colour":"Black","make":"Honda","model":"Accord","owner":"Tom"}
复制代码

恭喜。你建立了一辆汽车并验证了它记录在帐本上!

如今咱们已经完成了,咱们假设Tom很大方,想把他的Honda Accord送给一个叫Dave的人。

为了完成这个,返回到invoke.js而后利用输入的参数,将智能合约的交易从createCar改成changeCarOwner

await contract.submitTransaction('changeCarOwner', 'CAR12', 'Dave');
复制代码

第一个参数 ---CAR12--- 表示将要易主的车。第二个参数 ---Dave--- 表示车的新主人。

再次保存并执行程序:

node invoke.js
复制代码

如今咱们来再次查询帐本,以肯定Dave和CAR12键已经关联起来了:

node query.js
复制代码

将返回以下结果:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is:
{"colour":"Black","make":"Honda","model":"Accord","owner":"Dave"}
复制代码

CAR12的主人已经从Tom变成了Dave。

在实际的应用中,智能合约有权限控制逻辑。举个例子,只有有权限的用户能够建立新车,只有车子的拥有者能够转移车辆所属权。

总结

如今咱们已经完成了帐本的查询和更新,你也应该比较了解如何经过智能合约和区块链进行交互来查询帐本和更新帐本了。在教程中已经讲解了查询和更新的智能合约,API和SDK,想必你对其余商业场景也有了必定的了解和认识。

经过FabCar这个例子,咱们能够快速学习如何基于Node SDK开发应用程序。

AwesomeBlockchain
相关文章
相关标签/搜索