说实话对以太坊还不太熟,这几个月研究并基于以太坊搭建联盟链平台,主要是围绕CA认证、国密兼容和共识算法展开,最近需要整一个go的sdk,于是翻了各种资料如何部署和调用智能合约。

以hello合约作为示例:

// SPDX-License-Identifier: SimPL-2.0
pragma solidity >=0.4.0 <=0.7.0;

contract HelloWorld {

    function Hello() pure public returns(string){
        return "hello world!";
    }

}

说实话,单单安装solc我都费了一番功夫,老是报版本错误,我装的是0.6.12版本,这里要说一点就是:pure 要写在public前面,不然会报错。。。(刚接触solidity,还不知道为什么,如果你知道可以评论指出,先行感谢)

接下来就是编译:

solc --bin hello.sol -o hello.abi

solc --abi hello.sol -o hello.bin

abigen --bin=hello.bin --abi=hello.abi --pkg=contract --out=hello.go

这样就得到了可用的hello.go文件,后续需要导入并使用。

接着是部署和调用:

package ethclient

import(
    "github.com/ethereum/go-ethereum/ethclient/contract"    //hello.go放在该文件夹下
    "github.com/ethereum/go-ethereum/accounts/abi/bind"
    //其他包懒得写,主要是两个
)

func TestDeployContract(t *testing.T) {
    ethclient, err := Dial("http://localhost:8545")
    if err != nil {
        t.Error(err)
    }

    keyJson, err := ioutil.ReadFile("../build/bin/data/keystore/UTC--2020-08-17T03-35-30.955610100Z--520613668132f205d5ee27246098c72bbb793896")
    if err != nil {
        t.Error(err)
    }

    key, err := keystore.DecryptKey(keyJson, "")
    if err != nil {
        t.Error(err)
    }

    publicKey := key.PrivateKey.Public()
    publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
    if !ok {
        t.Error("error casting public key to ECDSA")
    }

    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
    nonce, err := ethclient.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        t.Error(err)
    }

    gasPrice, err := ethclient.SuggestGasPrice(context.Background())
    if err != nil {
        t.Error(err)
    }

    auth := bind.NewKeyedTransactor(key.PrivateKey)
    auth.Nonce = big.NewInt(int64(nonce))
    auth.Value = big.NewInt(0)     // in wei
    auth.GasLimit = uint64(300000) // in units
    auth.GasPrice = gasPrice

    address, tx, _, err := contract.DeployHello(auth, ethclient)    // 部署合约
    if err != nil {
        t.Error(err)
    }

    fmt.Println(address.Hex())      // 0x80bec2b5FbFC19Bc06c3423b6b1De65156528929
    fmt.Println(tx.Hash().Hex())    // 0x414d211952688a35c9a08619823e40ef6c2dd6b288c89375c269bb7946e1aa9b

    // 调用合约,建议部署和调用不要写在同一个函数中,我这里为了简便所以合到了一起
    nonce1, err := ethclient.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        t.Error(err)
    }
    auth.Nonce = big.NewInt(int64(nonce1))

    hello, err := contract.NewHello(address, ethclient)

    helloRaw := contract.HelloRaw{hello}

    helloTx, err := helloRaw.Transact(auth, "Hello")   // 这是hello.go中已经封装好的方法,也可以自己组装交易然后Send
    if err != nil {
        t.Error(err)
    }

    fmt.Printf("txid : %v\n", helloTx.Hash().String())  

}    

细节就不描述了,主要是就两个函数,DeployHello和Transact;其实,在Dial的时候是需要传入一个证书(毕竟是联盟链),但是,这里被我省掉了。