作者:明神特烦恼
智能合约是区块链与业务关联度最大的部分,合约的友好性、安全性、执行效率也是优秀的智能合约引擎的重要指标,长安链的支持多种语言的智能合约,包括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_go
import (
"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