一、概述
链码的编写需要自定义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)
2 条评论
555
555