前言
在本系列第 1 部分文章的中,我们一起学习了 Fabric、Composer 基础知识,利用 Hyperledger Compose 搭建了 Fabric 本地开发环境,运行了一个示例区块链网络应用。
现在,我们可以设想一个更具有现实意义的 User Story,像开发一个真实的区块链应用一样,从无到有地开发、部署、管理这个应用。在这个过程中,我们一定会遇到很多问题,正可以这些问题为线索,快速、高效地学习、研究区块链网络。这样,也可以使学习的过程更具趣味性。
我想把这个主题定为"共享自行车区块链网络"。共享经济的一些特点使之正适合利用区块链技术。这个示例应用会随着一些区块链问题的研究与解决,从最初的简单原型开始进行多次迭代。目前它的名字就定为:bikesharing Network。
本文中的示例以第 1 部分的示例为基础,请先行阅读、执行。本文示例所使用的 Fabric 的版本为 1.1.0,而 Composer 的最新版本为 0.19.4,与之前的 0.19.1 在使用方法上没有差别。
Composer 现在版本更新较频繁,请注意版本更新可能带来的变化。(Micro 版本号对应用应该没有影响,在本文公开后,如继续有升级,请自行升级 Composer 即可。)
升级 Composer 与重启 Fabric
升级 Composer
$ ~/fabric-tools/stopFabric.sh
$ npm uninstall -g composer-cli composer-rest-server generator-hyperledger-composer
$ npm install -g composer-cli composer-rest-server generator-hyperledger-composer
|
重启 Fabric 及 BND
重启 Fabric:
$ ~/fabric-tools/stopFabric.sh
$ ~/fabric-tools/startFabric.sh
|
重启 BND:
startFabric 后需要重新 install,start BND,import card,仍以第 1 部分中的 tutorial-network 为例:
$ cd ~/fabric-tools/tutorial-network
$ composer card delete --card admin@tutorial-network
$ composer network install --card PeerAdmin@hlfv1 --archiveFile tutorial-network@0.0.1.bna
$ composer network start --networkName tutorial-network --networkVersion 0.0.1 --
networkAdmin admin --networkAdminEnrollSecret adminpw --card PeerAdmin@hlfv1 --file
admin@tutorial-network.card
$ composer card import --file admin@tutorial-network.card
|
teardown
如果 ~/fabric-tools/teardownFabric.sh 被执行过,则在 startFabric 后,需要再次执行 createPeerAdminCard.sh 及之后步骤。详情请参考本系列第一部分的相关内容。
删除 Docker Images
如果需要彻底删除之前下载安装的 Composer、Fabric 环境,可以执行如下命令:
$ docker kill $(docker ps -q)
$ docker rm $(docker ps -aq)
$ docker rmi $(docker images dev-* -q)
|
User Story – 共享自行车区块链网络(bikesharing Network)
业务需求
虽然本文是一篇技术文章,但我认为,理解学习的目的及确定学习的方向,是一件首要的事情,所以,我们必须先理解一些商业相关内容,我们这个 User Story 的需求是什么,大概涵盖哪些方面。这个网络应满足以下需求:
- 共享自行车服务供应商会通过这个网络注册自行车;
- 用户会通过这个网络租用(解锁)、归还(上锁)自行车;
- 所有操作(状态修改、付费、报修)都作为 Transaction(交易)提交到这个区块链网络;
- 允许多个自行车服务供应商共用这个网络;
- 允许自行车服务供应商执行不同的业务逻辑;
- 允许其他共享服务(共享汽车、共享房间……)供应商、用户使用这个网络。
这样看来,我们需要解决很多问题:基于区块链模式下的业务模型设计、权限管理,节点的部署与控制,效率问题,系统可靠性问题……但不管如何,我们都会 Focus on Blockchain,会始终着力于典型的"区块链"应用。
当前简化的原型设计、开发、部署
我们要先从一个最简化的原型开始,也就是本部分所将涉及的内容:
- Assets:自行车;
- Participants:自行车供应商、自行车用户;
- Transaction:租车、还车;
- Query:设计基本查询;
- BND:Business Network Definition 业务网络定义;
- BND 部署与更新。
现在,我们需要先了解一些必需的 BND(业务网络定义)基础知识。我们可以带着对这个 User Story 的想法、疑问去学习 BND,及其他相关知识。
业务网络定义 Business Network Definition(BND)
BND 是 Composer 编程模型中的的关键概念,它包含了这个区块链业务网络的所有域模型、业务逻辑、访问控制规则、查询等内容。BND 会被打包后部署到 Fabric 节点上,我们可以利用 Composer 系统接口生成 BND 中一些域模型的实例,或者触发执行 BND 中定义的业务逻辑。下面即按各部分详细讲解,并给出 bikesharing 的实例。
域模型(Domain Models)
域模型主要包含了以下定义:Assets,Participants,Transactions,Events 等,它们是这个业务网络中将要被使用、处理的对象类型,也可以理解为一系列资源类型。它位于 models 目录下名为 *.cto 的文件中。域模型是用 Hyperledger Composer Modeling Language 定义的。
Composer 建模语言(Composer Modeling Language)
它是 Hyperledger Composer 提供的一个面向对象的建模语言,用来定义业务网络中的域模型。它有以下特点与属性。
1. Namespace 命名空间
模型归属于一个 namespace(命名空间)。一个 .cto 文件中会定义一个 namespace,其中包含的所有定义都属于这个 namespace。示例:
namespace org.bikesharing.biznet
asset Bike identified by bikeId { … }
|
则在以后可以用类似 "org.bikesharing.biznet.Bike" 引用这个类型。
2. 域模型的类型及其基础类、关键字:
表 1. 域模型的类型及其基础类、关键字
可从 Composer 源码 org.hyperledger.composer.system.cto 中获取更详细内容。
域模型定义往往包括若干属性(字段)定义。示例:
participant BikeUser identified by bikeUserId {
o String bikeUserId
o Integer age
}
asset Bike identified by bikeId { … }
|
资源 Assets,Participants,Transactions 可以被实例化为对象并存储在相应 AssetRegistry,ParticipantRegistry,TransactionRegistry 中,作为 Fabric 区块链网络的 World State(世界观)中的一部分。
Assets,Participants 需要定义 identified by 属性,以表示资源对象的唯一标识字段。但 Transactions、Events 定义中不能包括 identified by 属性,且不允许有 transactionId,eventId,timestamp 字段,因为它们已经在基础类定义 org.hyperledger.composer.system 中被声明。内容如下:
abstract transaction Transaction identified by transactionId {
o String transactionId
o DateTime timestamp
}
abstract event Event identified by eventId {
o String eventId
o DateTime timestamp
}
|
3. 原生类型
表 2. Composer 建模语言中的原生类型
当原生类型被用于定义模型中的某一属性时,以符号 "o" 作为标志,示例:
o String bikeUserId
o Integer age
|
4. Concept
Concept 可以理解为一个在多种模型定义间共享的一组属性的组合,一般会被 asset、participant、transaction 等资源"包含"。我们可以通过 getFactory().newConcept() 方法实例化 Concept,得到一个对象,但不能将它单独存储在 Registry 中,也不能被作为 Relationship 引用。它没有 "identified by" 字段。示例:
concept Address {
o String street
o String city
o String country
}
participant BikeUser identified by BikeUserId {
// …
o Address adr
// …
}
|
5. 抽象(abstract)与继承(extends)
抽象类型不能被实例化,但可被继承。Assets、Participants、Transaction、Concepts、Events 都可以被定义为 abstract,或通过 extends 继承另一个类型定义。示例:
abstract transaction BikeTransaction { … }
transaction BikeRentTransaction extends BikeTransaction { … }
abstract concept Address { … }
|
6. Relationship
在模型定义中,可以通过 Relationship 引用另一个资源对象。用符号"-->"标示。示例:
transaction BikeTransaction {
--> Bike bike
--> BikeUser user
}
|
在实例化这个上层对象时,需要使用完整的 identifier 来标识被引用资源,示例:
"resource:org.bikesharing.biznet.Bike#bid_1"
|
7. Enum
枚举变量定义及使用示例:
o AVAILABLE
o INUSE
o INREPAIR
}
asset Bike identified by bikeId {
// …
o BikeStatus status
// …
}
|
8. 数组
以上所述所有类型都可以用符号"[]"标示为数组,示例:
o Integer[] counts
o String[] comments
--> Bike[] bikes
|
9. Optional
默认情况下,模型中定义的属性是必选项,在实例化时必须指定初始值。但可以将其标示为 optional, 以表示在实例化时可以忽略此属性。示例:
o Integer age optional
|
10. 字段校验
资源字段可以设定默认值,示例:
o String bikeName default="MOF Bike"
|
Double,Integer,Long 类型字段可以定义 Range,示例:
o Integer year range=[1900, ]
|