代码入口
代码入口包名必须为 main。
package main// sdk代码中,有且仅有一个main()方法func main() {// main()方法中,下面的代码为必须代码,不建议修改main()方法当中的代码// 其中,TestContract为用户实现合约的具体名称err := sandbox.Start(new(FactContract))if err != nil {log.Fatal(err)}}
合约必要代码
package main// 合约结构体,合约名称需要写入main()方法当中type FactContract struct {}// 合约必须实现下面三个方法:// InitContract() protogo.Response// UpgradeContract() protogo.Response// InvokeContract(method string) protogo.Response// 用于合约的部署// @return: 合约返回结果,包括Success和Errorfunc (f *FactContract) InitContract() protogo.Response {return sdk.Success([]byte("Init contract success"))}// 用于合约的升级// @return: 合约返回结果,包括Success和Errorfunc (f *FactContract) UpgradeContract() protogo.Response {return sdk.Success([]byte("Upgrade contract success"))}// 用于合约的调用// @param method: 交易请求调用的方法// @return: 合约返回结果,包括Success和Errorfunc (f *FactContract) InvokeContract(method string) protogo.Response {switch method {case "save":return f.save()case "findByFileHash":return f.findByFileHash()default:return sdk.Error("invalid method")}}
智能合约示例
存证合约示例
1. 存储文件哈希、文件名称、时间。
2. 通过文件哈希查询该条记录。
注意
实现合约接口的结构体不能包含任何字段,例如存证合约中的 FactContract;
合约存储的 key 或 field 规则为:^[a-zA-Z0-9._-]+$;
合约中不能定义全局变量;因为在一个 Go 合约实例的生命周期中,可能会处理多笔交易,这些交易共用全局变量和一个 Contract 接口实例(该示例中的 FactContract),不同交易之间会相互产生不可控的影响,导致合约业务不能正常运行。
package mainimport ("encoding/json""fmt""strconv""chainmaker.org/chainmaker/contract-sdk-go/v2/pb/protogo""chainmaker.org/chainmaker/contract-sdk-go/v2/sandbox""chainmaker.org/chainmaker/contract-sdk-go/v2/sdk")// FactContract contract for facttype FactContract struct {}// Fact object to storetype Fact struct {FileHash string `json:"fileHash"`FileName string `json:"fileName"`Time int32 `json:"time"`}// NewFact create a fact objectfunc NewFact(fileHash string, fileName string, time int32) *Fact {fact := &Fact{FileHash: fileHash,FileName: fileName,Time: time,}return fact}// InitContract install contract funcfunc (f *FactContract) InitContract() protogo.Response {return sdk.Success([]byte("Init contract success"))}// UpgradeContract upgrade contract funcfunc (f *FactContract) UpgradeContract() protogo.Response {return sdk.Success([]byte("Upgrade contract success"))}// InvokeContract the entry func of invoke contract funcfunc (f *FactContract) InvokeContract(method string) protogo.Response {switch method {case "save":return f.save()case "findByFileHash":return f.findByFileHash()default:return sdk.Error("invalid method")}}func (f *FactContract) save() protogo.Response {params := sdk.Instance.GetArgs()// 获取参数fileHash := string(params["file_hash"])fileName := string(params["file_name"])timeStr := string(params["time"])time, err := strconv.ParseInt(timeStr, 10, 0)if err != nil {msg := "time is [" + timeStr + "] not int"sdk.Instance.Errorf(msg)return sdk.Error(msg)}// 构建结构体fact := NewFact(fileHash, fileName, int32(time))// 序列化factBytes, err := json.Marshal(fact)if err != nil {return sdk.Error(fmt.Sprintf("marshal fact failed, err: %s", err))}// 发送事件sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})// 存储数据err = sdk.Instance.PutStateByte("fact_bytes", fact.FileHash, factBytes)if err != nil {return sdk.Error("fail to save fact bytes")}// 记录日志sdk.Instance.Infof("[save] fileHash=" + fact.FileHash)sdk.Instance.Infof("[save] fileName=" + fact.FileName)// 返回结果return sdk.Success([]byte(fact.FileName + fact.FileHash))}func (f *FactContract) findByFileHash() protogo.Response {// 获取参数fileHash := string(sdk.Instance.GetArgs()["file_hash"])// 查询结果result, err := sdk.Instance.GetStateByte("fact_bytes", fileHash)if err != nil {return sdk.Error("failed to call get_state")}// 反序列化var fact Factif err = json.Unmarshal(result, &fact); err != nil {return sdk.Error(fmt.Sprintf("unmarshal fact failed, err: %s", err))}// 记录日志sdk.Instance.Infof("[findByFileHash] fileHash=" + fact.FileHash)sdk.Instance.Infof("[findByFileHash] fileName=" + fact.FileName)// 返回结果return sdk.Success(result)}func main() {err := sandbox.Start(new(FactContract))if err != nil {sdk.Instance.Errorf(err.Error())}}
存证合约代码说明
参数名称 | 描述 |
InitContract | 合约的初始化函数,在合约部署时被调用,在本合约中为空。 |
UpgradeContract | 合约的升级函数,在合约升级时被调用,在本合约中为空。 |
InvokeContract | 合约调用的函数。 |
save | save 函数实现将文件相关信息记录到链上的功能。步骤如下: 1. save 函数先通过参数处理 API 接口 GetArgs 函数拿到时间,文件哈希和文件名字等信息。 2. 构造 Fact 结构,序列化为 byte 数据;且当序列化错误时调用其他辅助类 API 接口 Log 函数记录相应日志。 3. 通过调用账本交互 API 接口 PutStateByte 函数将数据记录到链上。 4. 最后通过调用其他辅助类 API 接口 Success 函数将操作结果记录到链上。 |
findByFileHash | 通过文件哈希查询该条记录。步骤如下: 1. findByFileHash 通过参数处理 API 接口 GetArgs 函数拿到要查找的文件的文件哈希。 |