合约示例

最近更新时间:2026-04-21 14:22:49

我的收藏

代码入口

代码入口包名必须为 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和Error
func (f *FactContract) InitContract() protogo.Response {
return sdk.Success([]byte("Init contract success"))
}

// 用于合约的升级
// @return: 合约返回结果,包括Success和Error
func (f *FactContract) UpgradeContract() protogo.Response {
return sdk.Success([]byte("Upgrade contract success"))
}

// 用于合约的调用
// @param method: 交易请求调用的方法
// @return: 合约返回结果,包括Success和Error
func (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 main

import (
"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 fact
type FactContract struct {
}

// Fact object to store
type Fact struct {
FileHash string `json:"fileHash"`
FileName string `json:"fileName"`
Time int32 `json:"time"`
}

// NewFact create a fact object
func NewFact(fileHash string, fileName string, time int32) *Fact {
fact := &Fact{
FileHash: fileHash,
FileName: fileName,
Time: time,
}
return fact
}

// InitContract install contract func
func (f *FactContract) InitContract() protogo.Response {
return sdk.Success([]byte("Init contract success"))
}

// UpgradeContract upgrade contract func
func (f *FactContract) UpgradeContract() protogo.Response {
return sdk.Success([]byte("Upgrade contract success"))
}

// InvokeContract the entry func of invoke contract func
func (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 Fact
if 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 函数拿到要查找的文件的文件哈希。
2. 通过账本交互 API 接口 GetStateByte 函数获取文件的信息;若失败则通过其他辅助类 API 接口 Error 函数将操作结果记录到链上,否则,通过其他辅助类 API 接口 Log 函数和 Success 函数记录相关日志和返回结果。