作者:明神特烦恼
公众号:明神特烦恼

智能合约是区块链与业务关联度最大的部分,合约的友好性、安全性、执行效率也是优秀的智能合约引擎的重要指标,长安链的支持多种语言的智能合约,包括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注释的方法被编译器识别,用于外部访问。
    image.png
  • 合约逻辑
    save方法支持传入三个参数file_hashfile_nametime,以file_hash为索引存入区块链世界状态。
    findByFileHash方法支持传入一个参数file_hash,以file_hash为检索条件进行世界状态检索并返回。
  • 合约逻辑可任意修改,只要后续调用指定export的方法名即可
2)编译合约

按照官方参考文档方式 在chainmaker-go-contract容器中执行./build.sh
生成main.wasm文件

3)安装、调用、查询合约
  1. 下载go sdk代码

git clone --recursive https://git.chainmaker.org.cn/chainmaker/chainmaker-sdk-go.git

  1. 拷贝crypto-config文件

rm -rf chainmaker-sdk-go/testdata/crypto-config
cp -r chainmaker-go/build/crypto-config chainmaker-sdk-go/testdata/

  1. 编写测试用例
    可基于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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐