概述: 用Go向智能合约写入数据的教程。

写入智能合约

这写章节需要了解如何将智能合约的ABI编译成Go的合约文件。如果你还没看, 前先读上一个章节

写入智能合约需要我们用私钥来对交易事务进行签名。

  1. privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
  2. if err != nil {
  3. log.Fatal(err)
  4. }
  5. publicKey := privateKey.Public()
  6. publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
  7. if !ok {
  8. log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
  9. }
  10. fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

我们还需要先查到nonce和燃气价格。

  1. nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
  2. if err != nil {
  3. log.Fatal(err)
  4. }
  5. gasPrice, err := client.SuggestGasPrice(context.Background())
  6. if err != nil {
  7. log.Fatal(err)
  8. }

接下来,我们创建一个新的keyed transactor,它接收私钥。

  1. auth := bind.NewKeyedTransactor(privateKey)

然后我们需要设置keyed transactor的标准交易选项。

  1. auth.Nonce = big.NewInt(int64(nonce))
  2. auth.Value = big.NewInt(0) // in wei
  3. auth.GasLimit = uint64(300000) // in units
  4. auth.GasPrice = gasPrice

现在我们加载一个智能合约的实例。如果你还记得上个章节 我们创建一个名为Store的合约,并使用abigen工具生成一个Go文件。 要初始化它,我们只需调用合约包的New方法,并提供智能合约地址和ethclient,它返回我们可以使用的合约实例。

  1. address := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
  2. instance, err := store.NewStore(address, client)
  3. if err != nil {
  4. log.Fatal(err)
  5. }

我们创建的智能合约有一个名为SetItem的外部方法,它接受solidity“bytes32”格式的两个参数(key,value)。 这意味着Go合约包要求我们传递一个长度为32个字节的字节数组。 调用SetItem方法需要我们传递我们之前创建的auth对象(keyed transactor)。 在幕后,此方法将使用它的参数对此函数调用进行编码,将其设置为事务的data属性,并使用私钥对其进行签名。 结果将是一个已签名的事务对象。

  1. key := [32]byte{}
  2. value := [32]byte{}
  3. copy(key[:], []byte("foo"))
  4. copy(value[:], []byte("bar"))
  5. tx, err := instance.SetItem(auth, key, value)
  6. if err != nil {
  7. log.Fatal(err)
  8. }
  9. fmt.Printf("tx sent: %s\n", tx.Hash().Hex()) // tx sent: 0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870

现在我就可以看到交易已经成功被发送到了以太坊网络了: https://rinkeby.etherscan.io/tx/0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870

要验证键/值是否已设置,我们可以读取智能合约中的值。

  1. result, err := instance.Items(nil, key)
  2. if err != nil {
  3. log.Fatal(err)
  4. }
  5. fmt.Println(string(result[:])) // "bar"

搞定!

完整代码

Commands

  1. solc --abi Store.sol
  2. solc --bin Store.sol
  3. abigen --bin=Store_sol_Store.bin --abi=Store_sol_Store.abi --pkg=store --out=Store.go

Store.sol

  1. pragma solidity ^0.4.24;
  2. contract Store {
  3. event ItemSet(bytes32 key, bytes32 value);
  4. string public version;
  5. mapping (bytes32 => bytes32) public items;
  6. constructor(string _version) public {
  7. version = _version;
  8. }
  9. function setItem(bytes32 key, bytes32 value) external {
  10. items[key] = value;
  11. emit ItemSet(key, value);
  12. }
  13. }

contract_write.go

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  6. "github.com/ethereum/go-ethereum/common"
  7. "github.com/ethereum/go-ethereum/ethclient"
  8. store "./contracts" // for demo
  9. )
  10. func main() {
  11. client, err := ethclient.Dial("https://rinkeby.infura.io")
  12. if err != nil {
  13. log.Fatal(err)
  14. }
  15. privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
  16. if err != nil {
  17. log.Fatal(err)
  18. }
  19. publicKey := privateKey.Public()
  20. publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
  21. if !ok {
  22. log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
  23. }
  24. fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
  25. nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
  26. if err != nil {
  27. log.Fatal(err)
  28. }
  29. gasPrice, err := client.SuggestGasPrice(context.Background())
  30. if err != nil {
  31. log.Fatal(err)
  32. }
  33. auth := bind.NewKeyedTransactor(privateKey)
  34. auth.Nonce = big.NewInt(int64(nonce))
  35. auth.Value = big.NewInt(0) // in wei
  36. auth.GasLimit = uint64(300000) // in units
  37. auth.GasPrice = gasPrice
  38. address := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
  39. instance, err := store.NewStore(address, client)
  40. if err != nil {
  41. log.Fatal(err)
  42. }
  43. key := [32]byte{}
  44. value := [32]byte{}
  45. copy(key[:], []byte("foo"))
  46. copy(value[:], []byte("bar"))
  47. tx, err := instance.SetItem(auth, key, value)
  48. if err != nil {
  49. log.Fatal(err)
  50. }
  51. fmt.Printf("tx sent: %s", tx.Hash().Hex()) // tx sent: 0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870
  52. result, err := instance.Items(nil, key)
  53. if err != nil {
  54. log.Fatal(err)
  55. }
  56. fmt.Println(string(result[:])) // "bar"
  57. }

solc version used for these examples

  1. $ solc --version
  2. 0.4.24+commit.e67f0147.Emscripten.clang