使用 Go 與以太坊區塊鏈互動(二)

Peter Lai
Taipei Ethereum Meetup
7 min readSep 15, 2018

--

前言

上一篇文章(使用 Go 與以太坊區塊鏈互動(一))介紹了用 Go 與以太坊區塊鏈建立連線、取得目前區塊號碼或是送出一筆交易(私鑰儲存在節點上),這篇文章就介紹怎麼以 go-ethereum 產生私鑰並簽章一筆交易,最後再送到以太坊區塊鏈(geth, parity

文章內沒有說明怎麼建立連線,關於連線的方式可以參考上一篇文章。

文章內沒有說明怎麼建置 Go 的開發環境,關於 Go 的開發環境環境建置可以去谷歌查詢。

產生私鑰

產生私鑰的方式有很多種,可以從字串還原或是重新產生。

從字串還原

// from hex
privKey, err := crypto.HexToECDSA("your private key");
// from other
privKey, err := crypto.ToECDSA(DecodeStringToBytes("your private key"))
if err != nil {
fmt.Println(err)
} else {
// do something
}

重新產生

privKey, err := crypto.GenerateKey()

if err != nil {
fmt.Println(err)
} else {
// do something
}

取得公鑰和以太坊位置

publicKey :=  privKey.PublicKey
address := crypto.PubkeyToAddress(publicKey).Hex()

簽章交易

上面已經取得了私鑰,接下來可以來簽章交易,簽章交易有三個步驟:產生交易物件、產生 Signer 物件和用私鑰簽章。

產生交易物件

amount := big.NewInt(1)
gasLimit := uint64(90000)
gasPrice := big.NewInt(0)
data := []byte{}
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)

產生 Signer 物件

// EIP155 signer
// signer := types.NewEIP155Signer(big.NewInt(4))
signer := types.HomesteadSigner{}

用私鑰簽章

signedTx, _ := types.SignTx(tx, signer, privKey)

在測試這段時一直認為簽章交易的函數會放在 crypto,所以一直往那邊嘗試,結果簽章的結果一直沒有 v,後來才在 types 裡找到。

Transaction 裡面其實也有 Hash 函數,之所以從 Signer 呼叫是因為 Signer 會處理交易的資料,例如:EIP155 會將 v 增加 chainId * 2。

送出交易

go-ethereum/ethclient 原先就有 SendTransaction(eth_sendRawTransaction)

func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error {
data, err := rlp.EncodeToBytes(tx)
if err != nil {
return err
}
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data))
}

注意 CallContext 函數第二個參數帶入 nil,所以我們會取不到交易編號,這邊要新增一個 SendRawTransaction 函數來取得區塊編號

func (ec *Client) SendRawTransaction(ctx context.Context, tx *types.Transaction) (common.Hash, error) {
var txHash common.Hash
if data, err := rlp.EncodeToBytes(tx);err != nil {
return txHash, err
} else {
err := ec.rpcClient.CallContext(ctx, &txHash, "eth_sendRawTransaction", common.ToHex(data))
return txHash, err
}
}

如果不想新增函數,也可以計算出交易編號(以太坊的交易編號是 hash 序列化後的交易)

// serializedTx: rlp encode tx with r, s, v
txHash = sha3(serializedTx)

接下來我們就可以試著送出一筆交易了!

amount := big.NewInt(1)
gasLimit := uint64(90000)
gasPrice := big.NewInt(0)
data := []byte{}
tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
// EIP155 signer
// signer := types.NewEIP155Signer(big.NewInt(4))
signer := types.HomesteadSigner{}
signedTx, _ := types.SignTx(tx, signer, privKey)
// client.EthClient.SendTransaction(context.TODO(), signedTx)
txHash, err := client.SendRawTransaction(context.TODO(), signedTx)
// do something to txHash

實際測試

看完上面之後來實際測試,測試的網路為 rinkeby,下面為測試的交易資料。

amount := big.NewInt(100000000000) // 0.0000001 Ether
gasLimit := uint64(90000)
gasPrice := big.NewInt(1000000000) // 1 Gwei
data := []byte("send from sc0vu")

用私鑰簽章

tx := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data)
// EIP155 signer
signer := types.NewEIP155Signer(big.NewInt(4))
// works: signer := types.HomesteadSigner{}
signedTx, _ := types.SignTx(tx, signer, privKey)
if txHash, err := client.SendRawTransaction(context.TODO(), signedTx); err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(txHash.String())
}

稍微等一下就能在線上看到剛剛的交易

Rinkeby 測試網路的 Chain id 為 4,其他網路的 Chain id 可以看 EIP155

結論

這篇文章介紹以 go-ethereum 產生私鑰並簽章一筆交易,文章中只貼上部分的程式碼,完整的程式碼可以在 Github 看到,有任何錯誤或是問題也歡迎各位發 issue。

參考

--

--