首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Fabric基础架构原理(4):链码 | 赠书活动

Fabric基础架构原理(4):链码 | 赠书活动

作者头像
Henry Zhang
发布2019-04-12 16:38:57
5000
发布2019-04-12 16:38:57
举报
文章被收录于专栏:亨利笔记亨利笔记亨利笔记

题图摄于加州Carmel

本文选自新书《区块链核心技术与应用》,略有删节。上期介绍了Fabric基础架构的通道 ,本次介绍Fabric的智能合约 - 链码。欢迎大家参与文末"点赞即挖矿"的赠书活动。

本文首发于哈希1024社区:

https://hash1024.org/topics/87

智能合约能够部署和运行在区块链环境中,由一段代码来描述相关的业务逻辑。部署后的智能合约在区块链中无法修改,智能合约的执行完全由代码决定,不受人为因素的干扰。一般来说,参与方通过智能合约规定各自权利和义务、触发合约的条件以及结果,一旦该智能合约在区块链环境中运行就可以得出客观、准确的结果。

在 Fabric 中,智能合约也称为链码(chaincode),分为用户链码和系统链码,通常指的是用户链码。链码是访问账本的基本方法,一般是用Go等高级语言编写的、实现规定接口的代码。上层应用可以通过调用链码来初始化和管理账本的状态。只要有适当的权限,链码之间也可以互相调用。(本文来自公众号:亨利笔记)

1. 链码的背书策略

链码实例化时可指定背书策略,当确认节点接收到交易时,节点获知相关链码信息,然后检查该链码的背书策略,判断交易是否满足背书策略,若满足则标注交易为合法。(本文来自公众号:亨利笔记)

背书策略可分为主体 principal(P )和阈值 threshold(T) 两部分,具体如下:

1)principal 指定由哪些成员进行背书。

2)threshold 接受两个输入,分别为阈值t和若干个P的集合n,只要交易中包含了 n 中 t 个成员的背书则认为交易合法。

例如:

  • T(1, ‘A’, ‘B’) 则需要 A,B 中任意成员背书。
  • T(1, ‘A’, T(2, ‘B’, ‘C’)) 则需要 A成员背书或 B,C 成员同时背书。
2. 链码开发

链码的在开发过程中需要实现链码接口,交易的类型决定了哪个接口函数将会被调用,如 instantiate 和 upgrade 类型会调用链码的Init接口,而 invoke 类型的交易则调用了链码的 Invoke 接口。链码的接口定义如下:(本文来自公众号:亨利笔记)

type Chaincode interface {

Init(stub ChaincodeStubInterface) pb.Response

Invoke(stub ChaincodeStubInterface) pb.Response

}

下面通过一个例子讲解链码的开发流程,示例链码根据交易的类型创建键值对并记录到账本中,或者根据键名到账本中查找与之相对应的值。

请先确保 Go 语言环境已经安装并且正确设置 GOPATH 环境变量。

(1)创建链码存放目录

创建keyValueStore目录以存放链码,同时进入目录

mkdir $GOPATH/src/keyValueStore

cd $GOPATH/src/keyValueStore

创建并编辑链码文件 keyValueStore.go 。

(更多文章,请访问哈希1024社区: hash1024.org)

(2)链码源代码分析

1)导入头文件。

链码必须依赖 chaincode shim 包和 peer protobuf 包,它们分别用于链码的控制与数据传输,其次定义 KeyValueStore 类型,作为 chaincode shim 的载体。

package main

import (

"fmt"

"github.com/hyperledger/fabric/core/chaincode/shim"

"github.com/hyperledger/fabric/protos/peer"

)

type KeyValueStore struct {

}

2)实现Init方法。

Init 方法通过 shim.ChaincodeStubInterface 接口来获取实例化链码交易的相关信息,该接口的 GetStringArgs 方法可获取交易传给链码的参数。链码实例化时接收key 和 value 两个参数,因此先对参数个数进行验证,若验证通过,则第一个和第二个参数分别作为 key 和 value 存入到账本中。

把状态存入账本需要借助 shim.ChaincodeStubInterface 接口 PutState 方法来完成,由于账本中的数据都以键值对的形式储存,因此该方法也只接受 key,value两个参数,其中 value 为 byte 格式,里面还包含多个 json 格式的键值对。

由于执行结果需要以消息的形式返回给客户端,因此还需要把返回消息封装成 fabric/protos/peer 中 Response 格式。

值得注意的是,链码升级的时候都会调用 Init 方法,编写升级链码时应注意 Init 方法的实现,以避免重新初始化或覆盖上一版本的账本状态。

func (t * KeyValueStore) Init(stub shim.ChaincodeStubInterface) peer.Response {

args := stub.GetStringArgs()

if len(args) != 2 {

return shim.Error("Incorrect arguments. Expecting a key and a value")

}

err := stub.PutState(args[0], []byte(args[1]))

if err != nil {

return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))

}

return shim.Success(nil)

}

3)实现Invoke方法。

与Init方法类似,Invoke 方法通过 shim.ChaincodeStubInterface 的 GetFunctionAndParameters 方法来获取 invoke 交易的参数,其中返回的 fn 与 args 分别为交易调用的具体函数名以及相应参数,此时 Invoke 方法进一步判断fn的值以进行下一步操作(set或者get),并把操作结果存放在 result 变量中以返回操作结果。

func (t *KeyValueStore) Invoke(stub shim.ChaincodeStubInterface) peer.Response {

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 shim.Success([]byte(result))

}

为了完成对账本的读写,链码还需要实现以下两个方法:

set:把输入的键值对记录在账本中

get:根据键读取账本中与之对应的值

4)实现get和put方法。

正如前面所说,invoke 方法根据 fn 的值来执行相应的 get 或 put 函数,这两个函数也需要 shim.ChaincodeStubInterface 接口来访问账本数据。

func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {

if len(args) != 2 {

return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")

}

err := stub.PutState(args[0], []byte(args[1]))

if err != nil {

return "", fmt.Errorf("Failed to set asset: %s", args[0])

}

return args[1], nil

}

func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {

if len(args) != 1 {

return "", fmt.Errorf("Incorrect arguments. Expecting a key")

}

value, err := stub.GetState(args[0])

if err != nil {

return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)

}

if value == nil {

return "", fmt.Errorf("Asset not found: %s", args[0])

}

return string(value), nil

}

5)实现主函数main():

链码需要在main函数中调用shim.Start()方法用于链码的部署。

func main() {

if err := shim.Start(new(KeyValueStore)); err != nil {

fmt.Printf("Error starting KeyValueStore chaincode: %s", err)

}

}

(3)测试链码

链码的测试需要通过完整的Fabric网络,使用官方提供的例子可以快速构建测试网络,从而简化链码的开发流程。这里介绍搭建测试网络的步骤:

1)安装示例代码库。

2)进入 fabric-samples 目录。

$ cd

$GOPATH/src/github.com/hyperledger/fabric-samples

3)把新编写的链码放入fabric-samples的chaincode目录下。

$ cp -r

$GOPATH/src/keyValueStore ./chaincode

4)进入chaincode-docker-devmode目录并启动网络,命令中会创建了一个名称为myc的通道。

$ cd chaincode-docker-devmode

$ docker-compose -f docker-compose-simple.yaml up -d

5)进入chaincode容器,编译并运行链码。

$ docker exec -it chaincode

$ cd keyValueStore && go build

$ export CORE_PEER_ADDRESS=peer:7051

$ export CORE_CHAINCODE_ID_NAME=mycc:0

$./keyValueStore

$ exit

6)进入CLI容器并初始化链码,链码ID为mycc,版本号为0,部署的通道名称是myc。

$ docker exec -it cli bash

$ peer chaincode install -p chaincodedev/chaincode/keyValueStore -n mycc -v 0

$ peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc

7)Invoke和Query链码。

$ peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc

$ peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc

$ peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc

正常情况下,两次 query 返回的结果分别为 10 和 20。

开发链码时可以通过上述过程进行测试,但需避免使用相同的链码 ID 以免链码实例化失败。另外,对于链码升级来说,链码的 ID 应该保持不变,同时新链码的版本号需要比先前实例化的版本高,并通过 upgrade 交易来更新链码在通道中的状态。

假设对链码 keyValueStore.go 进行了更改,并把最新的链码保存在$GOPATH/src/keyValueStoreNew 下,则升级链码的操作如下:

1)进入fabric-samples目录并拷贝最新链码到chaincode目录。

$ cd $GOPATH/src/fabric-samples

$ cp -r $GOPATH/src/keyValueStoreNew ./chaincode

2)进入chaincode容器,编译并运行更新后的链码。

$ docker exec -it chaincode bash

$ cd keyValueStoreNew && go build

$ export CORE_PEER_ADDRESS=peer:7051

$ export CORE_CHAINCODE_ID_NAME=mycc:1

$ ./keyValueStoreNew

$ exit

3)进入cli容器并升级链码。

$ docker exec -it cli bash

$ peer chaincode install -p chaincodedev/chaincode/keyValueStoreNew -n mycc -v 1

$ peer chaincode upgrade -n mycc -v 1 -c '{"Args":["a","10"]}' -C myc

到此升级链码完毕,可以对最新的链码mycc进行操作。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 亨利笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 链码的背书策略
  • 2. 链码开发
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档