区块链服务
开发指南
文档版本 01
发布日期 2022-02-23
版权所有 © 华为技术有限公司 2022。 保留一切权利。
非经本公司书面许可,任何单位和个人不得擅自摘抄、复制本文档内容的部分或全部,并不得以任何形式传 播。
商标声明
和其他华为商标均为华为技术有限公司的商标。
本文档提及的其他所有商标或注册商标,由各自的所有人拥有。
注意
您购买的产品、服务或特性等应受华为公司商业合同和条款的约束,本文档中描述的全部或部分产品、服务或 特性可能不在您的购买或使用范围之内。除非合同另有约定,华为公司对本文档内容不做任何明示或暗示的声 明或保证。
由于产品版本升级或其他原因,本文档内容会不定期进行更新。除非另有约定,本文档仅作为使用指导,本文 档中的所有陈述、信息和建议不构成任何明示或暗示的担保。
目 录
1 简介...1
2 链代码开发... 3
2.1 开发前准备... 3
2.2 开发规范...5
2.3 Go 语言链代码开发... 6
2.3.1 链代码结构... 6
2.3.2 链代码相关的 API... 8
2.3.3 链代码示例(1.4 风格)...8
2.3.4 链代码示例(2.0 风格)... 11
2.3.5 链代码调测... 13
2.4 Java 语言链代码开发...14
2.4.1 链代码结构... 14
2.4.2 链代码相关的 API... 15
2.4.3 链代码示例... 15
2.4.4 链代码调测... 17
3 应用程序开发...21
3.1 概述... 21
3.2 开发前准备... 21
3.3 应用程序开发...21
4 示例 Demo... 23
4.1 GO SDK Demo... 23
4.2 Java SDK Demo... 29
4.3 Gateway Java Demo... 32
4.4 REST API Demo...36
5 区块链中间件接口... 45
5.1 概述... 45
5.2 政务区块链平台... 46
5.2.1 华为接口... 46
5.2.1.1 联盟链管理... 46
5.2.1.1.1 创建联盟链...46
5.2.1.1.2 删除联盟链...51
5.2.1.1.3 加入联盟链...53
开发指南 目 录
5.2.1.1.4 退出联盟链...54
5.2.1.2 区块链节点管理... 56
5.2.1.2.1 增加区块链节点... 56
5.2.1.2.2 删除区块链节点... 58
5.2.1.3 应用链管理... 59
5.2.1.3.1 创建应用链...59
5.2.1.3.2 退出应用链...61
5.2.1.3.3 删除应用链...63
5.2.1.3.4 加入应用链...64
5.2.1.4 合约管理... 66
5.2.1.4.1 部署合约... 66
5.2.1.4.2 升级合约... 69
5.2.1.4.3 终止合约... 72
5.2.1.5 运营管理... 74
5.2.1.5.1 查看联盟链列表... 74
5.2.1.5.2 查看联盟链信息... 76
5.2.1.5.3 查看区块链节点列表... 80
5.2.1.5.4 查看区块链节点信息... 83
5.2.1.5.5 查看应用链列表... 85
5.2.1.5.6 查看应用链信息... 87
5.2.1.5.7 查看合约列表... 89
5.2.1.5.8 查看合约信息... 93
5.2.1.5.9 查询区块列表... 97
5.2.1.5.10 查询区块信息... 100
5.2.1.5.11 查询交易列表... 102
5.2.1.5.12 查看交易统计信息...104
5.2.1.5.13 查询交易信息... 106
5.2.1.5.14 查询告警信息... 109
5.2.1.5.15 查询 BCS 配置信息...112
5.2.1.5.16 查询 BCS 全部配置信息... 113
5.2.1.5.17 查询操作结果... 115
5.2.1.5.18 查询区块交易列表...118
5.2.1.6 运维监控... 121
5.2.1.6.1 查询区块链节点监控数据详情... 121
5.2.1.6.2 查询联盟链监控数据详情... 123
5.2.1.7 告警审计... 126
5.2.1.7.1 查询审计日志... 126
5.2.1.8 区块链账号管理... 128
5.2.1.8.1 冻结账号... 128
5.2.1.8.2 解冻账号... 130
5.2.1.8.3 创建账号... 131
5.2.1.9 华为配置接口... 133
开发指南 目 录
5.2.1.9.1 设置用户... 133
5.2.2 政务接口... 134
5.2.2.1 运维监控... 134
5.2.2.1.1 查询当前指标... 134
5.2.2.1.2 查询范围指标... 137
5.2.2.2 平台管理... 140
5.2.2.2.1 查询区块链平台列表... 140
5.2.2.2.2 查询区块链平台状态... 141
5.2.2.3 组织管理... 143
5.2.2.3.1 查询组织列表... 143
5.2.2.3.2 更新组织... 144
5.2.2.3.3 注册组织... 146
5.2.2.4 联盟链管理...147
5.2.2.4.1 查询业务链处理流程进度... 148
5.2.2.4.2 查询服务配额... 149
5.2.2.4.3 查询联盟中业务链规格信息...151
5.2.2.4.4 查询联盟列表... 152
5.2.2.4.5 创建联盟... 155
5.2.2.4.6 查询联盟详细信息... 160
5.2.2.4.7 删除联盟... 163
5.2.2.4.8 查询业务链列表... 164
5.2.2.4.9 创建业务链... 166
5.2.2.4.10 查询业务链详细信息... 170
5.2.2.4.11 删除业务链...172
5.2.2.4.12 组织和共识证书下载... 173
5.2.2.4.13 查询区块链节点信息... 175
5.2.2.4.14 查询区块链共识节点信息...177
5.2.2.4.15 查询区块链实例后端集群信息... 179
5.2.2.4.16 业务链加入新组织...181
5.2.2.4.17 组织退出业务链... 184
5.2.2.4.18 联盟加入新组织... 185
5.2.2.4.19 组织退出联盟... 187
5.2.2.5 区块信息查询... 188
5.2.2.5.1 区块信息查询... 188
5.2.2.5.2 账本概要信息查询... 190
5.2.2.5.3 交易列表查询... 193
5.2.2.5.4 交易详情查询... 196
5.2.2.6 合约管理... 199
5.2.2.6.1 合约 SDK 下载... 199
5.2.2.6.2 合约查询... 201
5.2.2.6.3 合约部署... 203
5.2.2.6.4 合约删除... 205
开发指南 目 录
5.2.2.7 华为配置接口... 206
5.2.2.7.1 获取实例代理密码... 206
5.3 链代码调用... 209
5.4 链代码管理... 211
5.4.1 获取 Token... 211
5.4.2 安装链代码... 213
5.4.3 实例化链代码...216
5.4.4 获取安装的链码列表...219
5.4.5 查询指定链码版本信息... 223
5.4.6 查询链代码安装信息...226
5.4.7 查询链代码实例化信息... 228
5.4.8 查询应用链信息... 231
5.4.9 查询区块列表...234
5.4.10 查询交易列表... 237
5.4.11 查询交易总数... 240
5.4.12 查询区块交易列表...241
5.4.13 查询交易详情... 245
5.4.14 查询节点状态... 249
5.4.15 删除链代码...251
5.4.16 下载报告... 253
5.5 分布式身份... 255
5.5.1 概述... 255
5.5.2 分布式身份(DID)管理... 257
5.5.2.1 企业身份注册(带有 service)...258
5.5.2.2 注册 DID... 261
5.5.2.3 更新 DID... 263
5.5.2.4 查询 DID... 267
5.5.3 可验证凭证(VC)管理... 271
5.5.3.1 发布可验证凭证的模板... 271
5.5.3.2 查询凭证模板... 274
5.5.3.3 申请可验证凭证... 276
5.5.3.4 签发者确认凭证已签发... 278
5.5.3.5 查询凭证申请订单...279
5.5.3.6 查询待处理的申请订单... 281
5.5.3.7 签发可验证凭证... 284
5.5.3.8 根据索引查询可验证凭证...286
5.5.3.9 验证凭证... 288
5.6 可信数据交换... 291
5.6.1 概述... 291
5.6.2 数据集管理... 293
5.6.2.1 发布数据集...293
5.6.2.2 删除数据集...298
开发指南 目 录
5.6.2.3 关闭数据集...299
5.6.2.4 查询指定数据集... 301
5.6.2.5 查询数据集列表... 303
5.6.2.6 主动分享数据集... 306
5.6.2.7 获取数据解密后的明文... 310
5.6.2.8 提取文件中的暗水印... 312
5.6.3 数据订单管理...313
5.6.3.1 申请数据集...313
5.6.3.2 授权数据集...315
5.6.3.3 修改订单状态... 317
5.6.3.4 删除订单... 319
5.6.3.5 查询指定订单... 320
5.6.3.6 查询订单列表... 322
5.6.4 属性加密的密钥管理...325
5.6.4.1 初始化 ABE 主密钥... 325
5.6.4.2 更新 ABE 主密钥...327
5.6.4.3 查询 ABE 主密钥...328
5.6.4.4 申请 ABE 用户密钥... 330
5.6.4.5 授权 ABE 用户密钥... 332
5.6.4.6 查询 ABE 用户密钥申请...334
5.6.4.7 ABE 用户密钥解密数据...335
6 附录... 338
6.1 国密加密... 338
6.1.1 概述... 338
6.1.2 SDK 的使用... 339
6.1.3 附录... 340
6.2 同态加密... 341
6.2.1 概述... 341
6.2.2 同态加密库的使用... 342
6.2.3 AHE Lib 库接口... 343
6.2.4 Chaincode 库接口... 347
6.2.5 IDChaincode... 349
6.2.6 链代码示例... 349
6.2.7 应用示例... 351
6.2.8 同态加密交易验证 Demo... 353
6.3 错误码... 366
开发指南 目 录
1 简介
在使用区块链服务时,您需要开发自己的链代码和应用。本文档主要介绍链代码的开 发及其应用配置,专供具备Go/Java开发经验的开发人员使用。
区块链服务使用流程如下:
1. 购买区块链服务实例
Fabric架构版本的区块链服务支持在CCE集群和边缘集群上部署,具体可参见 Fabric服务部署。
2. 开发链代码
链代码是用Go、Java或Node.js语言编写的程序,主要用于操作账本上的数据,具 体可参见链代码开发。
3. 安装、实例化链代码
区块链服务为您提供界面化链代码管理功能,包括链代码安装、实例化等,具体 可参见链代码管理。
4. 下载SDK配置和证书
开发指南 1 简介
应用程序开发前,您需要获取对应服务实例的SDK配置文件和证书,具体可参见 访问Fabric服务区块链。
5. 开发应用程序
您需要自行开发应用程序业务逻辑代码。针对Fabric架构版本的区块链服务,应用 开发过程中可使用我们提供的国密加密SDK,也可使用Fabric官方社区提供的和您 自身的服务实例版本匹配的SDK,具体可参见应用程序开发。
另外,Fabric架构版本的区块链服务提供同态加密库供您使用,相关资料及资源请 参见同态加密。
说明
如果您对业务链代码和客户端APP的设计和开发有需求,可以联系华为云区块链合作伙伴提供进 一步服务,我们会结合您的业务以及华为云的优势和特点为您提供完善的解决方案,联系邮箱如 下:[email protected]
开发指南 1 简介
2 链代码开发
2.1 开发前准备
链代码(Chaincode)又称智能合约,是用Go、Java或Node.js语言编写的程序,主要 用于操作账本上的数据。链代码是运行在区块链上的、特定条件下自动执行的代码逻 辑,是用户利用区块链实现业务逻辑的重要途径。基于区块链特点,智能合约的运行 结果是可信的,其结果是无法被伪造和篡改的。
在使用区块链服务时,用户需要开发自己的链代码和应用程序。用户的应用程序通过 区块链网络中的Peer节点/节点调用链代码,用户链代码通过区块链网络的Peer节点/节 点来操作账本数据。
开发环境准备
请根据自身业务选择Go或Java开发环境。推荐使用CloudIDE(支持在线、快速地构建 链代码开发环境)。
Go开发环境准备:
1. 安装Go开发环境。安装包下载地址为:https://golang.org/dl/。(请选择1.9.2 之后的版本)
各个系统对应的包名(以1.11.12版本为例):
操作系统 包名
Windows go1.11.12.windows-amd64.msi Linux go1.11.12.linux-amd64.tar.gz
– Windows下您可以使用.msi后缀的安装包来安装。默认情况下.msi文件会安 装在“C:\Go”目录下。你可以将“C:\Go\bin”目录添加到Path环境变量 中。添加后您需要重启命令窗口才能生效。
– Linux下,您需要将下载的二进制包解压至/usr/local目录。将/usr/
local/go/bin目录添加至Path环境变量:
export PATH=$PATH:/usr/local/go/bin
开发指南 2 链代码开发
安装完go语言后可以通过命令go version查看版本信息,以及通过go env命令来 查看相关路径配置。
2. 安装Go编辑器。编辑器可自行选择,推荐使用Goland:https://
www.jetbrains.com/go/download。
Java开发环境准备:
仅适用于Fabric架构版本的区块链服务。
1. 安装Java开发环境。下载JDK并安装(建议选择最新版本):https://
www.oracle.com/technetwork/java/javase/downloads/index.html。
各个系统对应的包名(以15.0.2版本为例):
操作系统 包名
Windows jdk-15.0.2_windows-x64_bin.exe Linux jdk-15.0.2_linux-x64_bin.tar.gz
– Windows下您可以使用 .exe后缀的安装包来安装。
– Linux下,您需要将下载的二进制包解压至/usr/local目录。
export PATH=$PATH:/usr/local/go/bin
配置环境变量(若无则新建):
– JAVA_HOME为jdk安装目录如“C:\Program Files (x86)\Java\jdk1.8.0_91”
或“/usr/java/jdk1.8.0_91”(以下均略去双引号);
– CLASSPATH为“.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib
\tools.jar; ”;
– 在Path中新增两条“%JAVA_HOME%\bin”和“%JAVA_HOME%\jre
\bin”。
安装完jdk后,可以通过命令java -version查看版本信息。
2. 安装Java编辑器。编辑器可自行选择,推荐使用IntelliJ IDEA:https://
www.jetbrains.com/zh-cn/idea/download/#section=windows。
下载源码包
下载Fabric源码包作为三方库。仅适用于Fabric架构版本的区块链服务。
请根据实际需求,选择下载对应版本的Fabric源码包:
https://github.com/hyperledger/fabric/tree/release-1.4
说明
版本信息选择的是3.X.X版本(Fabric 1.4.0),则Fabric源码包对应选择1.4版本;
开发指南 2 链代码开发
2.2 开发规范
防止出现 panic 后链代码容器挂掉
说明
该内容仅适用于Go语言链代码开发。
该内容仅适用于Fabric架构版本的区块链服务。
为避免出现panic异常时链代码容器挂掉重启,找不到日志,导致问题无法及时定位,
可在Invoke函数入口处添加defer语句时,出现panic异常时返回错误给客户端。
// 定义命名返回值,发生panic在defer里面赋值,确保客户端可以收到返回值 // 使用debug.PrintStack()将错误的堆栈信息打印到标准输出,方便问题定位
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) (pr pb.Response) { defer func() {
if err:=recover(); err != nil {
fmt.Println("recover from invoke:", err) debug.PrintStack()
pr = shim.Error(fmt.Sprintf("invoke painc. err: %v",err)) }
}()
fmt.Println("ex02 Invoke")
function, args := stub.GetFunctionAndParameters() if function == "invoke" {
// Make payment of X units from A to B return t.invoke(stub, args)
} else if function == "delete" { // Deletes an entity from its state return t.delete(stub, args) } else if function == "query" {
// the old "Query" is now implemented in invoke return t.query(stub, args)
}
pr = shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"") return pr
}
分批次查询数据
说明
该内容仅适用于Fabric架构版本的区块链服务。
查询账本数据时,如果在一次查询中返回过多的数据,会导致资源占用过多,接口延 时较长(超过30s时peer会中断任务),应预先估计数据量,分批次进行查询。
修改或删除账本数据调用链代码时,同样也需要根据数据量大小确定是否采取分批次 操作的方式处理。
合理使用索引(账本数据存储方式为 CouchDB)
对于CouchDB来说,使用索引功能,写入数据时会消耗时间,但可明显提高数据查询 速度。因此可以根据业务需要,合理的在某些字段上建立索引。
添加权限验证
对智能合约执行者的权限进行验证,防止无权限的用户执行链代码。
开发指南 2 链代码开发
说明
如果业务上不要求确定的某个组织进行背书,为确保链代码上的数据不被任意组织恶意修改(自 己安装非法链代码,操作数据等),建议至少两个或两个以上组织共同参与背书。
参数校验
参数(包括入参和代码中定义的各种参数)在使用前需对其个数、类型、长度、取值 范围等做校验,验证其合法性,防止出现数组越界等问题。
日志处理
开发时需要对业务逻辑复杂、容易出错的地方,使用fmt打印日志,便于调测。但是在 开发调测结束后,需要将其删除,因为fmt会消耗时间和资源。
依赖配置
说明
该内容仅适用于Java语言链代码开发。
该内容仅适用于Fabric架构版本的区块链服务。
● 请使用Gradle或Maven构建管理工具组织链代码项目。若链代码项目中包含非本 地依赖,请确保对应区块链服务的节点均绑定了弹性ip。示例链代码获取方法:
登录区块链服务管理控制台,进入“体验中心 > 应用案例”,单击“Java示例 Demo-Java SDK Demo”中“Chaincode_Java_Demo”的“下载”按钮。
● 若链代码容器将运行在受限网络环境,请确保项目中的所有依赖已配置为本地依 赖。示例链代码获取方法:登录区块链服务管理控制台,进入“体验中心 > 应用 案例”,单击“Java示例Demo-Java SDK Demo”中
“Chaincode_Java_Local_Demo”的“下载”按钮。
2.3 Go 语言链代码开发
2.3.1 链代码结构
本章以Go语言为例来介绍。链代码即一个Go文件,创建好文件后进行函数开发等操 作。
当前支持两种方式编写链码:1.4风格(使用shim包)和2.2风格(使用fabric- contract-api-go包)。
说明
区块链服务BCS支持使用两种风格编写的链代码。
链代码接口
Fabric架构版本的区块链服务:
● 链代码启动必须通过1.4风格(调用shim包)中的Start函数,入参为shim包中定 义的Chaincode接口类型。实际开发中, 您需要自行定义一个结构体,实现 Chaincode接口。
type Chaincode interface {
Init(stub ChaincodeStubInterface) pb.Response
开发指南 2 链代码开发
Invoke(stub ChaincodeStubInterface) pb.Response }
● 2.2风格(使用fabric-contract-api-go包)的链代码实际开发中, 您需要自行定义 一个结构体,实现Chaincode接口。
type Chaincode interface {
Init(ctx contractapi.TransactionContextInterface, args…) error Invoke(ctx contractapi.TransactionContextInterface, args…) error }
链代码结构
● Fabric架构版本的区块链服务1.4风格Go语言的链代码结构如下:
package main // 引入必要的包 import (
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
// 声明一个结构体
type SimpleChaincode struct {}
// 为结构体添加Init方法
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { // 在该方法中实现链代码初始化或升级时的处理逻辑
// 编写时可灵活使用stub中的API }
// 为结构体添加Invoke方法
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { // 在该方法中实现链代码运行中被调用或查询时的处理逻辑
// 编写时可灵活使用stub中的API }
//主函数,需要调用shim.Start()方法 func main() {
err := shim.Start(new(SimpleChaincode)) if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err) }
}
● Fabric架构版本的区块链服务2.2风格Go语言的链代码结构如下:
package main // 引入必要的包 import (
"github.com/hyperledger/fabric/plugins/fabric-contract-api-go/contractapi")
// 声明一个结构体 type Chaincode struct { contractapi.Contract }
// 为结构体添加Init方法
func (ch * Chaincode) Init(ctx contractapi.TransactionContextInterface, args…) error { // 在该方法中实现链代码初始化或升级时的处理逻辑
}
// 为结构体添加Invoke方法
func (ch * Chaincode) Invoke(ctx contractapi.TransactionContextInterface, args…) error { // 在该方法中实现链代码运行中被调用或查询时的处理逻辑
} //主函数
开发指南 2 链代码开发
func main() {
cc, err := contractapi.NewChaincode(new(ABstore)) if err != nil {
panic(err.Error()) }
if err := cc.Start(); err != nil {
fmt.Printf("Error starting ABstore chaincode: %s", err) }
}
2.3.2 链代码相关的 API
Fabric源码包中的shim包提供了如下几种类型的接口,您可以参考使用:
● 参数解析API:调用链代码时需要给被调用的目标函数/方法传递参数,该API提供 解析这些参数的方法。
● 账本状态数据操作API:该API提供了对账本数据状态进行操作的方法,包括对状 态数据的查询及事务处理等。
● 交易信息获取API:获取提交的交易信息的相关API。
● 对PrivateData操作的API: Hyperledger Fabric在1.2.0版本中新增的对私有数据 操作的相关API。
● 其他API:其他的API,包括事件设置、调用其他链代码操作。
2.3.3 链代码示例(1.4 风格)
Fabric架构版本的区块链服务:
如下是一个账户转账的链代码示例(1.4风格)仅供安装实例化,若您需要调测请参考 Fabric官方示例中的链代码。
package main import ( "fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
type SimpleChaincode struct { }
// 初始化数据状态,实例化/升级链代码时被自动调用
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { // println函数的输出信息会出现在链代码容器的日志中
fmt.Println("ex02 Init")
// 获取用户传递给调用链代码的所需参数 _, args := stub.GetFunctionAndParameters() var A, B string // 两个账户
var Aval, Bval int // 两个账户的余额 var err error
// 检查合法性, 检查参数数量是否为4个, 如果不是, 则返回错误信息 if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4") }
A = args[0] // 账户A用户名
Aval, err = strconv.Atoi(args[1]) // 账户A余额 if err != nil {
return shim.Error("Expecting integer value for asset holding")
开发指南 2 链代码开发
}
B = args[2] // 账户B用户名
Bval, err = strconv.Atoi(args[3]) // 账户B余额 if err != nil {
return shim.Error("Expecting integer value for asset holding") }
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) // 将账户A的状态写入账本中
err = stub.PutState(A, []byte(strconv.Itoa(Aval))) if err != nil {
return shim.Error(err.Error()) }
// 将账户B的状态写入账本中
err = stub.PutState(B, []byte(strconv.Itoa(Bval))) if err != nil {
return shim.Error(err.Error()) }
return shim.Success(nil) }
// 对账本数据进行操作时(query, invoke)被自动调用
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { fmt.Println("ex02 Invoke")
// 获取用户传递给调用链代码的函数名称及参数 function, args := stub.GetFunctionAndParameters() // 对获取到的函数名称进行判断
if function == "invoke" {
// 调用 invoke 函数实现转账操作 return t.invoke(stub, args) } else if function == "delete" { // 调用 delete 函数实现账户注销 return t.delete(stub, args) } else if function == "query" { // 调用 query 实现账户查询操作 return t.query(stub, args) }
// 传递的函数名出错,返回 shim.Error()
return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"") }
// 账户间转钱
func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response { var A, B string // 账户A和B
var Aval, Bval int // 账户余额 var X int // 转账金额 var err error
if len(args) != 3 {
return shim.Error("Incorrect number of arguments. Expecting 3") }
A = args[0] // 账户A用户名 B = args[1] // 账户B用户名 // 从账本中获取A的余额
Avalbytes, err := stub.GetState(A) if err != nil {
return shim.Error("Failed to get state") }
if Avalbytes == nil {
return shim.Error("Entity not found") }
Aval, _ = strconv.Atoi(string(Avalbytes))
开发指南 2 链代码开发
// 从账本中获取B的余额
Bvalbytes, err := stub.GetState(B) if err != nil {
return shim.Error("Failed to get state") }
if Bvalbytes == nil {
return shim.Error("Entity not found") }
Bval, _ = strconv.Atoi(string(Bvalbytes)) // X为转账金额
X, err = strconv.Atoi(args[2]) if err != nil {
return shim.Error("Invalid transaction amount, expecting a integer value") }
// 转账 Aval = Aval - X Bval = Bval + X
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) // 更新转账后账本中A余额
err = stub.PutState(A, []byte(strconv.Itoa(Aval))) if err != nil {
return shim.Error(err.Error()) }
// 更新转账后账本中B余额
err = stub.PutState(B, []byte(strconv.Itoa(Bval))) if err != nil {
return shim.Error(err.Error()) }
return shim.Success(nil) }
// 账户注销
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1") }
A := args[0] // 账户用户名 // 从账本中删除该账户状态 err := stub.DelState(A) if err != nil {
return shim.Error("Failed to delete state") }
return shim.Success(nil) }
// 账户查询
func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response { var A string
var err error if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting name of the person to query") }
A = args[0] // 账户用户名 // 从账本中获取该账户余额 Avalbytes, err := stub.GetState(A) if err != nil {
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
return shim.Error(jsonResp)
开发指南 2 链代码开发
}
if Avalbytes == nil {
jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
return shim.Error(jsonResp) }
jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
fmt.Printf("Query Response:%s\n", jsonResp) // 返回转账金额
return shim.Success(Avalbytes) }
func main() {
err := shim.Start(new(SimpleChaincode)) if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err) }
}
2.3.4 链代码示例(2.0 风格)
Fabric架构版本的区块链服务:
如下是一个账户转账的链代码示例(2.0风格)仅供安装实例化,若您需要调测请参考 Fabric官方示例中的链代码。
package main import ( "errors"
"fmt"
"strconv"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// 链码实现
type ABstore struct { contractapi.Contract }
// 初始化链码数据,实例化或者升级链码时自动调用
func (t *ABstore) Init(ctx contractapi.TransactionContextInterface, A string, Aval int, B string, Bval int) error { // 使用println函数输出的信息会记录在链码容器日志中
fmt.Println("ABstore Init") var err error
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) // 将状态数据写入账本
err = ctx.GetStub().PutState(A, []byte(strconv.Itoa(Aval))) if err != nil {
return err }
err = ctx.GetStub().PutState(B, []byte(strconv.Itoa(Bval))) if err != nil {
return err }
return nil }
// A转账X给B
func (t *ABstore) Invoke(ctx contractapi.TransactionContextInterface, A, B string, X int) error { var err error
var Aval int var Bval int
开发指南 2 链代码开发
// 从账本获取状态数据
Avalbytes, err := ctx.GetStub().GetState(A) if err != nil {
return fmt.Errorf("Failed to get state") }
if Avalbytes == nil {
return fmt.Errorf("Entity not found") }
Aval, _ = strconv.Atoi(string(Avalbytes)) Bvalbytes, err := ctx.GetStub().GetState(B) if err != nil {
return fmt.Errorf("Failed to get state") }
if Bvalbytes == nil {
return fmt.Errorf("Entity not found") }
Bval, _ = strconv.Atoi(string(Bvalbytes)) // 执行转账
Aval = Aval - X Bval = Bval + X
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) // 将状态数据重新写回账本
err = ctx.GetStub().PutState(A, []byte(strconv.Itoa(Aval))) if err != nil {
return err }
err = ctx.GetStub().PutState(B, []byte(strconv.Itoa(Bval))) if err != nil {
return err }
return nil }
// 账户注销
func (t *ABstore) Delete(ctx contractapi.TransactionContextInterface, A string) error { // 从账本中删除账户状态
err := ctx.GetStub().DelState(A) if err != nil {
return fmt.Errorf("Failed to delete state") }
return nil }
// 账户查询
func (t *ABstore) Query(ctx contractapi.TransactionContextInterface, A string) (string, error) { var err error
// 从账本获取状态数据
Avalbytes, err := ctx.GetStub().GetState(A) if err != nil {
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
return "", errors.New(jsonResp) }
if Avalbytes == nil {
jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
return "", errors.New(jsonResp) }
jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
fmt.Printf("Query Response:%s\n", jsonResp) return string(Avalbytes), nil
}
开发指南 2 链代码开发
func main() {
cc, err := contractapi.NewChaincode(new(ABstore)) if err != nil {
panic(err.Error()) }
if err := cc.Start(); err != nil {
fmt.Printf("Error starting ABstore chaincode: %s", err) }
}
2.3.5 链代码调测
对链代码进行调测,可以使用CloudIDE进行单元测试,具体请参见以下操作。
操作步骤
步骤1 进入CloudIDE首页。
步骤2 创建链代码工程,具体请参见创建并启动IDE实例。
图2-1 创建链代码工程
步骤3 链代码调测。
说明
以下截图对1.4风格(使用shim包)的链代码进行调测。
开发指南 2 链代码开发
----结束
2.4 Java 语言链代码开发
2.4.1 链代码结构
本章以Java语言为例来介绍。链代码即一个Java项目,创建好文件后进行函数开发等 操作。
约束与限制
Java链代码仅支持Fabric 2.2及以上版本。
Java链代码仅适用于Fabric架构版本的区块链服务。
链代码接口
链代码启动必须通过调用shim包中的start方法。实际开发中, 您需要自行定义一个 类,来继承ChaincodeBase。以下为继承时必须重写的方法:
public class SimpleChaincodeSimple extends ChaincodeBase { @Override
public Response init(ChaincodeStub stub) { }
@Override
public Response invoke(ChaincodeStub stub) { }
}
● init方法:在链代码实例化或升级时被调用,完成初始化数据的工作。
● Invoke方法:更新或查询帐本数据状态时被调用, 需要在此方法中实现响应调用 或查询的业务逻辑。
链代码结构
Java语言的链代码结构如下:
开发指南 2 链代码开发
package main
// 引入必要的包,系统自动操作,只要在maven或gradle中配置即可 import org.hyperledger.fabric.shim.ChaincodeBase;
import org.hyperledger.fabric.shim.ChaincodeStub;
public class SimpleChaincodeSimple extends ChaincodeBase { @Override
public Response init(ChaincodeStub stub) {
// 在该方法中实现链代码初始化或升级时的处理逻辑 // 编写时可灵活使用stub中的API
}
@Override
public Response invoke(ChaincodeStub stub) {
// 在该方法中实现链代码运行中被调用或查询时的处理逻辑 // 编写时可灵活使用stub中的API
}
//主函数,需要调用shim.Start()方法 public static void main(String[] args) { new SimpleChaincode().start(args);
} }
2.4.2 链代码相关的 API
Fabric源码包中的shim包提供了如下几种类型的接口,您可以参考使用:
● 参数解析API:调用链代码时需要给被调用的目标函数/方法传递参数,该API提供 解析这些参数的方法。
● 账本状态数据操作API:该API提供了对账本数据状态进行操作的方法,包括对状 态数据的查询及事务处理等。
● 交易信息获取API:获取提交的交易信息的相关API。
● 其他API:其他的API,包括事件设置、调用其他链代码操作。
2.4.3 链代码示例
如下是一个读写数据的链代码示例,您也可以参考Fabric官方示例中其他链代码。
说明
新建Java项目时,您可以选择新建maven或者gradle项目,以导入依赖包。本示例以gradle项目 为例。
/* 导入此段代码到项目的build.gradle前,请删除或注释build.gradle中原有内容 */
buildscript { repositories { mavenLocal()
maven{ url "https://mirrors.huaweicloud.com/repository/maven/" } }
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4' } }
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'java'
sourceCompatibility = 1.8
// 依赖包所用的代码仓库,将从所选地址搜寻并下载依赖包 repositories {
mavenLocal()
maven{ url "https://mirrors.huaweicloud.com/repository/maven/" }
开发指南 2 链代码开发
maven{ url "https://jitpack.io" }/* 若Json-Schema无法导入,可以尝试加入此库 */
}
// 引入代码所需的依赖包 dependencies {
compile group: 'org.hyperledger.fabric-chaincode-java', name: 'fabric-chaincode-shim', version: '2.2.+' testCompile group: 'junit', name: 'junit', version: '4.12' // testCompile表明仅在调测时使用此包 testCompile 'org.mockito:mockito-core:2.4.1'
}
shadowJar {
baseName = 'chaincode' version = null
classifier = null manifest {
// 需要与继承ChaincodeBase类接口的类路径保持一致
attributes 'Main-Class': 'org.hyperledger.fabric.example.SimpleChaincode' }
}// 以下为SimpleChaincode类内中的内容
package org.hyperledger.fabric.example;//根据链代码文件实际位置编写,一般由系统自动生成 // 引入必要的包,系统自动操作,只要在maven或gradle中配置即可
import java.util.List;
import com.google.protobuf.ByteString;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperledger.fabric.shim.ChaincodeBase;
import org.hyperledger.fabric.shim.ChaincodeStub;
import static java.nio.charset.StandardCharsets.UTF_8;
// SimpleChaincode example simple Chaincode implementation public class SimpleChaincode extends ChaincodeBase {
private static Log logger = LogFactory.getLog(SimpleChaincode.class);
@Override
public Response init(ChaincodeStub stub) { logger.info("Init");
return newSuccessResponse();
}
@Override
public Response invoke(ChaincodeStub stub) { try {
logger.info("Invoke java simple chaincode");
String func = stub.getFunction();
List<String> params = stub.getParameters();
if (func.equals("insert")) { return insert(stub, params);
}
if (func.equals("query")) { return query(stub, params);
}
return newErrorResponse("Invalid invoke function name. Expecting one of: [\"insert\", \"query\"]");
} catch (Throwable e) { return newErrorResponse(e);
} }
// The Insert method implements the data storage function and stores the key-value on the chain private Response insert(ChaincodeStub stub, List<String> args) {
if (args.size() != 2) {
return newErrorResponse("Incorrect number of arguments. Expecting 2");
}
String key = args.get(0);
String val = args.get(1);
开发指南 2 链代码开发
stub.putState(key, ByteString.copyFrom(val, UTF_8).toByteArray());
return newSuccessResponse();
}
// The Query method implements the data query function by invoking the API to query the value of the key // API to query the value corresponding to a key
private Response query(ChaincodeStub stub, List<String> args) { if (args.size() != 1) {
return newErrorResponse("Incorrect number of arguments. Expecting name of the person to query");
}
String key = args.get(0);
// Get the value of key
String val = stub.getStringState(key);
if (val == null) {
String jsonResp = "{\"Error\":\"Null val for " + key + "\"}";
return newErrorResponse(jsonResp);
}
logger.info(String.format("Query Response:\nkey: %s, val: %s\n", key, val));
return newSuccessResponse(val, ByteString.copyFrom(val, UTF_8).toByteArray());
}
public static void main(String[] args) { new SimpleChaincode().start(args);
} }
2.4.4 链代码调测
对链代码进行调测,主要是使用MockStub进行单元测试。本章中测试的链代码获取方 法:登录区块链服务管理控制台,进入“体验中心 > 应用案例”,单击“Java示例 Demo-Java SDK Demo”中“Chaincode_Java_Demo”的“下载”按钮。
添加依赖
使用mock()方法,需要添加mockito相关依赖。
● gradle版本:
在build.gradle文件中的dependencies内添加如下配置依赖,注意不是buildscript 内的dependencies:
testCompile 'org.mockito:mockito-core:2.4.1'
● maven版本:
在pom.xml文件中的dependencies(若无则添加)内添加如下配置依赖:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.4.1</version>
</dependency>
编写测试代码
若创建项目时没有test文件夹,在src下新建文件夹,并如图在Gradle Source Sets里面 选择“test\java”,然后创建测试文件:SimpleChaincodeTest.java,如图所示:
开发指南 2 链代码开发
SimpleChaincodeTest.java测试代码内容:
import org.hyperledger.fabric.example.SimpleChaincode;
import org.hyperledger.fabric.shim.Chaincode;
import org.hyperledger.fabric.shim.ChaincodeStub;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public final class SimpleChaincodeTest { @Test
public void initTest() {
SimpleChaincode sc = new SimpleChaincode();
ChaincodeStub stub = mock(ChaincodeStub.class);
Chaincode.Response resp = sc.init(stub);
Assert.assertEquals(resp.getStatus(), Chaincode.Response.Status.SUCCESS);
} @Test
public void insertTest() {
SimpleChaincode sc = new SimpleChaincode();
ChaincodeStub stub = mock(ChaincodeStub.class);
when(stub.getFunction()).thenReturn("insert");
List<String> args = new ArrayList<>();
args.add("a");
args.add("100");
when(stub.getParameters()).thenReturn(args);
Chaincode.Response resp = sc.invoke(stub);
开发指南 2 链代码开发
Assert.assertEquals(resp.getStatus(), Chaincode.Response.Status.SUCCESS);
} @Test
public void insertTooManyArgsTest() {
SimpleChaincode sc = new SimpleChaincode();
ChaincodeStub stub = mock(ChaincodeStub.class);
when(stub.getFunction()).thenReturn("insert");
List<String> args = new ArrayList<>();
args.add("a");
args.add("100");
args.add("b");
args.add("100");
when(stub.getParameters()).thenReturn(args);
Chaincode.Response resp = sc.invoke(stub);
Assert.assertEquals(resp.getMessage(), "Incorrect number of arguments. Expecting 2");
} @Test
public void queryTest() {
SimpleChaincode sc = new SimpleChaincode();
ChaincodeStub stub = mock(ChaincodeStub.class);
when(stub.getFunction()).thenReturn("query");
List<String> args = new ArrayList<>();
args.add("a");
when(stub.getParameters()).thenReturn(args);
when(stub.getStringState("a")).thenReturn("100");
Chaincode.Response resp = sc.invoke(stub);
Assert.assertEquals(resp.getMessage(), "100");
} @Test
public void queryNoExistTest() {
SimpleChaincode sc = new SimpleChaincode();
ChaincodeStub stub = mock(ChaincodeStub.class);
when(stub.getFunction()).thenReturn("query");
List<String> args = new ArrayList<>();
args.add("a");
when(stub.getParameters()).thenReturn(args);
when(stub.getStringState("a")).thenReturn(null);
Chaincode.Response resp = sc.invoke(stub);
Assert.assertEquals(resp.getMessage(), "{\"Error\":\"Null val for a\"}");
} }
执行调测
在SimpleChaincodeTest.java中,单击SimpleChaincodeTest方法前的“Run Test”按 钮,执行测试。
开发指南 2 链代码开发
执行成功如图所示,表示链代码调测无问题:
执行失败示例如图所示,请根据失败提示修改链代码或者检查调测代码的逻辑:
开发指南 2 链代码开发
3 应用程序开发
3.1 概述
用户的应用程序通过链代码与账本数据进行交互。应用程序开发可使用的语言比较广 泛,如Golang、Solidity、Java、C++、Python、Node.js等。应用程序和链代码开发 语言无强对应关系,只要应用程序能通过SDK来调用链代码即可。
3.2 开发前准备
用户的应用程序通过链代码与账本数据进行交互。应用程序开发可使用的语言比较广 泛,如Golang、Solidity、Java、C++、Python、Node.js等。应用程序和链代码开发 语言无强对应关系,只要应用程序能通过SDK来调用链代码即可。
1. 您需要购买自己的区块链服务实例。
Fabric架构版本的区块链服务支持在CCE集群和边缘集群上部署,具体可参见 Fabric服务部署。
2. 您需要获取对应服务实例的SDK配置文件,具体可参见访问Fabric服务区块链。
3.3 应用程序开发
您需要自行开发应用程序业务逻辑代码。针对Fabric架构版本的区块链服务,应用开发 过程中可使用我们提供的国密加密SDK,也可使用Fabric官方社区提供的和您自身的服 务实例版本匹配的SDK。
另外,Fabric架构版本的区块链服务提供同态加密库供您使用,相关资料及资源请参见 同态加密。
说明
版本信息选择的是3.X.X版本(Fabric 1.4.0),则Fabric SDK对应选择1.4版本;
配置组织 ID
Fabric架构版本的区块链服务:
开发指南 3 应用程序开发
您需要修改应用程序中配置服务实例组织ID的相关代码,下载证书文件解压后的peer 文件包括目录名和对应组织ID。
如下图所示,仅供示例参考,请以实际操作的证书文件为准。
证书文件解压后目录名是6c448740d50d6197dc86b36b0abd0bc639a788a7.peer,组 织ID为6c448740d50d6197dc86b36b0abd0bc639a788a7。
配置 SDK 文件
1. 您需要修改应用程序中SDK配置文件相关代码,如下面示例所示,您需要填写正 确的SDK配置文件绝对路径。
var (
configFile = "/root/gosdkdemo/config/go-sdk-demo-channel-sdk-config.yaml"
org = " 6c448740d50d6197dc86b36b0abd0bc639a788a7"
)
2. 如果您下载SDK配置文件时填写的证书存放路径与实际不符,您需要修改SDK配置 文件中所有证书相关路径。
开发指南 3 应用程序开发
4 示例 Demo
4.1 GO SDK Demo
本节提供了一个基于Go SDK的Demo,帮助用户开发自己的Go客户端应用程序。
说明
本Demo仅适用于Hyperledger Fabric增强版的区块链服务。
准备工作
● 准备弹性云服务器。
● 在弹性云服务器上安装golang环境,Go版本要求:1.12及以上,1.16以下
(>=1.12,<1.16)。
● 获取Go SDK源码,获取方法:登录区块链服务管理控制台,进入“体验中心 > 应 用案例”,单击“GO示例Demo-GO SDK Demo”中Go应用程序源码的“下载”
按钮。
购买区块链服务
购买区块链服务,具体请参见服务部署。
安装及实例化链代码
本示例使用链代码文件获取方法:登录区块链服务管理控制台,进入“体验中心 > 应 用案例”,单击“GO示例Demo-GO SDK Demo”中Go语言示例链代码的“下载”按 钮。
参考章节:用户指南-区块链管理-链代码管理。
下载 SDK 和证书
步骤1 登录区块链服务管理控制台。
步骤2 在“服务管理”界面,在服务卡片中,单击“获取客户端配置”。
开发指南 4 示例 Demo
步骤3 在新打开的页面,勾选“SDK文件”,SDK配置参数如下:
参数 值
链代码名称 chaincode
说明链代码名称需要和链代码安装&实例化时的一致。
证书存放路径 /root/gosdkdemo/config
通道名称 channel
组织&Peer节点 保持系统默认
勾选“共识节点证书”。
勾选“Peer节点证书”,指定节点组织选择organization,勾选“管理员证书”。
步骤4 单击“下载”。下载SDK配置文件、orderer组织的管理员证书和organization组织的 管理员证书。
----结束
部署应用
1. 将Go SDK源码下载至准备的弹性云服务器“/root”路径下并解压。
下载方法:登录区块链服务管理控制台,进入“体验中心 > 应用案例”,单击
“GO示例Demo-GO SDK Demo”中Go应用程序源码的“下载”按钮。
2. 将下载SDK和证书步骤中的zip文件解压后,把configs文件夹中的orderer文件夹、
peer文件夹、sdk-config.json、sdk-config.yaml文件全部拷贝到/root/
gosdkdemo/config/目录下。
3. 在代码中找到“/gosdkdemo/src/main.go”文件,进行以下修改:
a. 将configFile中的值修改为实际的SDK配置文件名称,例如:demo-channel- sdk-config.yaml。
b. 将org的值修改为organization对应的组织哈希值。
在通道管理页面,单击“查看节点”获取组织的哈希值(MSP标识去掉
“MSP”后缀即为对应组织的哈希)。
var (
configFile = "/root/gosdkdemo/config/go-sdk-demo-channel-sdk-config.yaml"
org = " 9103f17cb6b4f69d75982eb48bececcc51aa3125"
)
4. 使用go mod方式配置GOPATH路径,请根据实际安装路径进行配置。
a. 设置环境变量GO111MODULE为on。
export GO111MODULE=on
b. go.mod文件如图所示,用户需要根据实际安装路径修改replace代码。
module main go 1.15
// 指定导入的依赖包及其版本 require (
github.com/bitly/go-simplejson v0.5.0
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869// indirect github.com/ghodss/yaml v1.0.0
github.com/hyperledger/fabric-sdk-go v1.0.0 github.com/pkg/errors v0.9.1
开发指南 4 示例 Demo
github.com/spf13/viper v1.7.1 ) // 以项目路径为/root/gosdkdemo/src为例
replace github.com/hyperledger/fabric-sdk-go => /root/gosdkdemo/src/github.com/hyperledger/
fabric-sdk-go
5. 找到“gosdkdemo/src”路径下的main.go文件,执行如下命令:
go run main.go
通过内存传入私钥
如果用户需要对私钥文件进行加密,并在demo中解密后传入FabricSDK。
1. 对于TLS私钥
在main.go文件的initializeSdk函数中,按如下方式调用函数:
encryptedtlsKey,err := GetTlsCryptoKey(org) //从配置文件指定路径下读取加密过的TLS私钥 //用户按照自行规定的加解密方法对encryptedtlsKey进行解密得到decryptedTlsKey字符串
SetClientTlsKey(decryptedTlsKey) //将解密后的TLS私钥传入Fabric-SDK,实现通过内存传入TLSKey
2. 对于MSP私钥
在main.go文件的insert函数中,按如下方式调用函数:
encryptedbytekey,_:= GetPrivateKeyBytes(org) //从配置文件指定路径下读取加密过的MSP私钥 //用户按照自行规定的加解密方法对encryptedbytekey进行解密得到decryptedKey字符串 SetPrivateKey(decryptedKey) //将解密后的MSP私钥赋值给全局变量privateKey并通过该变量传入
常用 API 接口
Fabric-sdk-go的主要入口是FabricSDK类,这个可以通过NewSDK()方法分别可以生 成。
Fabric-sdk-go的常用操作基本都可以用这四个client实现:FabricClient,
ChannelClient,ChannelMgmtClient,ResourceMgmtClient。
● FabricSDK
FabricSDK在pkg\fabsdk\fabsdk.go中,通过New ()方法生成object。New ()方法 支持可变参数Option,以下是生成FabricSDK的例子:
var opts []fabsdk.Option
opts = append(opts, fabsdk.WithOrgid(org))
opts = append(opts, fabsdk.WithUserName("Admin")) sdk, err = fabsdk.New(config.FromFile(configFile), opts...)
configFile是SDK配置文件的路径。OrgId是SDK配置文件中的组织id。
FabricSDK在def/fabapi/fabapi.go中,通过NewSDK()方法生成object。
NewSDK()方法有一个Options参数,以下是生成Options参数的例子:
deffab.Options{ConfigFile: configFile, LoggerFa logging.LoggerProvider(), UserName: sysadmin}
ConfigFile是SDK配置文件的路径。LoggerFactory是可选的,不提供的话default 会log到console。
● FabricClient
FabricClient主要有以下常用的接口。
开发指南 4 示例 Demo
接口名称 描述 参数值 返回值 CreateChann
el 创建
Channel的 接口,用于 创建channel。
request
CreateChannelReq uest
txn.TransactionID, error
QueryChann
elInfo 查询 Channel的 接口,用于 查询Channel的 信息。
name string, peers []Peer
Channel, error
InstallChainc
ode 安装链码的
接口,安装 链码到区块 链中。
request
InstallChaincodeRe quest
[]*txn.TransactionPropos lResponse, string, error
InstallChainc
ode 安装链码的
接口,安装 链码到区块 链中。
request
InstallChaincodeRe quest
[]*txn.TransactionPropos lResponse, string, error
QueryChann
els 查询
channel的 接口,查询 区块链中已 创建的通 道。
peer Peer *pb.ChannelQueryRespo nse, error
QueryInstall edChaincode s
查询已安装 链码的接 口,查询区 块链中已安 装的链码。
peer Peer *pb.ChaincodeQueryResp onse, error
● ChannelClient
ChannelClient主要包括链码查询和链码调用两类接口。
接口名称 描述 参数值 返回值
Query 链码查询接口,调用链
码进行查询。 request QueryRequest
[]byte, error
QueryWithOp
ts 带options的链码查询接 口,与Query类似,但 是可以通过QueryOpts 指定notifier, peers,和 timeout。
request
QueryRequest, opt
QueryOpts
[]byte, error
开发指南 4 示例 Demo
接口名称 描述 参数值 返回值 ExecuteTx 链码调用接口,用于链
码的调用。 request
ExecuteTxRequ est
TransactionID, error
ExecuteTxWit
hOpts 带options的链码调用接 口,与ExecuteTx类 似,但是可以通过 ExecuteTxOpts指定 notifier, peers,和 timeout。
request
ExecuteTxRequ estopt
ExecuteTxOpts
TransactionID, error
● ChannelMgmtCLient
ChannelMgmtClient 只有两个接口SaveChannel(req SaveChannelRequest) error 和SaveChannelWithOpts(req SaveChannelRequest, opts
SaveChannelOpts) error 这两个接口是用于创建channel用的,这两个接口里面 具体实现会调用到FabricClient里createChannel()接口。
● ResourceMgmtClient
ResourceMgmtClient主要就是与链码生命周期相关的接口和一个peer加入通道的 接口。
说明
链码的删除接口为我们增加的接口,目前只实现了删除链码安装包的功能。
接口名称 描述 参数值 返回值
InstallCC 安装链码,用于安
装链码。 reqInstallCCReq
uest []InstallCCRespo nse, error InstallCCWithOp
ts 带options的链码安
装,与InstallCC类 似,但是可以通过 InstallCCOpts指定 peers。
reqInstallCCReq uest,opts InstallCCOpts
[]InstallCCRespo nse, error
InstantiateCC 实例化链码接口,
用于实例化链码。
channelID string,reqInstant iateCCRequest
error
InstantiateCCWi
thOpts 带options的链码实 例化,与InstantiateCC类 似,但是可以通过 InstantiateCCOpts 指定peers和 timeout。
channelID string,reqInstant iateCCRequest, optsInstantiateC COpts
error
UpgradeCC 升级链码,用于链
码的升级。 channelID string,reqUpgrad eCCRequest
error
开发指南 4 示例 Demo
接口名称 描述 参数值 返回值 UpgradeCCWith
Opts 带options的链码升 级,与UpgradeCC 类似,但是可以通 过UpgradeCCOpts 指定peers和 timeout。
channelID string,reqUpgrad eCCRequest, optsUpgradeCC Opts
error
DeleteCC 删除链码,用于链 码的删除,目前只 有删除安装包的功 能。
channelID string,reqDelete CCRequest
error
DeleteCCWithO
pts 带options的链码删 除,与DeleteCC类 似,但是可以通过 DeleteCCWithOpt s指定peers和 timeout。
channelID string,reqDelete CCRequest,opts DeleteCCOpts
error
JoinChannel Peers加入Channel 的接口,用于peers 加入Channel。
channelID string error
JoinChannelWit
hOpts 带options的Peers 加入Channel的接 口,与JoinChannel类 似,但是可以通过 JoinChannelOpts 指定peers。
channelID string,optsJoinC hannelOpts
error
说明
带options的接口都可以指定peers,peers可通过def/fabapi/pkgfactory.go 里的 NewPeer(userName string, orgName string, url string, certificate string,
serverHostOverride string, config config.Config) (fab.Peer, error) 生成。这个method比 原生的NewPeer多两个参数userName, orgName, 这两个参数用于peer双向tls找到对应的 tls证书。
调用合约
Main.go是一个简单的客户端应用示例程序,主要是为了方便用户熟悉客户端开发的流 程,主要包含以下步骤:
//1.导入相关包:Sdk包中提供了一些API,以便用户的应用程序能够访问链代码。
import ( "fmt"
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" ……
)//2.创建文件配置:这部分封装了应用开发必要的一些公共配置,包括sdk配置文件路径、组织名
var (
configFile = "/root/fabric-go-demo/config/go-sdk-demo-channel-sdk-config.yaml"
org = "9103f17cb6b4f69d75982eb48bececcc51aa3125"
开发指南 4 示例 Demo
……
)//3.加载配置文件
loadConfig() //4. 初始化sdk initializeSdk()
//5. 执行链代码,将数据写入账本,key = "testuser",value= "100"
insert("insert",[][]byte{
[]byte("testuser"), []byte("100"),
})//6.查询链代码,输出查询结果,key = "testuser"
query("query", [][]byte{
[]byte("testuser"), })
表4-1 调用函数介绍
函数名 说明
getOptsToInitializeSDK 解析配置文件,创建并返回fabsdk.Option对象。
GetDefaultChaincodeId 解析配置文件,返回chaincodeID。
GetDefaultChannel 解析配置文件,返回channelID。
UserIdentityWithOrgAn
dName 用户身份验证,输入为组织名和用户名,返回为验证结
果。
ChannelClient 创建*channel.Client对象,输入为组织名、用户名以及通 道ID,返回*channel.Client对象。
insert 将数据写入账本,输入参数为链码的对应方法名称以及
要插入的键值对,返回为写入的结果。
query 查询链上信息,输入参数为链码的对应方法名称以及要
查询的数据,返回为查询的结果。
4.2 Java SDK Demo
本节提供一个基于Java SDK的Demo,帮助用户开发自己的Java客户端应用程序。
说明
本Demo仅适用于Hyperledger Fabric增强版的区块链服务。
准备工作
1. 开通CloudIDE(或者用户自己准备JDK、maven和eclipse/IntelliJ IDEA)。
CloudIDE是DevCloud的云端开发环境服务,向开发者提供按需配置、快速获取的 工作空间(包含编辑器和运行环境)。
在CloudIDE上创建一个空的Java工程,如图4-1所示。
开发指南 4 示例 Demo
图4-1 CloudIDE 上创建一个空的 Java 工程
2. 下载Java SDK示例源码,获取方法:登录区块链服务管理控制台,进入“体验中 心 > 应用案例”,在“Java示例Demo-Java SDK Demo”下方,单击
“App_Java_Src_Demo”中Java项目源码的“下载”按钮。
3. 购买区块链服务、安装链代码及实例化链代码操作,请参见《快速入门》。
部署应用
1. 下载SDK和证书。
a. 在“服务管理”界面,在服务卡片中,单击“获取客户端配置”。
b. 勾选“SDK文件”,SDK配置参数如下:
参数名称 说明
链代码名称 chaincodedemo
证书存放路径 /home/user/javasdkdemo_src/config 通道名称 channel
组织&Peer节 点
选择通道中所有节点组织
勾选“共识节点证书”。
勾选“Peer节点证书”,指定节点组织选择organization,勾选“管理员证 书”。
c. 单击“下载”,下载SDK配置文件、demo-orderer组织的管理员证书和 organization组织的管理员证书。
2. 拷贝并解压。
a. 先下载工程源代码javasdkdemo_src.zip文件并解压。
获取方法:登录区块链服务管理控制台,进入“体验中心 > 应用案例”,在
“Java示例Demo-Java SDK Demo”下方,单击“App_Java_Src_Demo”中 Java项目源码的“下载”按钮。
b. 将1步骤中的zip文件解压,把configs文件夹中的orderer文件夹、peer文件 夹、sdk-config.json、sdk-config.yaml文件全部拷贝到javasdkdemo_src目录
开发指南 4 示例 Demo
下的config目录下。然后再将javasdkdemo_src目录压缩成 javasdkdemo_src.zip包。
3. 部署应用。
a. 将新压缩的javasdkdemo_src工程源代码javasdkdemo_src.zip文件上传到 CloudIDE开发环境中。
CloudIDE编译工程如下所示:
b. 打开工程后等待一会,会自动下载工程依赖的包,然后按下图操作执行 Run,就会得到预期结果。
注意
● javasdkdemo工程中config目录下demo-channel-sdk-config.yaml文 件,必须与javasdkdemo/src/main/java/handler/Main.java文件中 helper.setConfigCtx("config/demo-channel-sdk-config.yaml")代码路 径相同,保证可以正常运行Main.java。
● 客户端app交易的时候,如果指定了未实例化的组织和peer,那么首次交 易会超时失败,请您重新运行即可正常交易。
每成功执行一次,表示向区块链存入一对键值对,<testuser,100>;在区块链 上查询键值为testuser的value值为100。也可以通过区块链浏览器可以查看交 易记录。
开发指南 4 示例 Demo
通过内存传入私钥
如果用户需要对私钥文件进行加密,并在demo中解密后传入FabricSDK。
对于MSP私钥:在FabricHelper文件的genFabricUser函数中,按如下方式调用函数:
//从配置文件指定路径下读取加密过的MSP私钥
String adminPrivateKeyString = extractPemString(msp, "keystore");
//对adminPrivateKeyString 进行解密得到decryptedKey字符串 //将解密后的MSP私钥重新赋值给变量adminPrivateKeyString String adminPrivateKeyString = decryptedKey;
说明
当前不支持通过内存传入TLS私钥。
常用接口
Fabric-sdk-java的使用主要分为:加密套件配置,通道操作,链码操作。
加密套件配置部分,根据配置文件内容,设置SDK里面的加密套件服务提供者、 加密 类型、安全级别等。
● 通道操作包括:通道查询、通道创建、通道加入、通道删除等。
● 链码操作包括:链码安装、链码实例化、链码查询、链码调用等。
使用Fabric-sdk-java主要会使用HFClient和Channel两个类,其导入路径分别为:
org.hyperledger.fabric.sdk.HFClient和org.hyperledger.fabric.sdk.Channel。
更多的api接口请参考Fabric官网。
4.3 Gateway Java Demo
本节提供一个基于Fabric Gateway Java的Demo,Fabric Gateway Java对Java SDK进 行了封装,简化了代码量,帮助用户开发自己的Java客户端应用程序。
说明
本Demo仅适用于Hyperledger Fabric增强版的区块链服务。
准备工作
1. 开通CloudIDE(或者用户自己准备JDK、maven和eclipse/IntelliJ IDEA)。
CloudIDE是DevCloud的云端开发环境服务,向开发者提供按需配置、快速获取的 工作空间(包含编辑器和运行环境)。
在CloudIDE上创建一个空的Java工程,如图4-2所示。
开发指南 4 示例 Demo
图4-2 CloudIDE 上创建一个空的 Java 工程
2. 下载Java SDK示例源码,获取方法:登录区块链服务管理控制台,进入“体验中 心 > 应用案例”,在“Java示例Demo-Java SDK Demo”下方,单击
“App_Gateway_Java_Demo”中Java项目源码的“下载”按钮。
3. 购买区块链服务、安装链代码及实例化链代码操作,请参见《快速入门》。
部署应用
1. 下载SDK和证书。
a. 在“服务管理”界面,在服务卡片中,单击“获取客户端配置”。
b. 勾选“SDK文件”,SDK配置参数如下:
参数名称 说明
链代码名称 chaincodedemo
证书存放路径 /home/user/gatewayjavademo/config 通道名称 channel
组织&Peer节 点
选择通道中所有节点组织
勾选“共识节点证书”。
勾选“Peer节点证书”,指定节点组织选择organization,勾选“管理员证 书”。
c. 单击“下载”,下载SDK配置文件、demo-orderer组织的管理员证书和 organization组织的管理员证书。
2. 拷贝并解压。
a. 先下载工程源代码gatewayjavademo.zip文件并解压。
获取方法:登录区块链服务管理控制台,进入“体验中心 > 应用案例”,在
“Java示例Demo-Java SDK Demo”下方,单击
“App_Gateway_Java_Demo”中Java项目源码的“下载”按钮。
b. 将1步骤中的zip文件解压,把configs文件夹中的orderer文件夹、peer文件 夹、sdk-config.json、sdk-config.yaml文件全部拷贝到gatewayjavademo目
开发指南 4 示例 Demo
录下的config目录下。然后再将gatewayjavademo目录压缩成 gatewayjavademo.zip包。
3. 部署应用。
a. 将新压缩的gatewayjavademo工程源代码gatewayjavademo.zip文件上传到 CloudIDE开发环境中。
CloudIDE编译工程如下所示:
b. 打开工程后等待一会,会自动下载工程依赖的包,然后按下图操作执行 Run,就会得到预期结果。
注意
● gatewayjavademo工程中config目录下demo-channel-sdk-config.yaml 文件,必须与gatewayjavademo/src/main/java/handler/Main.java和 MainForMultiTask文件中helper.setConfigCtx("config/demo-channel- sdk-config.yaml")代码路径相同,保证可以正常运行Main.java。
● 客户端app交易的时候,如果指定了未实例化的组织和peer,那么首次交 易会超时失败,请您重新运行即可正常交易。
每次成功执行Main.java,将会执行basicTransactionSample、
commitListenerSample、contractListenerSample、blockListenerSample四 个方法,向区块链存入多对键值对;可以通过区块链浏览器可以查看交易记 录。
开发指南 4 示例 Demo