上篇文章中我们介绍了链码的概念,本文中我们将介绍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链码之二的更多相关文章

  1. Hyperledger Fabric链码之三

    在<Hyperledger Fabric链码之一>和<Hyperledger Fabric链码之二>中我们介绍了链码的定义,并通过dev网络测试了测试了自己编写的链码程序. 本 ...

  2. Hyperledger Fabric链码之一

    什么是链码(Chaincode)? 我们知道区块链有3个发展阶段:区块链1.0,区块链2.0,区块链3.0.其中区块链2.0就是各种区块链平台百花齐放的阶段,区块链2.0最大的特点就是智能合约,我们接 ...

  3. Hyperledger fabric 链码篇GO(四)

    Hyperledger fabric 链码篇GO(四) fabric中的链码也就是我们区块链所认知的智能合约,fabric中可由nodejs,java,go编写,本篇只针对GO语言编写链码.将详细介绍 ...

  4. Hyperledger Fabric开发(二):创建网络

    运行fabric-samples项目中的一个例子:first-network,创建第一个网络(Building Your First Network). 该网络共有4个peer节点,划分为2个组织(o ...

  5. Hyperledger Fabric 实战(十二): Fabric 源码本地调试

    借助开发网络调试 fabric 源码本地调试 准备工作 IDE Goland Go 1.9.7 fabric-samples 模块 chaincode-docker-devmode fabric 源码 ...

  6. Hyperledger Fabric 1.0 从零开始(十二)——fabric-sdk-java应用【补充】

    在 Hyperledger Fabric 1.0 从零开始(十二)--fabric-sdk-java应用 中我已经把官方sdk具体改良办法,即使用办法发出来了,所有的类及文件都是完整的,在文章的结尾也 ...

  7. Hyperledger Fabric 1.0 从零开始(十二)——fabric-sdk-java应用

    Hyperledger Fabric 1.0 从零开始(十)--智能合约 Hyperledger Fabric 1.0 从零开始(十一)--CouchDB 上述两章,最近网上各路大神文章云集,方案多多 ...

  8. 二、主流区块链技术特点及Hyperledger Fabric V0.6版本特点

    一.主流区块链技术特点 二.HyperLedger子项目 三.Hyperledger fabric架构 V0.6逻辑架构: V0.6区块链网络 对应的0.6版本的运行时架构: 0.6版本的架构特点是: ...

  9. Hyperledger Fabric 1.2 --- Chaincode Operator 解读和测试(二)

    本文接上一节是测试部分 搭建一个模拟测试环境 作者将fabric release1.2工程中的 example-e2e进行了改造来进行本次实验: (1)首先我们将examples/e2e_cli/sc ...

随机推荐

  1. cvb源码分析,resful规范,drf,drf序列化组件,95

    1 CBV的源码分析 -Class Base View(基于类的视图) -Function Base View(基于函数的视图) -def as_view 类方法 -def view:类方法内部,闭包 ...

  2. 如何下载官网上下载历史Java版本(老版本Java)

    首先先打开Oracle的官网    -->Oracle 然后选择Trials and Downloads 然后往下翻,选择java(JDK) 然后看到了这个,再往下翻 点他,然后就是选择你想下载 ...

  3. 逻辑回归 vs 决策树 vs 支持向量机(I)

    原文链接:http://www.edvancer.in/logistic-regression-vs-decision-trees-vs-svm-part1/ 分类问题是我们在各个行业的商业业务中遇到 ...

  4. U-Boot bootargs简析

    Linux内核启动时需要一些配置信息,如根文件系统的类型.flash分区情况.串口终端的编号.内存的使用情况等等,而由于U-Boot和Linux Kernel的镜像是独立的两个文件,所以只能两者约定好 ...

  5. Django积木块九——富文本编辑器

    富文本编辑器 前端和后端都可以用富文本编辑器 # pip install django-tinymce # setting 'tinymce' TINYMCE_DEFAULT_CONFIG = { ' ...

  6. python3安装scrapy--记录

    1.pip install wheel 2.url=http://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml 搜索 下载对应的twiset和scrapy版本到本地 ...

  7. 急速安装lnmp 编译版本

    急速安装lnmp 编译版本 安装msyql+PHP 系统centos6.5 安装 开发软件包 已经改成了163的源需要执行下面的代码 官网不自带 libmcrypt libmcrypt-devel w ...

  8. The test form is only available for requests from the local machine

    使用浏览器测试Web服务时出现提示“The test form is only available for requests from the local machine.”的解决办法 在Web服务项 ...

  9. email program (客户端)演变过程有感

    以下内容全部为个人读后感(参考百度百科的相关资料) 首先我认为电子邮件是一个非常伟大的发明,它不仅成本低,而且传输效率快! 关于它的起源,我从百度百科中看到了两种说法     1.1969年10月世界 ...

  10. 腾讯优秀 SDK 汇总

    1. 热修复 -- Tinker 项目地址:http://www.tinkerpatch.com/ SDK地址:https://github.com/Tencent/tinker 集成参考文档: ht ...