在上一篇文章中, 通过执行node query.js, 查询Fabric 网络, 并返回了10辆车的信息:
#node query.js
命令行的输出如下:
这是10辆车。 由Adriana拥有的黑色特斯拉模型S,由Brad拥有的红色福特野马,由名叫Pari的人拥有的紫罗兰菲亚特Punto等等。 分类帐是基于键/值的,在这个实现中,关键值是从CAR0到CAR9。 这一点将变得特别重要。
查询账本的实现
现在让我们来看看底层的实现。 使用编辑器(例如atom或visual studio)并打开query.js程序。
应用程序的初始部分定义了某些变量,如链码,通道名称和网络端点:
下面的代码用来构造对于汽车的查询, 注意前面的注释, 这个注释中指明, 如果调用queryCar, 则参数args就要传入汽车的key, 也就是CAR0等;在这个例子中指明是用queryAllCars, 则返回所有的car对象
在上面的request对象中, 通过chaincodeId来指定需要调用的chaincode,如上面所示, chaincode是fabcar.
那么这个名为fabcar的chaincode, 具体是什么样子的呢?来看一下代码。首先这个chaincode是放在下面的这个目录下的:
打开这个chaincode 源文件,是用Go 语言编写的, 最上面定义了SmartContract 以及Car 两个类, 其中Car 是主要的Model 对象, 有Make,Model等几个属性。
下面的两个方法则是和Fabric 网络有关, 主要是通过shim对象进行交互。
第一个方法是Init,从注释中看, init 方法会在chaincode 被实例化时被执行。注释还提及一个最佳实践, 就是建议不要存放任何业务逻辑, 而是把初始化数据等业务逻辑放在一个单独的方法中进行调用,这里是initLedger方法。
第二个方法是Invoke, 也就是应用程序对于区块链网络的访问, 这里使用Invoke进行访问。在方法内部, 首先通过GetFunctionAndParameters方法来获取要调用的函数方法和参数。这里可以看出, Go 语言和Javascript 语言一样, 变量可以不定义直接使用, Go 语言编译器会根据上下文推导变量的类型;而且还可以返回多个变量。
这里面需要注意的第一点就是, 对于不了解Go语言的同学来说,函数名Init 前面的部分 (s *SmartContract) 是难以理解的, 实际上可以把这个看做Go语言的类方法,记得最前面的SmartContract吗? 前面的SmartContract 代码块中, SmartContract是空的:
// Define the Smart Contract structure
type SmartContract struct {
}
这个实际上就是Go语言的特色之一, 类方法的动态添加;在这里给SmartContract添加了Init和Invoke 两个方法, 当Invoke 方法被调用时, 则根据传入的参数, 执行queryCar还有queryAllCars等方法。
下面几个业务方法有 initLedger,queryCar,queryAllCars,createCar和changeCarOwner。 我们来仔细看一下queryAllCars函数,看看它如何与Fabric进行交互。
该函数使用Shim接口的函数GetStateByRange在startKey和endKey的args之间返回账本数据。 这些键分别定义为CAR0和CAR999。 因此,我们理论上可以创建1,000辆汽车(假设钥匙被正确标记),并且一个queryAllCars会显示每一个。这个方法的下半部分是转换成json格式返回。
下面是应用程序调用不同chaincode方法和区块链网络进行交易的示意图:
之前我们看到了queryAllCars方法的调用示例, 下面来尝试一下其他的方法。
查询特定车辆
返回到query.js程序并编辑构造函数请求来查询特定的车。 我们将通过将函数从queryAllCars更改为queryCar并将特定的“Key”更改为args参数。 我们在这里使用CAR4。 所以我们编辑的query.js程序现在应该包含以下内容:
然后来执行一下:
#node query.js
结果如下:
可以看到最下面的输出:
所以我们从查询所有车到只查询一个,Adriana的黑色特斯拉模型S.使用queryCar函数,我们可以查询任何关键字(例如CAR0),并获得与该车相对应的任何代码,模型,颜色和所有者。
现在,您应该对链码中的基本查询功能以及查询程序中的少数参数感到舒适。 更新分类帐的时间到了。。。
更新账本
现在我们已经完成了几个账本查询并添加了一些代码,我们已经准备好更新分类帐。 让我们为新手开一辆新车。
账本更新的工作,从应用产生一个交易提案(transaction proposal)开始。就像查询一样, 需要构造一个请求对象(request)来确定channel ID, 方法和特定的只能合约, 来指定一个事务(transaction)。 这个应用下一步会调用
channel.SendTransactionProposal
这个API 来发送交易提案到对等节点来执行背书(endorsement)。
区块链网络(如背书节点, endorsing peer) 返回一个提案响应(proposal response), 应用会利用这个对象来构建和签名交易请求(transaction request), 这个交易请求会通过调用channel.sendTransction API, 发送到ordering service 节点。 Ordering Service 会把这个事务打包到一个区块中, 并把这个区块发送到channel 中的所有节点来执行验证(在这个例子中只有一个背书节点)。
最后, 应用会调用eh.setPeerAddr API来连接peer 节点的事件监听端口, 然后通过调用eh.registerTxEvent 来注册和特定事务ID 相关联的事件。 这个API 允许应用来获知一个特定事务的最终状态(例如成功提交或者失败)。把这个过程想象成一个通知机制会更有利于理解。
这里并不打算更多的解释事务的生命周期, 后续会有更多介绍。
在这次的最初的invoke 调用中, 我们仅仅是创建一个新的asset:汽车。 在fabcar 目录中,有一个单独的Javascript 文件 ——invoke.js 我们会使用这个文件来执行事务。
打开文本编辑器, 查看如下代码:
你会看到我们可以调用两个函数之一 - createCar或changeCarOwner。 让我们创造一个红色的雪佛兰伏特,并把它交给一个名叫尼克的业主。 我们在分类帐上使用CAR9,所以我们将使用CAR10作为识别键。 更新的代码块应如下所示:
保存并执行代码:
#node invoke.js
结果会有很多的输出:
然后,我们把注意力在这一行:
Peer 节点发出事件通知, 我们的应用由于通过eh.registerTxEvent API , 会接收到通知。
现在, 我们返回query.js, 把代码修改为查询key为'CAR11'的汽车信息:
然后执行
#node query.js
查看结果, 发现我们的奔驰C200已经显示出来了:
车辆过户
还有一个changeCarOwner的智能合约没有用到, 现在, 你可以自己实现这个功能了吗?
原文发布时间为:2017年08月26日
本文作者:查理曼大帝
本文来源:****,如需转载请联系原作者。