首先通过长安链IDE进入网页进行登录
输入账号密码进行登录
然后开始合约编写,合约的功能是保存和查询用户的年龄信息,合约的名字叫 age_contract。合约有两个方法:
第一个方法 saveAge:保存年龄信息。接收两个参数:姓名 name 和年龄 age,把姓名和年龄保存到链上,成功返回成功信息,失败返回错误信息。
第二个方法 getAge:查询年龄信息。接收一个参数 name ,如果链上存在用户的年龄信息则返回,如果不存在则返回-1。
版本右上角应该优先选择v2.3.0+
首先选择创建合约工程,输入合约名字 age_contract,然后点击创建,此时在文件目录会有一个合约工程age_contract。这里会自动生成main.go、go.mod、go.sum文件。
然后开始编写合约
package main
import (
"chainmaker/pb/protogo"
"chainmaker/sdk"
"chainmaker/sandbox"
)
// AgeContract save and get age
type AgeContract struct {
}
// InitContract 合约初始化方法,会在合约部署到链上时执行
func (ac *AgeContract) InitContract() protogo.Response {
return sdk.Success([]byte("Init contract success"))
}
// InvokeContract 调用合约,在链上执行合约时,实际调用的是这个方法,此时调用合约会直接返回错误信息 `no contarct method`
func (ac *AgeContract) InvokeContract(method string) protogo.Response {
return sdk.Error("invalid method")
}
// UpgradeContract 合约升级方法, 会在合约升级时候调用
func (ac *AgeContract) UpgradeContract() protogo.Response {
return sdk.Success([]byte("Upgrade contract success"))
}
//合约入口
func main() {
//运行合约
err := sandbox.Start(new(AgeContract))
if err != nil {
log.Fatal(err)
}
}
这个是合约初始化的基本模版,像以后的都可以按照这个来实现。
然后需要添加saveAge 和 getAge方法:
// saveAge 保存用户年龄信息
func (ac *AgeContract) saveAge() protogo.Response {
//获取所有的合约参数
args := sdk.Instance.GetArgs()
name := string(args["name"])
ageStr := string(args["age"])
if name == "" || ageStr == "" {
message := "name or age is empty"
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error(message)
}
_, err := strconv.Atoi(ageStr)
if err != nil {
message := "convert age to int fail. err: " + err.Error()
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error(message)
}
//保存用户年龄信息到链
err = sdk.Instance.PutStateFromKey(name, ageStr)
if err != nil {
message := "put state from key fail. err: " + err.Error()
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error(message)
}
//返回合约执行成功信息
return sdk.Success([]byte("success"))
}
// getAge 获取用户年龄信息
func (ac *AgeContract) getAge() protogo.Response {
//获取所有的合约参数
args := sdk.Instance.GetArgs()
name := string(args["name"])
if name == "" {
message := "name is empty"
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error("-1")
}
ageStr, err := sdk.Instance.GetStateFromKey(name)
if err != nil {
message := "get state from key fail. err: " + err.Error()
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error("-1")
}
if ageStr == "" {
message := "age not found"
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error("-1")
}
//返回用户年龄
return sdk.Success([]byte(ageStr))
}
然后修改关联合约方法,再此之前合约都是相互独立的存在,我们现在要做的就是将合约联系起来:
// InvokeContract 调用合约,在链上执行合约时,实际调用的是这个方法
func (ac *AgeContract) InvokeContract(method string) protogo.Response {
switch method {
case "saveAge":
return ac.saveAge()
case "getAge":
return ac.getAge()
default:
return sdk.Error("no contract method")
}
}
整体代码如下:
package main
import (
"chainmaker/pb/protogo"
"chainmaker/sandbox"
"chainmaker/sdk"
"log"
"strconv"
"fmt"
)
// AgeContract save and get age
type AgeContract struct {
}
// InitContract 合约初始化方法,会在合约部署到链上时执行
func (ac *AgeContract) InitContract() protogo.Response {
return sdk.Success([]byte("Init contract success"))
}
// InvokeContract 调用合约,在链上执行合约时,实际调用的是这个方法
func (ac *AgeContract) InvokeContract(method string) protogo.Response {
switch method {
case "saveAge":
return ac.saveAge()
case "getAge":
return ac.getAge()
case "crossCall":
return ac.crossCall()
case "historyKvIterator":
return ac.historyKvIterator()
default:
return sdk.Error("no contract method")
}
}
// UpgradeContract 合约升级方法, 会在合约升级时候调用
func (ac *AgeContract) UpgradeContract() protogo.Response {
return sdk.Success([]byte("Upgrade contract success"))
}
// saveAge 保存用户年龄信息
func (ac *AgeContract) saveAge() protogo.Response {
//获取所有的合约参数
args := sdk.Instance.GetArgs()
name := string(args["name"])
ageStr := string(args["age"])
if name == "" || ageStr == "" {
message := "name or age is empty"
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error(message)
}
_, err := strconv.Atoi(ageStr)
if err != nil {
message := "convert age to int fail. err: " + err.Error()
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error(message)
}
//保存用户年龄信息到链
err = sdk.Instance.PutStateFromKey(name, ageStr)
if err != nil {
message := "put state from key fail. err: " + err.Error()
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error(message)
}
//返回合约执行成功信息
return sdk.Success([]byte("success"))
}
// getAge 获取用户年龄信息
func (ac *AgeContract) getAge() protogo.Response {
//获取所有的合约参数
args := sdk.Instance.GetArgs()
name := string(args["name"])
if name == "" {
message := "name is empty"
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error("-1")
}
ageStr, err := sdk.Instance.GetStateFromKey(name)
if err != nil {
message := "get state from key fail. err: " + err.Error()
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error("-1")
}
if ageStr == "" {
message := "age not found"
//打印日志,调试合约时,日志会在控制台输出中展示
sdk.Instance.Infof(message)
//返回合约执行错误信息
return sdk.Error("-1")
}
//返回用户年龄
return sdk.Success([]byte(ageStr))
}
//crossCall 跨合约调用
func (ac *AgeContract) crossCall() protogo.Response {
//要调用的合约的基本信息
contractName := "age_contract"
calledMethod := "getAge"
//跨合约调用的参数
crossContractArgs := make(map[string][]byte)
crossContractArgs["method"] = []byte(calledMethod)
crossContractArgs["name"] = []byte("Bob")
//开合约调用响应信息
response := sdk.Instance.CallContract(contractName, calledMethod, crossContractArgs)
sdk.Instance.EmitEvent("cross contract", []string{"success"})
return response
}
//historyKvIterator 历史迭代器
func (ac *AgeContract) historyKvIterator() protogo.Response {
key := "Bob"
//创建历史迭代器
result, err := sdk.Instance.NewHistoryKvIterForKey(key, "")
if err != nil {
msg := "failed to call get_state"
sdk.Instance.Log(msg + " " + err.Error())
return sdk.Error(msg)
}
//判断迭代器是否有数据,如果有迭代输出数据
for result.HasNext() {
v, err := result.Next()
if err != nil {
msg := "failed to call iterator"
sdk.Instance.Log(msg + " " + err.Error())
return sdk.Error(msg)
}
sdk.Instance.Log(fmt.Sprintf("NewHistoryKvIterForKey %v\n", v))
}
return sdk.Success([]byte("success"))
}
func main() {
//运行合约
err := sandbox.Start(new(AgeContract))
if err != nil {
log.Fatal(err)
}
}
保存之后找到左边的第二个按钮调试:
在这里选择我们的age_contract合约,然后合约方法为saveAge,key和value自己输入,然后构建合约再执行合约
执行完合约会显示这些成功界面
调用getAge则会返回刚刚的age信息