作者:明神特烦恼
公众号:明神特烦恼
智能合约是区块链与业务关联度最大的部分,合约的友好性、安全性、执行效率也是优秀的智能合约引擎的重要指标,长安链的支持多种语言的智能合约,包括go、rust、solidity、c++等。本节使用go语言开发智能合约,使用tinygo进行合约编译、使用go sdk 安装、调用、查询智能合约。
官方参考文档: https://docs.chainmaker.org.cn/dev/%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6.html
1.开发智能合约
1)合约模板
学习智能合约的开发,通常需要有Demo来学习API的使用方式,项目组织方式。在长安链学习过程中也是如此,所以第一步获取合约模块。
1.1)拉取镜像:docker pull chainmakerofficial/chainmaker-go-contract:1.1.1
长安链为我们提供合约开发的镜像,内部包含合约模板。
1.2)启动并进入容器: docker run -it --name chainmaker-go-contract -v ${PWD}:/home chainmakerofficial/chainmaker-go-contract:1.1.1 bash
启动chainmaker-go-contract
容器并进入,同时将当前目录挂载到容器的 /home
目录下
1.3) 解压并分析合约模板
cp /home
tar xzvf contract_go_template.tar.gz
- 目录结构:
其中业务逻辑在main.go
中,//export
注释的方法被编译器识别,用于外部访问。
- 合约逻辑
save
方法支持传入三个参数file_hash
、file_name
、time
,以file_hash
为索引存入区块链世界状态。
findByFileHash
方法支持传入一个参数file_hash
,以file_hash
为检索条件进行世界状态检索并返回。 - 合约逻辑可任意修改,只要后续调用指定
export
的方法名即可
2)编译合约
按照官方参考文档方式 在chainmaker-go-contract
容器中执行./build.sh
生成main.wasm文件
3)安装、调用、查询合约
- 下载go sdk代码
git clone --recursive https://git.chainmaker.org.cn/chainmaker/chainmaker-sdk-go.git
- 拷贝crypto-config文件
rm -rf chainmaker-sdk-go/testdata/crypto-config
cp -r chainmaker-go/build/crypto-config chainmaker-sdk-go/testdata/
- 编写测试用例
可基于sdk_user_contract_claim_test.go文件进行修改。
1)创建文件hash_test.go
package chainmaker_sdk_goimport ("chainmaker.org/chainmaker-go/common/random/uuid""chainmaker.org/chainmaker-sdk-go/pb/protogo/common""fmt""github.com/stretchr/testify/require""testing""time"
)var (hashContractName = "myhash001"hashVersion = "1.0.0"hashByteCodePath = "./testdata/hash-cc/main.wasm"
)func TestUserContractHash(t *testing.T) {fmt.Println("====================== create client ======================")client, err := createClientWithCertBytes()require.Nil(t, err)fmt.Println("====================== create admin1 ======================")admin1, err := createAdmin(orgId1)require.Nil(t, err)fmt.Println("====================== create admin2 ======================")admin2, err := createAdmin(orgId2)require.Nil(t, err)fmt.Println("====================== create admin3 ======================")admin3, err := createAdmin(orgId3)require.Nil(t, err)fmt.Println("====================== create admin4 ======================")admin4, err := createAdmin(orgId4)require.Nil(t, err)fmt.Println("====================== 创建合约 ======================")testUserContractHashCreate(t, client, admin1, admin2, admin3, admin4, true, true)fmt.Println("====================== 调用合约 ======================")fileHash, err := testUserContractHashInvoke(client, "save", true)require.Nil(t, err)fmt.Println("====================== 执行合约查询接口 ======================")//txId := "1cbdbe6106cc4132b464185ea8275d0a53c0261b7b1a470fb0c3f10bd4a57ba6"//fileHash = txId[len(txId)/2:]params := map[string]string{"file_hash": fileHash,}testUserContractHashQuery(t, client, "find_by_file_hash", params)
}func testUserContractHashCreate(t *testing.T, client *ChainClient,admin1, admin2, admin3, admin4 *ChainClient, withSyncResult bool, isIgnoreSameContract bool) {resp, err := createUserContract(client, admin1, admin2, admin3, admin4,hashContractName, hashVersion, hashByteCodePath, common.RuntimeType_GASM, []*common.KeyValuePair{}, withSyncResult)if !isIgnoreSameContract {require.Nil(t, err)}fmt.Printf("CREATE claim contract resp: %+v\n", resp)
}func testUserContractHashInvoke(client *ChainClient,method string, withSyncResult bool) (string, error) {//curTime := fmt.Sprintf("%d", CurrentTimeMillisSeconds())curTime := time.Now().Format("2006-01-02 15:04:05")fileHash := uuid.GetUUID()params := map[string]string{"time": "123","file_hash": fileHash,"file_name": fmt.Sprintf("file_%s", curTime),}err := invokeUserContract(client, hashContractName, method, "", params, withSyncResult)//err := invokeUserContractStepByStep(client, claimContractName, method, "", params, withSyncResult)if err != nil {return "", err}return fileHash, nil
}func testUserContractHashQuery(t *testing.T, client *ChainClient,method string, params map[string]string) {resp, err := client.QueryContract(hashContractName, method, params, -1)require.Nil(t, err)fmt.Printf("QUERY claim contract resp: %+v\n", resp)
}
2)执行TestUserContractHash Test方法
2.扩展
这里使用的是默认配置文件,如果需要改变端口、连接数、TLS使能等等,需要修改chainmaker-sdk-go/testdata/sdk_config.yml