在昨天講述了怎麼使用Go-ethereum建立起一條私有鏈,還沒看的可以點這邊觀看,而今天要來看的是當我們有一條私有鏈時,該怎麼用程式跟他互動,當然也是可以透過同樣的方式與現在公開的測試鏈或者以太坊主鏈,那我們就開始吧!我這邊是使用Golang語言做示範,而我這邊Go的版本為1.20.4。
在一個Golang的模組裡面要引進Go-ethereum就必須進入你的Golang專案資料夾裡面,在command line裡面下以下指令
$ go get github.com/ethereum/go-ethereum
下完指令後,Go-ethereum便會到你的專案模組了!我這邊使用的Go-ethereum版本為1.10.26。
在開始做交易之前,一定要先跟區塊鏈的節點做好連線,由於昨天我們已經弄出一條私有鏈了,使用Go-ethereum裡面的ethclient來進行連線,程式碼如下:
package main
import (
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
client, err := ethclient.Dial("連線網址,如果是要連你的私有鏈的話,就是 http://127.0.0.1:[你設定的PORT]")
if err != nil {
panic(err)
}
}
這時候可能會發現沒辦法import "github.com/ethereum/go-ethereum/ethclient"
進來,那是因為他有一些相依的套件還沒抓進來,可以對command line下以下指令:
$ go mod tidy
透過go mod tidy
可以用來獲取所需要的套件。
當做完之後,基本上許多與鏈上的互動都跟這個client做各種不同的展開,使用者個client已經可以做到很多不同的事情,像是查詢各種東西,例如帳戶餘額、帳戶的nonce、鏈上的交易、鏈上特定區塊內容等等。
在做交易的時候,基本上都會圍繞一個帳戶做展開,而我們都知道,在以太坊上一對公私鑰對就可以代表著一個帳戶,而如果一個帳戶要進行交易就要使用他的私鑰進行簽章,所以要進行交易一定要有要交易的人的私鑰,而要將私鑰放入程式中可以使用Go-ethereum裡面的crypto來將私鑰字串轉成他提供的一種PrivateKey的型別,程式碼如下:
package main
import (
"github.com/ethereum/go-ethereum/crypto"
)
func main() {
ecdsa_prk, err := crypto.HexToECDSA("你的私鑰16進位數字碼(不含0x字首)")
if err != nil {
panic(err)
}
}
而要產生出一個交易,Go-ethereum有做出一個Transaction的型別,在"github.com/ethereum/go-ethereum/core/types"
裡面,所以我們可以用他裡面提供的
func types.NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *types.Transaction
來新增一個Transaction,而以下便是對他帶入參數的解釋:
"github.com/ethereum/go-ethereum/common"
中,而裡面有個func common.HexToAddress(s string) common.Address
函數可以透過帶入具有0x為字首的16進位數字字串將他轉換成Address型別。func (*ethclient.Client).SuggestGasPrice(ctx context.Context) (*big.Int, error)
會建議你填的gas價格,而這個價格的算法如果都沒調整的話,預設是用最新的20個block的gas價格取平均。將這些東西都填好後,就會產生出一個Transaction,但是在讓他上鏈之前要先對他進行簽章,這時候我們要使用的是以下函式:
func types.SignTx(tx *types.Transaction, s types.Signer, prv *ecdsa.PrivateKey) (*types.Transaction, error)
這邊要帶入的是剛剛產生出來的Transaction以及你帳戶的私鑰,還有一個很重要的,便是你要使用什麼規則簽名,在現在的以太坊中,交易使用的簽章方式都是走EIP-155,裡面要帶入這個鏈的chainId,在"github.com/ethereum/go-ethereum/core/types"
中有func types.NewEIP155Signer(chainId *big.Int) types.EIP155Signer
可以幫你新增出他要的Signer型別,將該帶入的都帶入後,就會產生出一個有簽章的Transaction了,之後透過client將他丟上鏈就大功告成了!
完整的程式碼如下:
package main
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
client, err := ethclient.Dial("連線網址,如果是要連你的私有鏈的話,就是 http://127.0.0.1:[你設定的PORT]")
if err != nil {
panic(err)
}
// 如果不知道那條鏈的chainId可以透過這個方式拿到
chainID, err := client.NetworkID(context.TODO())
if err != nil {
panic(err)
}
sender_prk, err := crypto.HexToECDSA("你的私鑰16進位數字碼(不含0x字首)")
if err != nil {
panic(err)
}
nonce, err := client.PendingNonceAt(context.TODO(), crypto.PubkeyToAddress(sender_prk.PublicKey))
if err != nil {
panic(err)
}
to_addr := common.HexToAddress("你要轉錢給的帳戶地址(含0x字首)")
amount := big.NewInt(1000000) // 這邊是打算轉1000000wei
gasLimit := uint64(21000)
gasPrice, err := client.SuggestGasPrice(context.TODO())
if err != nil {
panic(err)
}
tx := types.NewTransaction(nonce, to_addr, amount, gasLimit, gasPrice, nil)
tx, err = types.SignTx(tx, types.NewEIP155Signer(chainID), sender_prk)
if err != nil {
panic(err)
}
err = client.SendTransaction(context.TODO(), tx)
if err != nil {
panic(err)
}
}
如果成功之後,就代表著交易已經送出去,但不代表著交易上鏈,如果要讓交易上鏈可以透過"github.com/ethereum/go-ethereum/accounts/abi/bind"
裡面提供的func bind.WaitMined(ctx context.Context, b bind.DeployBackend, tx *types.Transaction) (*types.Receipt, error)
將context、client與剛剛的tx帶入後,他就會等到交易上鏈才會執行接下來的動作,如果是用私有鏈的話,記得要設定讓礦工挖礦才可以讓交易上鏈。
以上就是如何使用Go-ethereum來對以太坊進行交易,而剛剛有稍微提到,與智能合約的互動在以太坊上也是一種交易,那麼也是要使用上述方法來達成嗎?我們要怎麼組合出交易所要帶入的data呢?這個就等到明天再繼續講解了!