一、概述

链码的编写需要自定义st ruct ,实现shim包Chaincode接口的两个方法:

type Chaincode interface {
  Init(stub ChaincodeStubInterface) pb.Response
  Invoke(stub ChaincodeStubInterface) pb.Response
}

链码的生命周期包括链码安装、实例化、升级等。其中链码的实例化和升级都会调用Init () 方法,链码的invoke、query调用方式都只会调用Invoke()方法。
在shim包中,fabric给我们提供了ChaincodeStubInterface接口,在该接口中,我们可以 使用接口中的方法实现具体的链码业务。
特别说明一句,如果链码方法最后返回的pb.Response是shim.Error(),那么所有的操 作,包括数据插入、数据删除、创建复合键、发送事件、调用其他链码等都是无效的,即一次 链码的调用具有事务性,如果返回shim.Success()则所有操作都成功,否则所有操作都失败。

二、接口描述

1. 参数解析

直接获取所有参数

  • 字节数组[]byt e形式
// GetArgs() [][]byte
args := stub.GetArgs()
// args 所有参数的[]byte形式
  • 字符串string形式
// GetStringArgs() []string
args := stub.GetStringArgs()
// args 所有参数的[]string形式 

获取方法名和调用参数(st ring格式)

// GetFunctionAndParameters() (functionName string,args []string)
functionName, args := stub.GetFunctionAndParameters()
// functionName 方法名
// args 方法名后面的所有参数

2. 复合键操作

创建复合键

// CreateCompositeKey(objectType string, attributes []string) (string, error)
compositeKey, err := stub.CreateCompositeKey(objectType, attributes)
// objectType用于分类,放在compositeKey中的最前面,无实际含义
// attributes是一个slice,会拼接成string类型,接在objectType后面

分割复合键

// SplitCompositeKey(compositeKey string) (string, []string, error)
key, compositeKeys, err := stub.SplitCompositeKey(iterator.Next().Key)
// iterator为查询复合键的迭代器,Next()方法返回下一个复合键对象,Key标识该 对象的复合键;
// compositeKeys,[]string类型,得到即为创建复合键时的attributes值

查询复合键

  • 查询所有结果
// GetStateByPartialCompositeKey(objectType string, keys []string)(StateQueryIteratorInterface, error)
iterator, err := stub.GetStateByPartialCompositeKey(objectType, keys)
// keys用于筛选
// 当keys为空时,返回该objectType下的所有复合键;
// 当keys不为空时,需要按照顺序,依次填入字段,即按照最左匹配原则,筛选出符合条件的复合键;
  • 分页展示结果
GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string, pageSize int32, bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error)

3. 账本数据处理

查询数据

  • 查询单个数据
// GetState(key string) ([]byte, error)
data, err := stub.GetState(key)
  • 查询范围数据

    • 查询所有结果
    // GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error)
    iterator, err :=stub.GetStateByRange(startKey, endKey)
    // startKey, endKey为查询的范围边界,注意是左闭右开, [startKey, endKey)
    // 即查询的结果是不包括endKey的值;
    • 分⻚展示结果(仅couchDB支持)
    GetStateByRangeWithPagination(startKey, endKey string, pageSize int32, bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error)
  • 条件查询(仅couchDB支持)

    • 查询所有结果(仅couchDB支持)
    // GetQueryResult(query string) (StateQueryIteratorInterface, error)
    iterator, err := stub.GetQueryResult(query)
    // query为富查询语句
    • 分⻚展示结果(仅couchDB支持)
    // GetQueryResultWithPagination(query string, pageSize int32, bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error)
    iterator, metadata, err := stub.GetQueryResultWithPagination(query, pageSize, bookmark)

插入数据

// PutState(key string, value []byte) error
err := stub.PutState(key, value)

删除数据

// DelState(key string) error
err := stub.DelState(key)

查询数据修改的历史记录

// GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error)
iterator, err := stub.GetHistoryForKey(key)

4. 交易信息

获取交易ID

GetTxID()string

获取ChannelID

GetChannelID()string

获取交易创建的时间戳

GetTxTimestamp()(*timestamp.Timestamp,error)

获取交易提交者的身份信息

GetCreator()([]byte,error)

获取交易的临时信息

GetTransient()(map[string][]byte,error)

5. 发送事件

// SetEvent(name string, payload []byte) error
err := stub.SetEvent(key, value)
// sdk监听链码时,会根据正则表达式对key值进行筛选,value值将会传给链码的监听对象

6. 私有数据处理

7. 调用其他链码

// InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response
resp := stub.InvokeChaincode(chaincodeName, args, channel)

三、其他接口补充

1. 数据状态查询结果迭代器接口(StateQueryIteratorInterf ace)

判断是否还有元素

HasNext()bool

关闭迭代器

Close()error

获取下一个元素

Next()(*queryresult.KV,error)

2. 历史记录查询结果迭代器接口(HistoryQueryIteratorInterf ace)

判断是否还有元素

HasNext()bool

关闭迭代器

Close()error

获取下一个元素

Next()(*queryresult.KeyModification, error)

四、Fabric SDK调用流程

1. 获取Fabric SDK

sdk,err := fabsdk.New(config.FromFile(sdkConfig))

2. 加入Org、User信息

rcp := sdk.Context(fabsdk.WithOrg(orgName), fabsdk.WithUser(userName))

3. 获取指定Channel的Channel Provider

ccp := sdk.ChannelContext(channelID, fabsdk.WithUser(userName))
1) 获取该Channel的Channel Client
cc,err := channel.New(ccp)

封装channel.Request

var req = &channel.Request{ 
          ChaincodeID: chaincodeID, 
          Fcn fcn,
            Args args, 
          TransientMap transientMap,
}

封装channel.Request Opt ion

var opts = []channel.RequestOption
opts = append(opts, channel.WithTargetEndpoints(peer...)
opts = append(opts, channel.WithOrdererEndpoint(orderers...))

调用链码返回结果

// invoke调用
response, err := cc.Execute(*req, opts...)
// query调用
response, err := cc.Query(*req, opts...)
2)获取该Channel的Event Client
ec,err := event.New(ccp,event.WithBlockEvents())

监听区块/链码/交易事件

// 监听区块事件
blockReg, notify, err := ec.RegisterBlockEvent()
// 监听链码事件
chaincodeReg, notify, err := ec.RegisterChaincodeEvent(chaincodeId, ".*") 
// 监听交易事件
txReg, notify, err := ec.RegisterTxStatusEvent(txId)

获取数据

for { 
      select {
      case event := <- notify: 
      // todo
        } 
}

取消监听

// 取消监听区块事件 
ec.Unregister(blockReg)
// 取消监听链码事件 
ec.Unregister(chaincodeReg) 
// 取消监听交易事件 
ec.Unregister(txReg)
最后修改:2020 年 09 月 03 日 11 : 47 AM
如果觉得我的文章对你有用,请随意赞赏