Hyperledger Fabric链码之二
上篇文章中我们介绍了链码的概念,本文中我们将介绍Fabric下链码的编写和测试。我们会通过一个简单例子的方式来阐述链码API的使用。
链码API
每一个链码程序都必须实现一个接口Chaincode Interface, 这些方法用来响应接受到的交易。特别的,当链码接收到``Instantiate``和``upgrade``类型的交易时会调用``Init``方法,执行一些需要的初始化,包括应用状态的初始化。当链码接收到``Invoke``类型的交易时候会调用``Invoke``方法来处理交易提议。
链码中调用的其他接口“shim” APIs,用来访问和修改账本,以及调用其他链码操作。
在本文中,我们通过一个简单的资产管理的链码应用来展示这些APIs的使用。
简单资产链码
我们的应用是一个简单的链码,用来在账本上创建资产(key-value健值对)。
选择代码目录位置
如果没有使用Go做过开发,应该首先确定系统中已经安装和配置了Golang。然后为链码应用程序创建一个目录,我们使用如下的命令进行创建:
mkdir -p $GOPATH/src/sacc && cd $GOPATH/src/sacc
现在,我们创建源文件
touch sacc.go
编写链码
现在我们来编写一个具体的链码。每个链码都实现了``Chaincode Interface``,主要是``Init``和``Invoke``函数。因此在编写程序时,我们首先要导入shim接口,以及其他一些包,,比如:``peer protobuf``包。然后,我们添加一个struct ``SimpleAsset``作为链码shim函数的接收器(这块不懂,请补习go语言基本知识)。
代码如下:
.. code:: go
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct {
}
初始化链码
接下来,我们要实现``Init``函数。
.. code:: go //Init在链码初始化的时候调用,用来初始化一些数据
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { }
注意:链码升级也会调用这个函数。当进行链码升级的时候,确保新的链码对``Init``进行了合适的修改。特别的,如果没有迁移或者升级不需要进行一些初始化,那么可以提供一个空的``Init``。
然后,我们通过`ChaincodeStubInterface.GetStringArgs`来获取调用``Init``的参数列表。在我们的例子中,我们期望得到一个健值对。
代码如下:
.. code:: go
// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data, so be careful to avoid a scenario where you
// inadvertently clobber your ledger's data!
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
// 从交易提议中获取参数列表
args := stub.GetStringArgs()
if len(args) != {
return shim.Error("Incorrect arguments. Expecting a key and a value")
}
}
在代码中我们简单检查了参数数量,然后我们将初始的状态存入账本中。我们调用``ChaincodeStubInterface.PutState``并传入健值参数。假设程序执行正常,最后我们返回一个``peer.Response``来表明初始化成功。
.. code:: go // Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data, so be careful to avoid a scenario where you
// inadvertently clobber your ledger's data!
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
// Get the args from the transaction proposal
args := stub.GetStringArgs()
if len(args) != {
return shim.Error("Incorrect arguments. Expecting a key and a value")
} // Set up any variables or assets here by calling stub.PutState() // We store the key and the value on the ledger
err := stub.PutState(args[], []byte(args[]))
if err != nil {
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[]))
}
return shim.Success(nil)
}
调用链码
首先我们添加``Invoke``函数
.. code:: go
// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The 'set'
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
}
像``Init``一样,我们通过``ChaincodeStubInterface``来获取参数。``Invoke`` 的函数参数是链码应用程序调用时候的名字。在我们的例子中,我们的应用程序只有两个函数: "set"和“get”,用来设置资产和资产状态的查询。
1、我们首先调用“ChaincodeStubInterface.GetFunctionAndParameters"来获取函数名字和参数。
2、验证函数参数是否为“set”或者“get”,调用链码应用相关函数,返回成功或者失败的响应(通过“shim.Sucess"或者”shim.Error"函数)。
.. code:: go
// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters()
var result string
var err error
if fn == "set" {
result, err = set(stub, args)
} else {
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
}
// Return the result as success payload
return shim.Success([]byte(result))
}
实现链码应用
前面提到,我们的链码应用程序实现了两个函数,可以通过”Invoke“函数来进行调用。下面是实现方法,分别调用了”ChaincodeStubInterface“的“PutState”和“GetState”接口。
.. code:: go
// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
}
err := stub.PutState(args[], []byte(args[]))
if err != nil {
return "", fmt.Errorf("Failed to set asset: %s", args[])
}
return args[], nil
}
// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
}
value, err := stub.GetState(args[])
if err != nil {
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[], err)
}
if value == nil {
return "", fmt.Errorf("Asset not found: %s", args[])
}
return string(value), nil
}
整个链码程序(合并)
最后,我们添加“main”函数,调用了“shim.Start"函数。
.. code:: go
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct {
}
// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
// Get the args from the transaction proposal
args := stub.GetStringArgs()
if len(args) != {
return shim.Error("Incorrect arguments. Expecting a key and a value")
}
// Set up any variables or assets here by calling stub.PutState()
// We store the key and the value on the ledger
err := stub.PutState(args[], []byte(args[]))
if err != nil {
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[]))
}
return shim.Success(nil)
}
// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters()
var result string
var err error
if fn == "set" {
result, err = set(stub, args)
} else { // assume 'get' even if fn is nil
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
}
// Return the result as success payload
return shim.Success([]byte(result))
}
// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
}
err := stub.PutState(args[], []byte(args[]))
if err != nil {
return "", fmt.Errorf("Failed to set asset: %s", args[])
}
return args[], nil
}
// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
}
value, err := stub.GetState(args[])
if err != nil {
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[], err)
}
if value == nil {
return "", fmt.Errorf("Asset not found: %s", args[])
}
return string(value), nil
}
// main function starts up the chaincode in the container during instantiate
func main() {
if err := shim.Start(new(SimpleAsset)); err != nil {
fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
}
}
链码编译
现在让我们编译刚才编写的链码。
go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim
go build --tags nopkcs11
假设编译过程中没有错误,接下来我们对链码进行测试。
使用dev模式进行测试
正常情况下,链码由节点来启动和维护。然而在“dev“模式下,链码由用户来编译和启动,这种模式便于快速的编码、编译和调试链码。
我们使用预先生成的orderer和channel artifacts来启动”dev“模式下的测试网络。之后,用户可以快速的进行链码的编译和调用。
安装超级账本Fabric-samples
如何你还未安装过samples,首先需要安装。
git clone https://github.com/hyperledger/fabric-samples.git
进入fabric-samples的”chaincode-docker-devmode“目录
.. code:: bash cd chaincode-docker-devmode
下载docker镜像
在”dev“模式下,我们需要4个docker镜像。如果已经clone了”fabric-samples", 执行’download-platfrom-specific-binaries', 会下载需要的镜像。下载成功后,执行’docker images‘,会显示已经下载好的镜像如下:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hyperledger/fabric-tools latest e09f38f8928d 4 hours ago 1.32 GB
hyperledger/fabric-tools x86_64-1.0.0 e09f38f8928d 4 hours ago 1.32 GB
hyperledger/fabric-orderer latest 0df93ba35a25 4 hours ago 179 MB
hyperledger/fabric-orderer x86_64-1.0.0 0df93ba35a25 4 hours ago 179 MB
hyperledger/fabric-peer latest 533aec3f5a01 4 hours ago 182 MB
hyperledger/fabric-peer x86_64-1.0.0 533aec3f5a01 4 hours ago 182 MB
hyperledger/fabric-ccenv latest 4b70698a71d3 4 hours ago 1.29 GB
hyperledger/fabric-ccenv x86_64-1.0.0 4b70698a71d3 4 hours ago 1.29 GB
现在打开3个终端,切换到`chaincode-docker-devmode`。
终端1 - 启动网络
docker-compose -f docker-compose-simple.yaml up
执行上面命令后会启动fabric网路,包括一个`SingleSampleMSPSolo`配置的orderer和一个`dev`模式的peer。同时还会启动另外两个容器 — 一个是链码环境容器,另一个CLI用来跟链码进行交互,同时内嵌了创建和加入通道的命令,因此我们可以立刻进行合约的调用操作。
终端2 — 编译&启动链码
docker exec -it chaincode bash 执行后进入到链码容器中, root@d2629980e76b:/opt/gopath/src/chaincode#
现在,编译你的链码: cd sacc
go build 然后运行链码: CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc
现在链码在peer节点上启动,链码日志表明链码已经成功注册到peer上。注意,在这个阶段链码还没有与通道进行关联,由接下来的`instantiate`来完成。
终端3 — 使用链码
尽管你使用的是`--peer-chaincodedev`模式,你仍然需要安装链码以便于生命周期系统链码能够通过正常的检查,以后该模式下可能会移除这个检查。
我们使用CLI容器来调用链码。
docker exec -it cli bash
peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc
现在调用`invoke`来将`a`的值改为`20`。
peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc
最后,我们查询`a`。 我们会看到a的值已经改为`20`
peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
测试新的链码
我们默认仅加载了`sacc`。 然而,我们可以很容的测试其他的链码,通过将链码添加到`chaincode`子目录下并重新启动你的网络。此时在链码容器中就可以使用这些新添加的链码。
Hyperledger Fabric链码之二的更多相关文章
- Hyperledger Fabric链码之三
在<Hyperledger Fabric链码之一>和<Hyperledger Fabric链码之二>中我们介绍了链码的定义,并通过dev网络测试了测试了自己编写的链码程序. 本 ...
- Hyperledger Fabric链码之一
什么是链码(Chaincode)? 我们知道区块链有3个发展阶段:区块链1.0,区块链2.0,区块链3.0.其中区块链2.0就是各种区块链平台百花齐放的阶段,区块链2.0最大的特点就是智能合约,我们接 ...
- Hyperledger fabric 链码篇GO(四)
Hyperledger fabric 链码篇GO(四) fabric中的链码也就是我们区块链所认知的智能合约,fabric中可由nodejs,java,go编写,本篇只针对GO语言编写链码.将详细介绍 ...
- Hyperledger Fabric开发(二):创建网络
运行fabric-samples项目中的一个例子:first-network,创建第一个网络(Building Your First Network). 该网络共有4个peer节点,划分为2个组织(o ...
- Hyperledger Fabric 实战(十二): Fabric 源码本地调试
借助开发网络调试 fabric 源码本地调试 准备工作 IDE Goland Go 1.9.7 fabric-samples 模块 chaincode-docker-devmode fabric 源码 ...
- Hyperledger Fabric 1.0 从零开始(十二)——fabric-sdk-java应用【补充】
在 Hyperledger Fabric 1.0 从零开始(十二)--fabric-sdk-java应用 中我已经把官方sdk具体改良办法,即使用办法发出来了,所有的类及文件都是完整的,在文章的结尾也 ...
- Hyperledger Fabric 1.0 从零开始(十二)——fabric-sdk-java应用
Hyperledger Fabric 1.0 从零开始(十)--智能合约 Hyperledger Fabric 1.0 从零开始(十一)--CouchDB 上述两章,最近网上各路大神文章云集,方案多多 ...
- 二、主流区块链技术特点及Hyperledger Fabric V0.6版本特点
一.主流区块链技术特点 二.HyperLedger子项目 三.Hyperledger fabric架构 V0.6逻辑架构: V0.6区块链网络 对应的0.6版本的运行时架构: 0.6版本的架构特点是: ...
- Hyperledger Fabric 1.2 --- Chaincode Operator 解读和测试(二)
本文接上一节是测试部分 搭建一个模拟测试环境 作者将fabric release1.2工程中的 example-e2e进行了改造来进行本次实验: (1)首先我们将examples/e2e_cli/sc ...
随机推荐
- C#HTTP请求之POST请求和GET请求
POST请求 /// <summary> /// POST请求获取信息 /// </summary> /// <param name="url"> ...
- ADO.NET学习笔记(1)
ADO.Net是.Net框架中为数据库的访问而封装的一个库.通过这个库我们可以简单便捷的访问数据库,并对数据库进行一些增删改查的操作,目前ADO.Net支持四种主流的数据库,分别是SQL.OLE DB ...
- Linux-3.0.8中基于S5PV210的IRQ模块代码追踪和分析
init/main.c: asmlinkage void start_kernel(void) { ...... early_irq_init(); init_IRQ(); ...... } earl ...
- C++11常用特性的使用经验总结
转自:http://www.cnblogs.com/feng-sc C++11已经出来很久了,网上也早有很多优秀的C++11新特性的总结文章,在编写本博客之前,博主在工作和学习中学到的关于C++11方 ...
- orcale mysql基本的分页查询法
orcale分页查询sql语句: SELECT * FROM ( SELECT A.*, ROWNUM RN FROM (SELECT * FROM TABLE_NAME) A WHERE ROWNU ...
- [python] 3 、基于串口通信的嵌入式设备上位机自动测试程序框架(简陋框架)
星期一, 20. 八月 2018 01:53上午 - beautifulzzzz 1.前言 做类似zigbee.ble mesh...无线网络节点性能测试的时候,手动操作然后看表象往往很难找出真正的原 ...
- Javascript高级编程学习笔记(46)—— 选择符API
选择符API 在DOM1中DOM只提供了 getElementById.getElementsByTagName 两种获取文档元素的方法 很多时候这两种方法往往不能较为方便地获取我们所需要的元素 所以 ...
- Logistic回归Cost函数和J(θ)的推导----Andrew Ng【machine learning】公开课
最近翻Peter Harrington的<机器学习实战>,看到Logistic回归那一章有点小的疑问. 作者在简单介绍Logistic回归的原理后,立即给出了梯度上升算法的code:从算法 ...
- 吴恩达机器学习笔记7-梯度下降III(Gradient descent intuition) --梯度下降的线性回归
梯度下降算法和线性回归算法比较如图: 对我们之前的线性回归问题运用梯度下降法,关键在于求出代价函数的导数,即: 我们刚刚使用的算法,有时也称为批量梯度下降.实际上,在机器学习中,通常不太会给算法起名字 ...
- Git使用详细教程(7):.gitignore使用详解
Git提供了一个.gitignore文件,帮助我们忽略掉一些不想或者不能提交到版本控制器中的文件.这个文件的使用时必须要掌握的. *.a # 忽略所有目录下的.a结尾的文件 !lib.a # 但lib ...