合约开发样例

非国密样例

本开发样例使用标准单群组四节点区块链网络结构,搭建请参考:安装

在利用SDK进行项目开发时,对智能合约进行操作需要利用go-sdk的abigen工具将Solidity智能合约转换为Go文件代码。整体上主要包含六个流程:

  • 准备需要编译的智能合约
  • 配置好相应版本的solc编译器
  • 构建go-sdk的合约编译工具abigen
  • 编译生成go文件
  • 准备建立ssl连接需要的证书
  • 使用生成的go文件进行合约部署、调用

HelloWorld样例

准备HelloWorld.sol合约文件

在 go-sdk 主目录中新建 helloworld 文件夹,在该文件夹中创建 HelloWorld.sol 合约。该合约提供两个接口,分别是get()和set(),用于获取/设置合约变量name。合约内容如下

pragma solidity>=0.4.24 <0.6.11;

contract HelloWorld {
    string value;
    event setValue(string);
    string public version = "1";

    constructor() public {
        value = "Hello, World!";
    }

    function get() public view returns (string memory) {
        return value;
    }

    function set(string v) public {
        value = v;
        emit setValue(v);
    }
}

安装solc编译器

该编译器用于将 sol 合约文件编译成 abi 和 bin 文件,目前FISCO BCOS支持的solc编译器版本0.4.25/0.5.2,即将支持0.6.10

# 该指令在helloworld文件夹中执行
bash ../tools/download_solc.sh -v 0.4.25

构建go-sdk的代码生成工具abigen

该工具用于将 abi 和 bin 文件转换为 go 文件

# 该指令在helloworld文件夹中执行,编译生成abigen工具
go build ../cmd/abigen

编译生成go文件

先利用solc编译合约文件HelloWorld.sol,生成abi和bin文件

# 该指令在helloworld文件夹中执行
./solc-0.4.25 --bin --abi -o ./ ./HelloWorld.sol

helloworld目录下会生成HelloWorld.bin和HelloWorld.abi。此时利用abigen工具将HelloWorld.bin和HelloWorld.abi转换成HelloWorld.go:

# 该指令在helloworld文件夹中执行
./abigen --bin ./HelloWorld.bin --abi ./HelloWorld.abi --pkg helloworld --type HelloWorld --out ./HelloWorld.go

最后helloworld文件夹下面存在以下6个文件:

HelloWorld.abi 、HelloWorld.bin、HelloWorld.go、HelloWorld.sol、solc-0.4.25、abigen

准备建立ssl连接需要的证书

使用build_chain.sh脚本搭建区块链时会在./nodes/127.0.0.1/sdk文件夹中生成sdk证书、私钥以及ca证书,需要将这三个文件拷贝至go-sdk主目录

部署合约

在helloworld文件夹中创建contract文件夹,在contract文件夹中创建helloworld_main.go文件,在该文件中调用HelloWorld.go部署智能合约

package main

import (
	"fmt"
	"log"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	"github.com/FISCO-BCOS/go-sdk/helloworld" // import helloworld
)

func main(){
	config := &conf.ParseConfig("config.toml")[0]

	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}
	address, tx, instance, err := helloworld.DeployHelloWorld(client.GetTransactOpts(), client) // deploy contract
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("contract address: ", address.Hex())  // the address should be saved
	fmt.Println("transaction hash: ", tx.Hash().Hex())
	_ = instance
}

注解

  • 合约地址需要手动保存,调用合约接口时使用

调用合约get接口

在contract文件夹中创建helloworld_get.go文件,调用合约get接口,获取智能合约中name变量存储的值

package main

import (
	"fmt"
	"log"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	"github.com/FISCO-BCOS/go-sdk/helloworld"
	"github.com/ethereum/go-ethereum/common"
)

func main() {
	config := &conf.ParseConfig("config.toml")[0]
	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}

	// load the contract
	contractAddress := common.HexToAddress("contract addree in hex") // 0x481D3A1dcD72cD618Ea768b3FbF69D78B46995b0
	instance, err := helloworld.NewHelloWorld(contractAddress, client)
	if err != nil {
		log.Fatal(err)
	}

	helloworldSession := &helloworld.HelloWorldSession{Contract: instance, CallOpts: *client.GetCallOpts(), TransactOpts: *client.GetTransactOpts()}

	value, err := helloworldSession.Get()    // call Get API
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("value :", value)
}

调用合约set接口

在contract文件夹中创建helloworld_set.go文件,调用合约set接口,设置智能合约中name变量的值

package main

import (
	"fmt"
	"log"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	"github.com/FISCO-BCOS/go-sdk/helloworld"
	"github.com/ethereum/go-ethereum/common"
)

func main() {
	config := &conf.ParseConfig("config.toml")[0]
	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}

	// load the contract
	contractAddress := common.HexToAddress("contract addree in hex") // 0x481D3A1dcD72cD618Ea768b3FbF69D78B46995b0
	instance, err := helloworld.NewHelloWorld(contractAddress, client)
	if err != nil {
		log.Fatal(err)
	}

	helloworldSession := &helloworld.HelloWorldSession{Contract: instance, CallOpts: *client.GetCallOpts(), TransactOpts: *client.GetTransactOpts()}

	var value = "Hello, FISCO BCOS"

	tx, err := helloworldSession.Set(value)  // call set API
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("tx sent: %s\n", tx.Hash().Hex())

	// wait for the mining
	receipt, err := client.WaitMined(tx)
	if err != nil {
		log.Fatalf("tx mining error:%v\n", err)
	}
	fmt.Printf("transaction hash of receipt: %s\n", receipt.GetTransactionHash())
}

KVTableTest样例

准备Table.sol合约文件

在 go-sdk 主目录中新建 kvtabletest 文件夹,拷贝 Table.sol 合约。

# 创建 kvtabletest 文件夹
mkdir kvtabletest && cd kvtabletest

# 拷贝 Table.sol 合约
cp ../.ci/Table/Table.sol ./

准备KVTableTest.sol合约文件

该合约调用 Table 合约,实现创建用户表 t_kvtest,并对 t_kvtest 表进行读写。

pragma solidity>=0.4.24 <0.6.11;

import "./Table.sol";

contract KVTableTest {
    event SetResult(int256 count);

    KVTableFactory tableFactory;
    string constant TABLE_NAME = "t_kvtest";

    constructor() public {
        //The fixed address is 0x1010 for KVTableFactory
        tableFactory = KVTableFactory(0x1010);
        // the parameters of createTable are tableName,keyField,"vlaueFiled1,vlaueFiled2,vlaueFiled3,..."
        tableFactory.createTable(TABLE_NAME, "id", "item_price,item_name");
    }

    //get record
    function get(string memory id) public view returns (bool, int256, string memory) {
        KVTable table = tableFactory.openTable(TABLE_NAME);
        bool ok = false;
        Entry entry;
        (ok, entry) = table.get(id);
        int256 item_price;
        string memory item_name;
        if (ok) {
            item_price = entry.getInt("item_price");
            item_name = entry.getString("item_name");
        }
        return (ok, item_price, item_name);
    }

    //set record
    function set(string memory id, int256 item_price, string memory item_name)
    public
    returns (int256)
    {
        KVTable table = tableFactory.openTable(TABLE_NAME);
        Entry entry = table.newEntry();
        // the length of entry's field value should < 16MB
        entry.set("id", id);
        entry.set("item_price", item_price);
        entry.set("item_name", item_name);
        // the first parameter length of set should <= 255B
        int256 count = table.set(id, entry);
        emit SetResult(count);
        return count;
    }
}

安装solc编译器

该编译器用于将 sol 合约文件编译成 abi 和 bin 文件,目前FISCO BCOS支持的solc编译器版本0.4.25/0.5.2,即将支持0.6.10

# 该指令在 kvtabletest 文件夹中执行
bash ../tools/download_solc.sh -v 0.4.25

构建 go-sdk 的代码生成工具 abigen

该工具用于将 abi 和 bin 文件转换为 go 文件

# 该指令在 kvtabletest 文件夹中执行,编译生成 abigen 工具
go build ../cmd/abigen

编译生成 go 文件

先利用 solc 编译合约文件 KVTableTest.sol,生成 abi 和 bin 文件

# 该指令在 kvtabletest 文件夹中执行(控制台可能会打印 warning)
./solc-0.4.25 --bin --abi -o ./ ./KVTableTest.sol

kvtabletest 目录下会生成 KVTableTest.bin、KVTableTest.abi和其它一些文件。此时利用 abigen 工具将 KVTableTest.bin 和 KVTableTest.abi 转换成 KVTableTest.go:

# 该指令在 kvtabletest 文件夹中执行
./abigen --bin ./KVTableTest.bin --abi ./KVTableTest.abi --pkg kvtabletest --type KVTableTest --out ./KVTableTest.go

最后 kvtabletest 文件夹下面存在以下6个文件和其它若干文件:

KVTableTest.abi 、KVTableTest.bin、KVTableTest.go、KVTableTest.sol、solc-0.4.25、abigen

准备建立ssl连接需要的证书

使用build_chain.sh脚本搭建区块链时会在./nodes/127.0.0.1/sdk文件夹中生成sdk证书、私钥以及ca证书,需要将这三个文件拷贝至go-sdk主目录

部署合约

在 kvtabletest 文件夹中创建 contract 文件夹,在 contract 文件夹中创建 kvtabletest_main.go 文件,调用 KVTableTest.go 部署智能合约。合约将创建 t_kvtest 表,该表用于记录某公司仓库中物资,以唯一的物资编号作为主key,保存物资的名称和价格。

package main

import (
	"fmt"
	"log"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	kvtable "github.com/FISCO-BCOS/go-sdk/kvtabletest" // import kvtabletest
)

func main(){
	config := &conf.ParseConfig("config.toml")[0]

	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}
	address, tx, instance, err := kvtable.DeployKVTableTest(client.GetTransactOpts(), client)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("contract address: ", address.Hex())  // the address should be saved
	fmt.Println("transaction hash: ", tx.Hash().Hex())
	_ = instance
}

注解

  • 合约地址需要手动保存,调用合约接口时使用

调用合约set接口

在 contract 文件夹中新建 kvtabletest_set.go 文件,该文件调用合约 set 接口,向 t_kvtest 表中插入一条数据:id=”100010001001”、item_name=”Laptop”、item_price=6000。

package main

import (
	"fmt"
	"log"
	"math/big"
	"strconv"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	kvtable "github.com/FISCO-BCOS/go-sdk/kvtabletest"
	"github.com/ethereum/go-ethereum/common"
)

func main() {
	config := &conf.ParseConfig("config.toml")[0]
	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}

	// load the contract
	contractAddress := common.HexToAddress("contract addree in hex") // 0x9526BDd51d7F346ec2B48192f25a800825A8dBF3
	instance, err := kvtable.NewKVTableTest(contractAddress, client)
	if err != nil {
		log.Fatal(err)
	}

	kvtabletestSession := &kvtable.KVTableTestSession{Contract: instance, CallOpts: *client.GetCallOpts(), TransactOpts: *client.GetTransactOpts()}

	id := "100010001001"
	item_name := "Laptop"
	item_price := big.NewInt(6000)
	tx, err := kvtabletestSession.Set(id,item_price,item_name)    // call set API
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("tx sent: %s\n", tx.Hash().Hex())

	// wait for the mining
	receipt, err := client.WaitMined(tx)
	if err != nil {
		log.Fatalf("tx mining error:%v\n", err)
	}
	fmt.Printf("transaction hash of receipt: %s\n", receipt.GetTransactionHash())
	setedLines, err := strconv.Atoi(receipt.Output[2:])
	if err != nil {
		log.Fatalf("error when transfer string to int: %v\n", err)
	}
	fmt.Printf("seted lines: %v\n", setedLines)
}

调用合约get接口

在 contract 文件夹中新建 kvtabletest_get.go 文件,该文件调用合约 get 接口,查看id=”100010001001” 在表 t_kvtest 中的数据。

package main

import (
	"fmt"
	"log"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	kvtable "github.com/FISCO-BCOS/go-sdk/kvtabletest"
	"github.com/ethereum/go-ethereum/common"
)

func main() {
	config := &conf.ParseConfig("config.toml")[0]
	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}

	// load the contract
	contractAddress := common.HexToAddress("contract addree in hex") // 0x481D3A1dcD72cD618Ea768b3FbF69D78B46995b0
	instance, err := kvtable.NewKVTableTest(contractAddress, client)
	if err != nil {
		log.Fatal(err)
	}

	kvtabletestSession := &kvtable.KVTableTestSession{Contract: instance, CallOpts: *client.GetCallOpts(), TransactOpts: *client.GetTransactOpts()}

	id := "100010001001"

	bool, item_price, item_name, err := kvtabletestSession.Get(id)  // call get API
	if err != nil {
		log.Fatal(err)
	}
	if !bool {
		log.Fatalf("id:%v is not found \n", id)
	}
	fmt.Printf("id: %v, item_price: %v, item_name: %v \n", id, item_price, item_name)
}

TableTest样例

准备Table.sol合约文件

在 go-sdk 主目录中新建 tabletest 文件夹,拷贝 Table.sol 合约。

# 创建 tabletest 文件夹
mkdir tabletest && cd tabletest

# 拷贝 Table.sol 合约
cp ../.ci/Table/Table.sol ./

准备TableTest.sol合约文件

该合约调用 AMDB 专用的智能合约 Table.sol,实现创建用户表 t_test,并对t_test 表进行增删改查。

pragma solidity>=0.4.24 <0.6.11;
pragma experimental ABIEncoderV2;

import "./Table.sol";

contract TableTest {
    event CreateResult(int256 count);
    event InsertResult(int256 count);
    event UpdateResult(int256 count);
    event RemoveResult(int256 count);

    TableFactory tableFactory;
    string constant TABLE_NAME = "t_test";
    constructor() public {
        tableFactory = TableFactory(0x1001); //The fixed address is 0x1001 for TableFactory
        // the parameters of createTable are tableName,keyField,"vlaueFiled1,vlaueFiled2,vlaueFiled3,..."
        tableFactory.createTable(TABLE_NAME, "name", "item_id,item_name");
    }

    //select records
    function select(string memory name)
    public
    view
    returns (string[] memory, int256[] memory, string[] memory)
    {
        Table table = tableFactory.openTable(TABLE_NAME);

        Condition condition = table.newCondition();

        Entries entries = table.select(name, condition);
        string[] memory user_name_bytes_list = new string[](
            uint256(entries.size())
        );
        int256[] memory item_id_list = new int256[](uint256(entries.size()));
        string[] memory item_name_bytes_list = new string[](
            uint256(entries.size())
        );

        for (int256 i = 0; i < entries.size(); ++i) {
            Entry entry = entries.get(i);

            user_name_bytes_list[uint256(i)] = entry.getString("name");
            item_id_list[uint256(i)] = entry.getInt("item_id");
            item_name_bytes_list[uint256(i)] = entry.getString("item_name");
        }

        return (user_name_bytes_list, item_id_list, item_name_bytes_list);
    }
    //insert records
    function insert(string memory name, int256 item_id, string memory item_name)
    public
    returns (int256)
    {
        Table table = tableFactory.openTable(TABLE_NAME);

        Entry entry = table.newEntry();
        entry.set("name", name);
        entry.set("item_id", item_id);
        entry.set("item_name", item_name);

        int256 count = table.insert(name, entry);
        emit InsertResult(count);

        return count;
    }
    //update records
    function update(string memory name, int256 item_id, string memory item_name)
    public
    returns (int256)
    {
        Table table = tableFactory.openTable(TABLE_NAME);

        Entry entry = table.newEntry();
        entry.set("item_name", item_name);

        Condition condition = table.newCondition();
        condition.EQ("name", name);
        condition.EQ("item_id", item_id);

        int256 count = table.update(name, entry, condition);
        emit UpdateResult(count);

        return count;
    }
    //remove records
    function remove(string memory name, int256 item_id) public returns (int256) {
        Table table = tableFactory.openTable(TABLE_NAME);

        Condition condition = table.newCondition();
        condition.EQ("name", name);
        condition.EQ("item_id", item_id);

        int256 count = table.remove(name, condition);
        emit RemoveResult(count);

        return count;
    }
}

安装solc编译器

该编译器用于将 sol 合约文件编译成 abi 和 bin 文件,目前FISCO BCOS支持的solc编译器版本0.4.25/0.5.2,即将支持0.6.10

# 该指令在 tabletest 文件夹中执行
bash ../tools/download_solc.sh -v 0.4.25

构建 go-sdk 的代码生成工具 abigen

该工具用于将 abi 和 bin 文件转换为 go 文件

# 该指令在 tabletest 文件夹中执行,编译生成 abigen 工具
go build ../cmd/abigen

编译生成 go 文件

先利用 solc 编译合约文件 TableTest.sol,生成 abi 和 bin 文件

# 该指令在 tabletest 文件夹中执行(控制台可能会打印 warning)
./solc-0.4.25 --bin --abi -o ./ ./TableTest.sol

tabletest 目录下会生成 TableTest.bin、TableTest.abi和其它一些文件。此时利用 abigen 工具将 TableTest.bin 和 TableTest.abi 转换成 TableTest.go:

# 该指令在 tabletest 文件夹中执行
./abigen --bin ./TableTest.bin --abi ./TableTest.abi --pkg tabletest --type TableTest --out ./TableTest.go

最后 tabletest 文件夹下面存在以下6个文件和其它若干文件:

TableTest.abi 、TableTest.bin、TableTest.go、TableTest.sol、solc-0.4.25、abigen

准备建立ssl连接需要的证书

使用build_chain.sh脚本搭建区块链时会在./nodes/127.0.0.1/sdk文件夹中生成sdk证书、私钥以及ca证书,需要将这三个文件拷贝至go-sdk主目录

部署合约

在 tabletest 文件夹中创建 contract 文件夹,在 contract 文件夹中创建 tabletest_main.go 文件,调用 TableTest.go 部署智能合约。合约将创建用户表 t_test,该表有三个字段 name、item_id 和 item_name,用于记录某公司员工领用的物资和编号信息。

package main

import (
	"fmt"
	"log"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	table "github.com/FISCO-BCOS/go-sdk/tabletest" // import tabletest
)

func main(){
	config := &conf.ParseConfig("config.toml")[0]

	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}
	address, tx, instance, err := table.DeployTableTest(client.GetTransactOpts(), client)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("contract address: ", address.Hex())  // the address should be saved
	fmt.Println("transaction hash: ", tx.Hash().Hex())
	_ = instance
}

注解

  • 合约地址需要手动保存,调用合约接口时使用

调用合约Insert接口

在 contract 文件夹中新建 tabletest_insert.go 文件,该文件调用合约 Insert 接口,向用户表 t_test 插入一条数据:name=”Bob”、item_id=100010001001、item_name=”Laptop”。

package main

import (
	"fmt"
	"log"
	"math/big"
	"strconv"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	table "github.com/FISCO-BCOS/go-sdk/tabletest"
	"github.com/ethereum/go-ethereum/common"
)

func main() {
	config := &conf.ParseConfig("config.toml")[0]
	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}

	// load the contract
	contractAddress := common.HexToAddress("contract addree in hex") // 0x9526BDd51d7F346ec2B48192f25a800825A8dBF3
	instance, err := table.NewTableTest(contractAddress, client)
	if err != nil {
		log.Fatal(err)
	}

	tabletestSession := &table.TableTestSession{Contract: instance, CallOpts: *client.GetCallOpts(), TransactOpts: *client.GetTransactOpts()}

	name := "Bob"
	item_id := big.NewInt(100010001001)
	item_name := "Laptop"
	tx, err := tabletestSession.Insert(name,item_id,item_name)    // call Insert API
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("tx sent: %s\n", tx.Hash().Hex())

	// wait for the mining
	receipt, err := client.WaitMined(tx)
	if err != nil {
		log.Fatalf("tx mining error:%v\n", err)
	}
	fmt.Printf("transaction hash of receipt: %s\n", receipt.GetTransactionHash())
	insertedLines, err := strconv.Atoi(receipt.Output[2:])
	if err != nil {
		log.Fatalf("error when transfer string to int: %v\n", err)
	}
	fmt.Printf("inserted lines: %v\n", insertedLines)
}

调用合约select接口

在 contract 文件夹中新建 tabletest_select.go 文件,该文件调用合约 select 接口,查看用户 “Bob” 在用户表 t_test 中的数据。

package main

import (
	"fmt"
	"log"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	table "github.com/FISCO-BCOS/go-sdk/tabletest"
	"github.com/ethereum/go-ethereum/common"
)

func main() {
	config := &conf.ParseConfig("config.toml")[0]
	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}

	// load the contract
	contractAddress := common.HexToAddress("contract addree in hex") // 0x481D3A1dcD72cD618Ea768b3FbF69D78B46995b0
	instance, err := table.NewTableTest(contractAddress, client)
	if err != nil {
		log.Fatal(err)
	}

	tabletestSession := &table.TableTestSession{Contract: instance, CallOpts: *client.GetCallOpts(), TransactOpts: *client.GetTransactOpts()}

	name := "Bob"

	names, item_ids, item_names, err := tabletestSession.Select(name)  // call select API
	if err != nil {
		log.Fatal(err)
	}
	for i:=0; i<len(names); i++ {
		fmt.Printf("name: %v, item_id: %v, item_name: %v \n", names[i], item_ids[i], item_names[i])
	}

}

调用合约update接口

在 contract 文件夹中新建 tabletest_update.go 文件,该文件调用合约 update 接口,更新用户表 t_test 中用户 “Bob” 的数据,将 item_name 从 Laptop 修改为 Macbook Pro。

package main

import (
	"fmt"
	"log"
	"math/big"
	"strconv"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	table "github.com/FISCO-BCOS/go-sdk/tabletest"
	"github.com/ethereum/go-ethereum/common"
)

func main() {
	config := &conf.ParseConfig("config.toml")[0]
	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}

	// load the contract
	contractAddress := common.HexToAddress("contract addree in hex") // 0x9526BDd51d7F346ec2B48192f25a800825A8dBF3
	instance, err := table.NewTableTest(contractAddress, client)
	if err != nil {
		log.Fatal(err)
	}

	tabletestSession := &table.TableTestSession{Contract: instance, CallOpts: *client.GetCallOpts(), TransactOpts: *client.GetTransactOpts()}

	name := "Bob"
	item_id := big.NewInt(100010001001)
	item_name := "Macbook Pro"
	tx, err := tabletestSession.Update(name,item_id,item_name)    // call Update API
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("tx sent: %s\n", tx.Hash().Hex())

	// wait for the mining
	receipt, err := client.WaitMined(tx)
	if err != nil {
		log.Fatalf("tx mining error:%v\n", err)
	}
	fmt.Printf("transaction hash of receipt: %s\n", receipt.GetTransactionHash())
	updatedLines, err := strconv.Atoi(receipt.Output[2:])
	if err != nil {
		log.Fatalf("error when transfer string to int: %v\n", err)
	}
	fmt.Printf("updated lines: %v\n", updatedLines)
}

调用合约remove接口

在 contract 文件夹中新建 tabletest_remove.go 文件,该文件调用合约 remove 接口,删除用户表 t_test 中用户 “Bob” 的数据。

package main

import (
	"fmt"
	"log"
	"math/big"
	"strconv"

	"github.com/FISCO-BCOS/go-sdk/client"
	"github.com/FISCO-BCOS/go-sdk/conf"
	table "github.com/FISCO-BCOS/go-sdk/tabletest"
	"github.com/ethereum/go-ethereum/common"
)

func main() {
	config := &conf.ParseConfig("config.toml")[0]
	client, err := client.Dial(config)
	if err != nil {
		log.Fatal(err)
	}

	// load the contract
	contractAddress := common.HexToAddress("contract addree in hex") // 0x9526BDd51d7F346ec2B48192f25a800825A8dBF3
	instance, err := table.NewTableTest(contractAddress, client)
	if err != nil {
		log.Fatal(err)
	}

	tabletestSession := &table.TableTestSession{Contract: instance, CallOpts: *client.GetCallOpts(), TransactOpts: *client.GetTransactOpts()}

	name := "Bob"
	item_id := big.NewInt(100010001001)
	tx, err := tabletestSession.Remove(name,item_id)    // call Remove API
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("tx sent: %s\n", tx.Hash().Hex())

	// wait for the mining
	receipt, err := client.WaitMined(tx)
	if err != nil {
		log.Fatalf("tx mining error:%v\n", err)
	}
	fmt.Printf("transaction hash of receipt: %s\n", receipt.GetTransactionHash())
	removedLines, err := strconv.Atoi(receipt.Output[2:])
	if err != nil {
		log.Fatalf("error when transfer string to int: %v\n", err)
	}
	fmt.Printf("removed lines: %v\n", removedLines)
}

国密样例

使用国密特性的开发流程和非国密大致相同,不同点在于以下几部分:

  • 搭建的 FISCO BCOS 区块链网络需要开启国密特性,可参考:国密支持
  • go-sdk 的 config.toml 配置文件中 KeyFile 配置项,需要将非国密私钥替换为国密私钥
  • go-sdk 的 config.toml 配置文件中 SMCrypto 配置项,需要修改为 true
  • 安装 solc 编译器时需要添加 -g 选项,替换为国密版本
  • 使用 abigen 工具将 bin 和 abi 转换为 go 文件时,需要添加参数 –smcrypto=true

HelloWorld样例

准备HelloWorld.sol合约文件

在 go-sdk 主目录中新建 helloworld 文件夹,在该文件夹中创建 HelloWorld.sol 合约。该合约提供两个接口,分别是get()和set(),用于获取/设置合约变量name。合约内容如下

pragma solidity>=0.4.24 <0.6.11;

contract HelloWorld {
    string name;

    constructor() public {
        name = "Hello, World!";
    }

    function get() public view returns (string memory) {
        return name;
    }

    function set(string memory n) public {
        name = n;
    }
}

安装国密solc编译器

该编译器用于将 sol 合约文件编译成 abi 和 bin 文件

# 该指令在helloworld文件夹中执行
bash ../tools/download_solc.sh -v 0.4.25 -g

构建go-sdk的代码生成工具abigen

该工具用于将 abi 和 bin 文件转换为 go 文件

# 该指令在helloworld文件夹中执行,编译生成abigen工具
go build ../cmd/abigen

编译生成go文件

先利用solc编译合约文件HelloWorld.sol,生成abi和bin文件

# 该指令在helloworld文件夹中执行
./solc-0.4.25-gm --bin --abi -o ./ ./HelloWorld.sol

helloworld目录下会生成HelloWorld.bin和HelloWorld.abi。此时利用abigen工具将HelloWorld.bin和HelloWorld.abi转换成HelloWorld.go:

# 该指令在helloworld文件夹中执行
./abigen --bin ./HelloWorld.bin --abi ./HelloWorld.abi --pkg helloworld --type HelloWorld --out ./HelloWorld.go --smcrypto=true
  • 接下来的步骤同非国密,不占用多余篇幅