shim.CMStubInterface
接口shim.CMStubInterface
定义了和链交互的方法,我们通过在合约中调用该接口的方法和链交互。方法列表如下:
# 获取合约执行的参数
GetArgs() map[string][]byte
# 从链上查询信息
# 查询信息 指定 key field
GetState(key, field string) (string, error)
GetStateByte(key, field string) ([]byte, error)
# 查询信息 指定 key
GetStateFromKey(key string) (string, error)
GetStateFromKeyByte(key string) ([]byte, error)
# 存储数据到链上
# 存储信息 指定key field
PutState(key, field string, value string) error
PutStateByte(key, field string, value []byte) error
# 存储信息 指定 key
PutStateFromKey(key string, value string) error
PutStateFromKeyByte(key string, value []byte) error
# 从链上删除数据
# 删除信息 指定key field
DelState(key, field string) error
# 删除信息 指定key
DelStateFromKey(key string) error
# 获取合约创建者的组织ID
GetCreatorOrgId() (string, error)
# 获取合约创建者的角色
GetCreatorRole() (string, error)
# 获取合约创建者的公钥
GetCreatorPk() (string, error)
# 获取交易发起者的组织ID
GetSenderOrgId() (string, error)
# 获取交易交易发起者的角色
GetSenderRole() (string, error)
# 获取交易发起者的公钥
GetSenderPk() (string, error)
# 获取当前块高度
GetBlockHeight() (int, error)
# 获取交易ID
GetTxId() (string, error)
# 获取交易时间戳
GetTxTimeStamp() (string, error)
# 发布合约事件
EmitEvent(topic string, data []string)
# 记录合约日志
Log(message string)
# 跨合约调用
CallContract(contractName, contractVersion string, args map[string][]byte) protogo.Response
# 范围查询迭代器 [startKey, limitKey)
NewIterator(startKey string, limitKey string) (ResultSetKV, error)
# 范围查询迭代器 [key+startField, key+limitField)
NewIteratorWithField(key string, startField string, limitField string) (ResultSetKV, error)
# 前缀范围查询迭代器 有 `key+field` 前缀的数据
NewIteratorPrefixWithKeyField(key string, field string) (ResultSetKV, error)
# 前缀范围查询迭代器 有 `key` 前缀的数据
NewIteratorPrefixWithKey(key string) (ResultSetKV, error)
# 历史数据查询迭代器 key+field 数据的历史版本
NewHistoryKvIterForKey(key, field string) (KeyHistoryKvIter, error)
# 查询交易发起方地址
GetSenderAddr() (string, error)
// 结果集
type ResultSet interface {
// NextRow get next row,
// sql: column name is EasyCodec key, value is EasyCodec string val. as: val := ec.getString("columnName")
// kv iterator: key/value is EasyCodec key for "key"/"value", value type is []byte. as: k, _ := ec.GetString("key") v, _ := ec.GetBytes("value")
NextRow() (*serialize.EasyCodec, error)
// HasNext return does the next line exist
HasNext() bool
// Close .
Close() (bool, error)
}
type ResultSetKV interface {
ResultSet
// Next return key,field,value,code
Next() (string, string, []byte, error)
}
type KeyHistoryKvIter interface {
ResultSet
// Next return txId, blockHeight, timestamp, value, isDelete, error
Next() (*KeyModification, error)
}
写合约的时候我们通过编写代码完成合约逻辑,SDK提供了合约和链交互的方法,帮助我们完成和链交互。
shim.CMStubInterface
详细介绍CallContract
方法。代码如下:
//crossCall 跨合约调用 func (ac *AgeContract) crossCall(stub shim.CMStubInterface) protogo.Response { //要调用的合约的基本信息 contractName := "age_contract" contractVersion := "1.0.0" calledMethod := "getAge" //跨合约调用的参数 crossContractArgs := make(map[string][]byte) crossContractArgs["method"] = []byte(calledMethod) crossContractArgs["name"] = []byte("Bob") //开合约调用响应信息 response := stub.CallContract(contractName, contractVersion, crossContractArgs) stub.EmitEvent("cross contract", []string{"success"}) return response }saveAge
、 查询用户年龄信息getAge
开合约调用crossCall
和 历史迭代器historyKvIterator
。
package main import ( "fmt" "strconv" "chainmaker/pb/protogo" "chainmaker/shim" ) // AgeContract save and get age type AgeContract struct { } // InitContract 合约初始化方法,会在合约部署到链上时执行 func (ac *AgeContract) InitContract(stub shim.CMStubInterface) protogo.Response { return shim.Success([]byte("Init Success")) } // InvokeContract 调用合约,在链上执行合约时,实际调用的是这个方法 func (ac *AgeContract) InvokeContract(stub shim.CMStubInterface) protogo.Response { //获取要调用的合约方法 method := string(stub.GetArgs()["method"]) //case "***" 关联方法名 "***" 到 ac.*** switch method { case "saveAge": return ac.saveAge(stub) case "getAge": return ac.getAge(stub) case "crossCall": return ac.crossCall(stub) case "historyKvIterator": return ac.historyKvIterator(stub) default: return shim.Error("no contarct method") } } // saveAge 保存用户年龄信息 func (ac *AgeContract) saveAge(stub shim.CMStubInterface) protogo.Response { //获取所有的合约参数 args := stub.GetArgs() name := string(args["name"]) ageStr := string(args["age"]) if name == "" || ageStr == "" { message := "name or age is empty" //打印日志,调试合约时,日志会在控制台输出中展示 stub.Log(message) //返回合约执行错误信息 return shim.Error(message) } _, err := strconv.Atoi(ageStr) if err != nil { message := "convert age to int fail. err: " + err.Error() //打印日志,调试合约时,日志会在控制台输出中展示 stub.Log(message) //返回合约执行错误信息 return shim.Error(message) } //保存用户年龄信息到链上 err = stub.PutStateFromKey(name, ageStr) if err != nil { message := "put state from key fail. err: " + err.Error() //打印日志,调试合约时,日志会在控制台输出中展示 stub.Log(message) //返回合约执行错误信息 return shim.Error(message) } //返回合约执行成功信息 return shim.Success([]byte("success")) } // getAge 获取用户年龄信息 func (ac *AgeContract) getAge(stub shim.CMStubInterface) protogo.Response { //获取所有的合约参数 args := stub.GetArgs() name := string(args["name"]) if name == "" { message := "name is empty" //打印日志,调试合约时,日志会在控制台输出中展示 stub.Log(message) //返回合约执行错误信息 return shim.Error("-1") } ageStr, err := stub.GetStateFromKey(name) if err != nil { message := "get state from key fail. err: " + err.Error() //打印日志,调试合约时,日志会在控制台输出中展示 stub.Log(message) //返回合约执行错误信息 return shim.Error("-1") } if ageStr == "" { message := "age not found" //打印日志,调试合约时,日志会在控制台输出中展示 stub.Log(message) //返回合约执行错误信息 return shim.Error("-1") } //返回用户年龄 return shim.Success([]byte(ageStr)) } //crossCall 跨合约调用 func (ac *AgeContract) crossCall(stub shim.CMStubInterface) protogo.Response { //要调用的合约的基本信息 contractName := "age_contract" contractVersion := "1.0.0" calledMethod := "getAge" //跨合约调用的参数 crossContractArgs := make(map[string][]byte) crossContractArgs["method"] = []byte(calledMethod) crossContractArgs["name"] = []byte("Bob") //开合约调用响应信息 response := stub.CallContract(contractName, contractVersion, crossContractArgs) stub.EmitEvent("cross contract", []string{"success"}) return response } //historyKvIterator 历史迭代器 func (ac *AgeContract) historyKvIterator(stub shim.CMStubInterface) protogo.Response { key := "Bob" //创建历史迭代器 result, err := stub.NewHistoryKvIterForKey(key, "") if err != nil { msg := "failed to call get_state" stub.Log(msg + " " + err.Error()) return shim.Error(msg) } //判断迭代器是否有数据,如果有迭代输出数据 for result.HasNext() { v, err := result.Next() if err != nil { msg := "failed to call iterator" stub.Log(msg + " " + err.Error()) return shim.Error(msg) } stub.Log(fmt.Sprintf("NewHistoryKvIterForKey %v\n", v)) } return shim.Success([]byte("success")) } func main() { //运行合约 err := shim.Start(new(AgeContract)) if err != nil { panic(err) } }main.go
,且 main.go
的包名需要是 main
,合约的入口需要在 main.go
中,且 InvokeContract()
方法代码需要在 main.go
中。即 :
package main import ( "chainmaker/pb/protogo" "chainmaker/shim" ) ... ... ... ... // 调用合约方法 func (f *AgeContract) InvokeContract(stub shim.CMStubInterface) protogo.Response { //获取要调用的合约方法 method := string(stub.GetArgs()["method"]) //case "saveAge" 关联方法名 "saveAge" 到 f.saveAge() //case "getAge" 关联方法名 "getAge" 到 f.getAge() switch method { case "saveAge": return f.saveAge(stub) case "getAge": return f.getAge(stub) default: return shim.Error("no contarct method") } } // 合约入口 func main() { //运行合约 err := shim.Start(new(AgeContract)) if err != nil { panic(err) } }InvokeContract
里面关联方法逻辑需要是如下模式(合约调试会根据下面的模式解析方法名)。注意不要在 case 后面使用定义的常量。
switch method { case "字符串": ... ... case "字符串": ... ... default: return shim.Error("no contarct method") }getState
和 putState
等方法存储。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。