概述: 用Go搭建模拟客户端作为测试以太坊应用程序的客户端的教程。

使用模拟客户端

您可以使用模拟客户端来快速轻松地在本地测试您的交易,非常适合单元测试。为了开始,我们需要一个带有初始ETH的账户。为此,首先生成一个账户私钥。

  1. privateKey, err := crypto.GenerateKey()
  2. if err != nil {
  3. log.Fatal(err)
  4. }

接着从accounts/abi/bind包创建一个NewKeyedTransactor,并为其传递私钥。

  1. auth := bind.NewKeyedTransactor(privateKey)

下一步是创建一个创世账户并为其分配初始余额。我们将使用core包的GenesisAccount类型。

  1. balance := new(big.Int)
  2. balance.SetString("10000000000000000000", 10) // 10 eth in wei
  3. address := auth.From
  4. genesisAlloc := map[common.Address]core.GenesisAccount{
  5. address: {
  6. Balance: balance,
  7. },
  8. }

现在我们将创世分配结构体和配置好的汽油上限传给account/abi/bind/backends包的NewSimulatedBackend方法,该方法将返回一个新的模拟以太坊客户端。

  1. blockGasLimit := uint64(4712388)
  2. client := backends.NewSimulatedBackend(genesisAlloc, blockGasLimit)

您可以像往常一样使用此客户端。作为一个示例,我们将构造一个新的交易并进行广播。

  1. fromAddress := auth.From
  2. nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
  3. if err != nil {
  4. log.Fatal(err)
  5. }
  6. value := big.NewInt(1000000000000000000) // in wei (1 eth)
  7. gasLimit := uint64(21000) // in units
  8. gasPrice, err := client.SuggestGasPrice(context.Background())
  9. if err != nil {
  10. log.Fatal(err)
  11. }
  12. toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
  13. var data []byte
  14. tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
  15. chainID := big.NewInt(1)
  16. signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
  17. if err != nil {
  18. log.Fatal(err)
  19. }
  20. err = client.SendTransaction(context.Background(), signedTx)
  21. if err != nil {
  22. log.Fatal(err)
  23. }
  24. fmt.Printf("tx sent: %s\n", signedTx.Hash().Hex()) // tx sent: 0xec3ceb05642c61d33fa6c951b54080d1953ac8227be81e7b5e4e2cfed69eeb51

到现在为止,您可能想知道交易何时才会被开采。为了“开采”它,您还必须做一件额外的事情,在客户端调用Commit提交新开采的区块。

  1. client.Commit()

现在您可以获取交易收据并看见其已被处理。

  1. receipt, err := client.TransactionReceipt(context.Background(), signedTx.Hash())
  2. if err != nil {
  3. log.Fatal(err)
  4. }
  5. if receipt == nil {
  6. log.Fatal("receipt is nil. Forgot to commit?")
  7. }
  8. fmt.Printf("status: %v\n", receipt.Status) // status: 1

因此,请记住:模拟客户端允许您使用模拟客户端的Commit方法手动开采区块。

完整代码

client_simulated.go

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "math/big"
  7. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  8. "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
  9. "github.com/ethereum/go-ethereum/common"
  10. "github.com/ethereum/go-ethereum/core"
  11. "github.com/ethereum/go-ethereum/core/types"
  12. "github.com/ethereum/go-ethereum/crypto"
  13. )
  14. func main() {
  15. privateKey, err := crypto.GenerateKey()
  16. if err != nil {
  17. log.Fatal(err)
  18. }
  19. auth := bind.NewKeyedTransactor(privateKey)
  20. balance := new(big.Int)
  21. balance.SetString("10000000000000000000", 10) // 10 eth in wei
  22. address := auth.From
  23. genesisAlloc := map[common.Address]core.GenesisAccount{
  24. address: {
  25. Balance: balance,
  26. },
  27. }
  28. blockGasLimit := uint64(4712388)
  29. client := backends.NewSimulatedBackend(genesisAlloc, blockGasLimit)
  30. fromAddress := auth.From
  31. nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
  32. if err != nil {
  33. log.Fatal(err)
  34. }
  35. value := big.NewInt(1000000000000000000) // in wei (1 eth)
  36. gasLimit := uint64(21000) // in units
  37. gasPrice, err := client.SuggestGasPrice(context.Background())
  38. if err != nil {
  39. log.Fatal(err)
  40. }
  41. toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
  42. var data []byte
  43. tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
  44. chainID := big.NewInt(1)
  45. signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
  46. if err != nil {
  47. log.Fatal(err)
  48. }
  49. err = client.SendTransaction(context.Background(), signedTx)
  50. if err != nil {
  51. log.Fatal(err)
  52. }
  53. fmt.Printf("tx sent: %s\n", signedTx.Hash().Hex()) // tx sent: 0xec3ceb05642c61d33fa6c951b54080d1953ac8227be81e7b5e4e2cfed69eeb51
  54. client.Commit()
  55. receipt, err := client.TransactionReceipt(context.Background(), signedTx.Hash())
  56. if err != nil {
  57. log.Fatal(err)
  58. }
  59. if receipt == nil {
  60. log.Fatal("receipt is nil. Forgot to commit?")
  61. }
  62. fmt.Printf("status: %v\n", receipt.Status) // status: 1
  63. }