如何编写智能合约——基于长安链的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、调用Save方法对文件1进行存证

2、调用Save方法对文件1的修订版进行存证

3、调用Save方法对文件2进行存证

4、调用find方法查询文件1的存证信息

5、调用find方法查询文件2的存证信息

6、调用getHistory方法查询文件1的全流程历史的存证信息

结论

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/54272.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mac新手入门(快捷键)

系统常用快捷键 基本操作 Command-Z 撤销Command-X 剪切  Command-C 拷贝(Copy) Option Shift Command V 纯文本拷贝 Command-V 粘贴  Command-A 全选(All)Command-S 保存(Save) Command-F 查找&#xff0…

实现实时Web应用,使用AJAX轮询、WebSocket、还是SSE呢??

文章目录 短轮询(Short Polling)长轮询(Long Polling)Comet “服务器推” (这玩意现在用的很少了,了解一下即可)WebSocket原理:方法:事件: SSE原理事件 总结 …

MyBatisPlus 使用 IPage 分页

3.4.0之前版本 Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor new PaginationInterceptor();paginationInterceptor.setLimit(-1);paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));r…

安全建设当中的冷门知识

今天说点有趣的话题,也是因为在安全建设过程中,安全员也不太可能都按照最理想的状态去工作,有资源的问题,有管理惰性问题,当然也有管理者本身决策的问题。 安全行业起步较晚,16年才施行网络安全法&#xff…

【JAVA】数据脱敏技术(对称加密算法、非对称加密算法、哈希算法、消息认证码(MAC)算法、密钥交换算法)使用方法

文章目录 数据脱敏的定义和目的数据脱敏的技术分类对称加密算法非对称加密算法哈希算法消息认证码(MAC)算法密钥交换算法 数据脱敏的技术方案实现字符替换哈希算法(例如:SHA-3 算法)消息认证码(MAC)算法(CM…

代理导致的git错误

问题: 今天在clone时出现如下错误: fatal: unable to access https://github.com/NirDiamant/RAG_Techniques.git/: Failed to connect to 127.0.0.1 port 10089 after 2065 ms: Couldnt connect to server真是让人感到奇怪!就在前天&#…

影刀RPE学习——自动化

下载网址:影刀RPA - 影刀官网 (yingdao.com) 傻瓜式安装进入界面: 官方教程:影刀RPA零基础入门教程(2024最新版):01 引入课-影刀初相识_哔哩哔哩_bilibili

Unity Hub自动安装指定版本Unity的Android开发环境

Unity开发Android环境要求SDK、DNK、JDK、Gradle版本都要对才能发布APK,自己去配置很容易出错。Unity Hub可以自动安装指定版本Unity的Android开发环境。 1.安装国内用的UnityHub(我这里用的3.3.2-c6) 2.找到对应的Unity版本 3.点击【从Unit…

职场 Death Note

场景一 测试:哎,怎么会这样呢?时间没到,他怎么就变成这个样子了呢?一副大惊小怪,整个办公室都是他的声音 开发:对对对,我代码问题,别BB了。 你直接说这个地方不对&#…

【Kubernetes】常见面试题汇总(十九)

目录 59.简述 Kubernetes 所支持的存储供应模式? 60.简述 Kubernetes CSl 模型? 61.简述 Kubernetes Worker节点加入集群的过程? 59.简述 Kubernetes 所支持的存储供应模式? Kubernetes 支持两种资源的存储供应模式&#xff1a…

sqlgun靶场训练

1.看到php?id ,然后刚好有个框,直接测试sql注入 2.发现输入1 union select 1,2,3#的时候在2处有回显 3.查看表名 -1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schemadatabase()# 4.查看列名…

【自动驾驶】决策规划算法 | 数学基础(三)直角坐标与自然坐标转换Ⅱ

写在前面: 🌟 欢迎光临 清流君 的博客小天地,这里是我分享技术与心得的温馨角落。📝 个人主页:清流君_CSDN博客,期待与您一同探索 移动机器人 领域的无限可能。 🔍 本文系 清流君 原创之作&…

Karpathy认为“LLM”这个名字不准确 马斯克非常赞同

LLM 应该改名吗?你怎么看。在 AI 领域,几乎每个人都在谈论大型语言模型,其英文全称为 Large Language Models,简写为 LLM。因为 LLM 中有“Language”一词,因此,大家默认这种技术和语言密切相关。然而&…

Rust:深入浅出说一说 Error 类型

1. Rust 的错误返回机制 Rust 函数计算过程如果发生错误怎么办?Rust没有采取 C 的异常机制,而是允许直接返回错误信息。 这意味着,Rust 提供了错误返回机制,允许函数正常结束时返回计算结果,同时,如果计算…

纯小白安装pytorch(快速上手)

1.首先进入你的虚拟环境(不进入也没关系) 在anaconda prompt中打开,输入activate 虚拟环境2.查看自己的conda源 conda config --show channels3.清空自己的conda源 conda config --remove-key channels4.添加源 conda config --add channels https://mirrors.tu…

蓝桥杯-STM32G431RBT6(解决LCD与LED引脚冲突的问题)

一、LCD与LED为什么会引脚冲突 LCD与LED引脚共用。 网上文章是在LCD_WriteRAM、LCD_WriteRAM_Prepare、LCD_WriteReg中添加,但问题并没有解决。 二、使用步骤 在如下函数中加入uint16_t tempGPIOC->ODR; GPIOC->ODRtemp; LCD_Init(); void LCD_C…

动态规划:07.路径问题_珠宝的最大价值_C++

题目链接:LCR 166. 珠宝的最高价值 - 力扣(LeetCode)https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof/description/ 一、题目解析 题目: 解析: 有过做前几道题的经验,我们会发现这道题其实就…

IDEA甚至前进后退跳转键

1:快捷键设置 idea设置前进、后退快捷键_idea后退前进快捷键-CSDN博客 2:界面设置 IDEA添加“前进 后退 ”添加到工具栏_idea 最新社区版 左右箭头怎么设置-CSDN博客

安卓显示驱动

安卓显示驱动是用于在Android设备上提供图形和视频显示的底层软件组件。 显示驱动在Android系统中扮演着至关重要的角色,它们负责将图形和视频内容从系统内存传输到显示屏上。这些驱动程序确保了用户界面、图像、视频和游戏等视觉元素的正常显示。以下是关于安卓显…

SpringBoot2:web开发常用功能实现及原理解析-上传与下载

文章目录 一、上传文件1、前端上传文件给Java接口2、Java接口上传文件给Java接口 二、下载文件1、前端调用Java接口下载文件2、Java接口下载网络文件到本地3、前端调用Java接口下载网络文件 一、上传文件 1、前端上传文件给Java接口 Controller接口 此接口支持上传单个文件和…