当前位置: 首页 > news >正文

如何编写智能合约——基于长安链的Go语言的合约开发

场景设计:文件存证系统

在数字化时代,文件存证和版本追踪变得越来越重要。设想一个场景:在一个法律事务管理系统中,用户需要提交和管理各种文件的版本记录,以确保每个文件在不同时间点的状态可以被准确追踪。文件可能经历多个版本,例如合同的修订、文件内容的更新等。为了确保文件的合法性和准确性,需要一个系统来记录每次修改,并能够查询和管理这些版本历史。

我们的智能合约将实现一个文件存证系统,该系统不仅允许存储和检索文件信息,还支持版本管理和历史记录查询。用户可以保存文件、查询特定版本的文件,并获取某类型文件的所有历史记录。

本合约场景主要包括以下几个步骤:

1. 文件存证:用户将文件的哈希值、类型、版本、文件名及时间等信息存储在区块链上,确保其合法性和完整性。

2. 文件查询:用户可以通过文件类型和版本号查询存证信息,验证文件是否已经存证。

3. 历史记录查询:用户可以查看某种文件类型下所有历史版本的存证信息。

合约编写过程:

1. 引入必要的包

要撰写智能合约,首先需要引入 Chainmaker 框架的相关依赖包,如 sandbox、sdk 和 protogo,这些包提供了与区块链交互的功能,并用于处理智能合约中的各种操作。

package mainimport ("chainmaker/pb/protogo""chainmaker/sandbox""chainmaker/sdk""encoding/json""fmt""log""strconv"
)

2. 定义智能合约结构体

定义 FactContract 作为合约的核心结构体。我们还定义了 Fact 结构体来存储文件的存证信息,包括证据类型、版本、文件哈希、文件名和时间。

type FactContract struct {
}type Fact struct {EvidenceType stringVersion      stringFileHash     stringFileName     stringTime         int
}// 新建存证对象
func NewFact(evidenceType string, version string, fileHash string, fileName string, time int) *Fact {return &Fact{EvidenceType: evidenceType,Version:      version,FileHash:     fileHash,FileName:     fileName,Time:         time,}
}

3. 实现合约的初始化和升级方法

智能合约必须实现 InitContract() 和 UpgradeContract() 方法。

• InitContract() 用于合约的初始部署,成功后会返回一条确认消息。

• UpgradeContract() 用于合约的升级操作,确保升级后的合约能正确被执行。

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

4. 实现智能合约的调用方法

在 InvokeContract 方法中,根据不同的请求方法调用相应的功能模块。包括保存存证、查询存证、删除存证以及获取历史记录。

func (f *FactContract) InvokeContract(method string) protogo.Response {switch method {case "save":return f.SaveEvidence()case "find":return f.FindEvidence()case "getHistory":return f.GetHistoryByEvidenceType()default:return sdk.Error("invalid method")}
}

5. 文件存证功能

SaveEvidence 方法用于将文件的存证信息保存到区块链上,存储的字段包括文件类型、版本号、哈希值、文件名和时间。

func (f *FactContract) SaveEvidence() protogo.Response {params := sdk.Instance.GetArgs()evidenceType := string(params["evidence_type"])version := string(params["version"])fileHash := string(params["file_hash"])fileName := string(params["file_name"])timeStr := string(params["time"])time, err := strconv.Atoi(timeStr)if err != nil {msg := "time is [" + timeStr + "] not int"sdk.Instance.Errorf(msg)return sdk.Error(msg)}fact := NewFact(evidenceType, version, fileHash, fileName, time)factBytes, err := json.Marshal(fact)if err != nil {return sdk.Error(fmt.Sprintf("传过来的参数序列化失败, err: %s", err))}sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})err = sdk.Instance.PutStateByte(fact.EvidenceType, fact.Version, factBytes)if err != nil {return sdk.Error("fail to save fact bytes")}createUser, _ := sdk.Instance.GetSenderRole()sdk.Instance.Infof("[saveUser] create=" + createUser)return sdk.Success([]byte(fact.FileName + fact.FileHash))
}

6. 文件查询功能

FindEvidence 方法根据文件类型和版本号查询指定文件的存证信息。

func (f *FactContract) FindEvidence() protogo.Response {evidenceType := string(sdk.Instance.GetArgs()["evidence_type"])version := string(sdk.Instance.GetArgs()["version"])result, err := sdk.Instance.GetStateByte(evidenceType, version)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("[find_by_file_hash] fileHash=" + fact.FileHash)sdk.Instance.Infof("[find_by_file_hash] fileName=" + fact.FileName)return sdk.Success(result)
}

7. 文件历史查询功能

GetHistoryByEvidenceType 方法用于查询某个文件类型下的所有历史版本信息。

func (f *FactContract) GetHistoryByEvidenceType() protogo.Response {evidenceType := string(sdk.Instance.GetArgs()["evidence_type"])iter, err := sdk.Instance.NewIteratorPrefixWithKey(evidenceType)if err != nil {return sdk.Error("failed to create iterator")}defer iter.Close()var results []Datafor {key, field, value, err := iter.Next()if err != nil {sdk.Instance.Infof("Error iterating: %v", err)}if key == "" {break}results = append(results, Data{Key:   key,Field: field,Value: string(value),})}jsonBytes, err := json.Marshal(results)if err != nil {return sdk.Error(fmt.Sprintf("Error marshaling results: %v", err))}return sdk.Success(jsonBytes)
}

8. 合约入口

最后,使用 main 方法作为合约的入口,启动合约。

func main() {err := sandbox.Start(new(FactContract))if err != nil {log.Fatal(err)}
}

9.完整代码

/*
Copyright (C) BABEC. All rights reserved.
Copyright (C) THL A29 Limited, a Tencent company. All rights reserved.
SPDX-License-Identifier: Apache-2.0
*/package mainimport ("chainmaker/pb/protogo""chainmaker/sandbox""chainmaker/sdk""encoding/json""fmt""log""strconv"
)type FactContract struct {
}// 存证对象
type Fact struct {EvidenceType stringVersion      stringFileHash     stringFileName     stringTime         int
}// 新建存证对象
func NewFact(evidenceType string, version string, fileHash string, fileName string, time int) *Fact {fact := &Fact{EvidenceType: evidenceType,Version:      version,FileHash:     fileHash,FileName:     fileName,Time:         time,}return fact
}func (f *FactContract) InitContract() protogo.Response {return sdk.Success([]byte("Init contract success"))
}func (f *FactContract) UpgradeContract() protogo.Response {return sdk.Success([]byte("Upgrade contract success"))
}func (f *FactContract) InvokeContract(method string) protogo.Response {switch method {case "save":return f.SaveEvidence()case "find":return f.FindEvidence()case "getHistory":return f.GetHistoryByEvidenceType()default:return sdk.Error("invalid method")}
}func (f *FactContract) SaveEvidence() protogo.Response {params := sdk.Instance.GetArgs()// 获取参数evidenceType := string(params["evidence_type"])version := string(params["version"])fileHash := string(params["file_hash"])fileName := string(params["file_name"])timeStr := string(params["time"])time, err := strconv.Atoi(timeStr)if err != nil {msg := "time is [" + timeStr + "] not int"sdk.Instance.Errorf(msg)return sdk.Error(msg)}// 构建结构体fact := NewFact(evidenceType, version, fileHash, fileName, time)// 序列化factBytes, err := json.Marshal(fact)if err != nil {return sdk.Error(fmt.Sprintf("传过来的参数序列化失败, err: %s", err))}// 发送事件sdk.Instance.EmitEvent("topic_vx", []string{fact.FileHash, fact.FileName})// 存储数据err = sdk.Instance.PutStateByte(fact.EvidenceType, fact.Version, 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)createUser, _ := sdk.Instance.GetSenderRole()sdk.Instance.Infof("[saveUser] create=" + createUser)// 返回结果return sdk.Success([]byte(fact.FileName + fact.FileHash))}func (f *FactContract) FindEvidence() protogo.Response {// 获取参数evidenceType := string(sdk.Instance.GetArgs()["evidence_type"])version := string(sdk.Instance.GetArgs()["version"])// 查询结果result, err := sdk.Instance.GetStateByte(evidenceType, version)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("[find_by_file_hash] fileHash=" + fact.FileHash)sdk.Instance.Infof("[find_by_file_hash] fileName=" + fact.FileName)// 返回结果return sdk.Success(result)
}// 定义数据结构
type Data struct {Key   string `json:"key"`Field string `json:"field"`Value string `json:"value"`
}func (f *FactContract) GetHistoryByEvidenceType() protogo.Response {// 获取参数evidenceType := string(sdk.Instance.GetArgs()["evidence_type"])// 查询结果iter, err := sdk.Instance.NewIteratorPrefixWithKey(evidenceType)if err != nil {return sdk.Error("failed to delere get_state")}defer iter.Close()var results []Data// 遍历结果for {key, field, value, err := iter.Next()if err != nil {sdk.Instance.Infof("Error iterating: %v", err)}if key == "" {break}// 将当前的 key, field, value 保存到结果数组results = append(results, Data{Key:   key,Field: field,Value: string(value), // 将 byte[] 转换为 string})}jsonBytes, err := json.Marshal(results)if err != nil {return sdk.Error(fmt.Sprintf("Error marshaling results: %v", err))}// 返回结果return sdk.Success(jsonBytes)
}func main() {err := sandbox.Start(new(FactContract))if err != nil {log.Fatal(err)}
}

10.部署测试

我们使用长安链的长安链IDE (chainmaker.org.cn)部署测试我们的代码。

我们将创建以下虚拟文件证据数据:

1. 文件1

• 证据类型: “contract”

• 版本: “v1.0”

• 文件哈希: “abc123”

• 文件名: “Contract_A.pdf”

• 时间: 1694668800 (对应的时间为2023-09-13 00:00:00)

2. 文件1的修订版

• 证据类型: “contract”

• 版本: “v1.1”

• 文件哈希: “abc124”

• 文件名: “Contract_A_Revision.pdf”

• 时间: 1695273600 (对应的时间为2023-09-22 00:00:00)

3. 文件2

• 证据类型: “report”

• 版本: “v1.0”

• 文件哈希: “def456”

• 文件名: “Report_B.docx”

• 时间: 1694860800 (对应的时间为2023-09-16 00:00:00)

1、存证文件1

2、存证文件1修订版

3、存证文件2

4、查询文件1

5、查询文件2

6、查询文件1的修订历史记录

结论

通过以上演示,我们展示了如何使用智能合约进行文件存证和版本追踪。通过保存、查询和获取历史记录的方法,用户可以有效地管理文件的各个版本,并确保文件信息的完整性和准确性。这些功能使得文件的存证和版本管理更加高效、透明和可追溯。


http://www.mrgr.cn/news/25919.html

相关文章:

  • sicp每日一题[2.7]
  • 高颜值浅色系可视化界面,用在B端,无出其右。
  • (不用互三)AI绘画工具大比拼:Midjourney VS Stable Diffusion该如何选择?
  • Verilog和Matlab实现RGB888互转YUV444
  • 【ESP-IDF FreeRTOS】事件组
  • 基于Web的门店管理系统的设计与实现---附源码76269
  • Jetpack Compose Side Effects in Details 副作用的详细信息
  • 通过 Sniper Links 提高您的电子邮件确认率
  • 【SSRF漏洞】——http协议常见绕过
  • K1计划100%收购 MariaDB; TDSQL成为腾讯云核心战略产品; Oracle@AWS/Google/Azure发布
  • java 可变参数
  • java.io.IOException: Too many open files 分析与解决
  • [DCVRP] 基于复杂网络的k-opt算法解空间表示(五)
  • ElementPlus自定义更换主题色
  • Excel图片批量插入单元格排版处理插件【图片大师】
  • 出海公司如何快速搭建海外团队指南
  • MongoDB与Pymongo深度实践:从基础概念到无限级评论应用示例
  • 项目实战应用Redis分布式锁
  • 828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署CodeX Docs文档工具
  • python 实现euler modified变形欧拉法算法