github.com/ethereum/go-ethereum@v1.16.1/ethclient/ethclient_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package ethclient_test 18 19 import ( 20 "bytes" 21 "context" 22 "errors" 23 "fmt" 24 "math/big" 25 "reflect" 26 "testing" 27 "time" 28 29 "github.com/ethereum/go-ethereum" 30 "github.com/ethereum/go-ethereum/accounts/abi" 31 "github.com/ethereum/go-ethereum/common" 32 "github.com/ethereum/go-ethereum/consensus/beacon" 33 "github.com/ethereum/go-ethereum/consensus/ethash" 34 "github.com/ethereum/go-ethereum/core" 35 "github.com/ethereum/go-ethereum/core/types" 36 "github.com/ethereum/go-ethereum/crypto" 37 "github.com/ethereum/go-ethereum/eth" 38 "github.com/ethereum/go-ethereum/eth/ethconfig" 39 "github.com/ethereum/go-ethereum/ethclient" 40 "github.com/ethereum/go-ethereum/node" 41 "github.com/ethereum/go-ethereum/params" 42 "github.com/ethereum/go-ethereum/rpc" 43 ) 44 45 // Verify that Client implements the ethereum interfaces. 46 var ( 47 _ = ethereum.ChainReader(ðclient.Client{}) 48 _ = ethereum.TransactionReader(ðclient.Client{}) 49 _ = ethereum.ChainStateReader(ðclient.Client{}) 50 _ = ethereum.ChainSyncReader(ðclient.Client{}) 51 _ = ethereum.ContractCaller(ðclient.Client{}) 52 _ = ethereum.GasEstimator(ðclient.Client{}) 53 _ = ethereum.GasPricer(ðclient.Client{}) 54 _ = ethereum.LogFilterer(ðclient.Client{}) 55 _ = ethereum.PendingStateReader(ðclient.Client{}) 56 // _ = ethereum.PendingStateEventer(ðclient.Client{}) 57 _ = ethereum.PendingContractCaller(ðclient.Client{}) 58 ) 59 60 var ( 61 testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 62 testAddr = crypto.PubkeyToAddress(testKey.PublicKey) 63 testBalance = big.NewInt(2e15) 64 revertContractAddr = common.HexToAddress("290f1b36649a61e369c6276f6d29463335b4400c") 65 revertCode = common.FromHex("7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452600a6024527f75736572206572726f7200000000000000000000000000000000000000000000604452604e6000fd") 66 ) 67 68 var genesis = &core.Genesis{ 69 Config: params.AllDevChainProtocolChanges, 70 Alloc: types.GenesisAlloc{ 71 testAddr: {Balance: testBalance}, 72 revertContractAddr: {Code: revertCode}, 73 }, 74 ExtraData: []byte("test genesis"), 75 Timestamp: 9000, 76 BaseFee: big.NewInt(params.InitialBaseFee), 77 } 78 79 var testTx1 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ 80 Nonce: 0, 81 Value: big.NewInt(12), 82 GasPrice: big.NewInt(params.InitialBaseFee), 83 Gas: params.TxGas, 84 To: &common.Address{2}, 85 }) 86 87 var testTx2 = types.MustSignNewTx(testKey, types.LatestSigner(genesis.Config), &types.LegacyTx{ 88 Nonce: 1, 89 Value: big.NewInt(8), 90 GasPrice: big.NewInt(params.InitialBaseFee), 91 Gas: params.TxGas, 92 To: &common.Address{2}, 93 }) 94 95 func newTestBackend(config *node.Config) (*node.Node, []*types.Block, error) { 96 // Generate test chain. 97 blocks := generateTestChain() 98 99 // Create node 100 if config == nil { 101 config = new(node.Config) 102 } 103 n, err := node.New(config) 104 if err != nil { 105 return nil, nil, fmt.Errorf("can't create new node: %v", err) 106 } 107 // Create Ethereum Service 108 ecfg := ðconfig.Config{Genesis: genesis, RPCGasCap: 1000000} 109 ethservice, err := eth.New(n, ecfg) 110 if err != nil { 111 return nil, nil, fmt.Errorf("can't create new ethereum service: %v", err) 112 } 113 // Import the test chain. 114 if err := n.Start(); err != nil { 115 return nil, nil, fmt.Errorf("can't start test node: %v", err) 116 } 117 if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil { 118 return nil, nil, fmt.Errorf("can't import test blocks: %v", err) 119 } 120 // Ensure the tx indexing is fully generated 121 for ; ; time.Sleep(time.Millisecond * 100) { 122 progress, err := ethservice.BlockChain().TxIndexProgress() 123 if err == nil && progress.Done() { 124 break 125 } 126 } 127 return n, blocks, nil 128 } 129 130 func generateTestChain() []*types.Block { 131 generate := func(i int, g *core.BlockGen) { 132 g.OffsetTime(5) 133 g.SetExtra([]byte("test")) 134 if i == 1 { 135 // Test transactions are included in block #2. 136 g.AddTx(testTx1) 137 g.AddTx(testTx2) 138 } 139 } 140 _, blocks, _ := core.GenerateChainWithGenesis(genesis, beacon.New(ethash.NewFaker()), 2, generate) 141 return append([]*types.Block{genesis.ToBlock()}, blocks...) 142 } 143 144 func TestEthClient(t *testing.T) { 145 backend, chain, err := newTestBackend(nil) 146 if err != nil { 147 t.Fatal(err) 148 } 149 client := backend.Attach() 150 defer backend.Close() 151 defer client.Close() 152 153 tests := map[string]struct { 154 test func(t *testing.T) 155 }{ 156 "Header": { 157 func(t *testing.T) { testHeader(t, chain, client) }, 158 }, 159 "BalanceAt": { 160 func(t *testing.T) { testBalanceAt(t, client) }, 161 }, 162 "TxInBlockInterrupted": { 163 func(t *testing.T) { testTransactionInBlock(t, client) }, 164 }, 165 "ChainID": { 166 func(t *testing.T) { testChainID(t, client) }, 167 }, 168 "GetBlock": { 169 func(t *testing.T) { testGetBlock(t, client) }, 170 }, 171 "StatusFunctions": { 172 func(t *testing.T) { testStatusFunctions(t, client) }, 173 }, 174 "CallContract": { 175 func(t *testing.T) { testCallContract(t, client) }, 176 }, 177 "CallContractAtHash": { 178 func(t *testing.T) { testCallContractAtHash(t, client) }, 179 }, 180 "AtFunctions": { 181 func(t *testing.T) { testAtFunctions(t, client) }, 182 }, 183 "TransactionSender": { 184 func(t *testing.T) { testTransactionSender(t, client) }, 185 }, 186 } 187 188 t.Parallel() 189 for name, tt := range tests { 190 t.Run(name, tt.test) 191 } 192 } 193 194 func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) { 195 tests := map[string]struct { 196 block *big.Int 197 want *types.Header 198 wantErr error 199 }{ 200 "genesis": { 201 block: big.NewInt(0), 202 want: chain[0].Header(), 203 }, 204 "first_block": { 205 block: big.NewInt(1), 206 want: chain[1].Header(), 207 }, 208 "future_block": { 209 block: big.NewInt(1000000000), 210 want: nil, 211 wantErr: ethereum.NotFound, 212 }, 213 } 214 for name, tt := range tests { 215 t.Run(name, func(t *testing.T) { 216 ec := ethclient.NewClient(client) 217 ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) 218 defer cancel() 219 220 got, err := ec.HeaderByNumber(ctx, tt.block) 221 if !errors.Is(err, tt.wantErr) { 222 t.Fatalf("HeaderByNumber(%v) error = %q, want %q", tt.block, err, tt.wantErr) 223 } 224 if got != nil && got.Number != nil && got.Number.Sign() == 0 { 225 got.Number = big.NewInt(0) // hack to make DeepEqual work 226 } 227 if got.Hash() != tt.want.Hash() { 228 t.Fatalf("HeaderByNumber(%v) got = %v, want %v", tt.block, got, tt.want) 229 } 230 }) 231 } 232 } 233 234 func testBalanceAt(t *testing.T, client *rpc.Client) { 235 tests := map[string]struct { 236 account common.Address 237 block *big.Int 238 want *big.Int 239 wantErr error 240 }{ 241 "valid_account_genesis": { 242 account: testAddr, 243 block: big.NewInt(0), 244 want: testBalance, 245 }, 246 "valid_account": { 247 account: testAddr, 248 block: big.NewInt(1), 249 want: testBalance, 250 }, 251 "non_existent_account": { 252 account: common.Address{1}, 253 block: big.NewInt(1), 254 want: big.NewInt(0), 255 }, 256 "future_block": { 257 account: testAddr, 258 block: big.NewInt(1000000000), 259 want: big.NewInt(0), 260 wantErr: errors.New("header not found"), 261 }, 262 } 263 for name, tt := range tests { 264 t.Run(name, func(t *testing.T) { 265 ec := ethclient.NewClient(client) 266 ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) 267 defer cancel() 268 269 got, err := ec.BalanceAt(ctx, tt.account, tt.block) 270 if tt.wantErr != nil && (err == nil || err.Error() != tt.wantErr.Error()) { 271 t.Fatalf("BalanceAt(%x, %v) error = %q, want %q", tt.account, tt.block, err, tt.wantErr) 272 } 273 if got.Cmp(tt.want) != 0 { 274 t.Fatalf("BalanceAt(%x, %v) = %v, want %v", tt.account, tt.block, got, tt.want) 275 } 276 }) 277 } 278 } 279 280 func testTransactionInBlock(t *testing.T, client *rpc.Client) { 281 ec := ethclient.NewClient(client) 282 283 // Get current block by number. 284 block, err := ec.BlockByNumber(context.Background(), nil) 285 if err != nil { 286 t.Fatalf("unexpected error: %v", err) 287 } 288 289 // Test tx in block not found. 290 if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound { 291 t.Fatal("error should be ethereum.NotFound") 292 } 293 294 // Test tx in block found. 295 tx, err := ec.TransactionInBlock(context.Background(), block.Hash(), 0) 296 if err != nil { 297 t.Fatalf("unexpected error: %v", err) 298 } 299 if tx.Hash() != testTx1.Hash() { 300 t.Fatalf("unexpected transaction: %v", tx) 301 } 302 303 tx, err = ec.TransactionInBlock(context.Background(), block.Hash(), 1) 304 if err != nil { 305 t.Fatalf("unexpected error: %v", err) 306 } 307 if tx.Hash() != testTx2.Hash() { 308 t.Fatalf("unexpected transaction: %v", tx) 309 } 310 311 // Test pending block 312 _, err = ec.BlockByNumber(context.Background(), big.NewInt(int64(rpc.PendingBlockNumber))) 313 if err != nil { 314 t.Fatalf("unexpected error: %v", err) 315 } 316 } 317 318 func testChainID(t *testing.T, client *rpc.Client) { 319 ec := ethclient.NewClient(client) 320 id, err := ec.ChainID(context.Background()) 321 if err != nil { 322 t.Fatalf("unexpected error: %v", err) 323 } 324 if id == nil || id.Cmp(params.AllDevChainProtocolChanges.ChainID) != 0 { 325 t.Fatalf("ChainID returned wrong number: %+v", id) 326 } 327 } 328 329 func testGetBlock(t *testing.T, client *rpc.Client) { 330 ec := ethclient.NewClient(client) 331 332 // Get current block number 333 blockNumber, err := ec.BlockNumber(context.Background()) 334 if err != nil { 335 t.Fatalf("unexpected error: %v", err) 336 } 337 if blockNumber != 2 { 338 t.Fatalf("BlockNumber returned wrong number: %d", blockNumber) 339 } 340 // Get current block by number 341 block, err := ec.BlockByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) 342 if err != nil { 343 t.Fatalf("unexpected error: %v", err) 344 } 345 if block.NumberU64() != blockNumber { 346 t.Fatalf("BlockByNumber returned wrong block: want %d got %d", blockNumber, block.NumberU64()) 347 } 348 // Get current block by hash 349 blockH, err := ec.BlockByHash(context.Background(), block.Hash()) 350 if err != nil { 351 t.Fatalf("unexpected error: %v", err) 352 } 353 if block.Hash() != blockH.Hash() { 354 t.Fatalf("BlockByHash returned wrong block: want %v got %v", block.Hash().Hex(), blockH.Hash().Hex()) 355 } 356 // Get header by number 357 header, err := ec.HeaderByNumber(context.Background(), new(big.Int).SetUint64(blockNumber)) 358 if err != nil { 359 t.Fatalf("unexpected error: %v", err) 360 } 361 if block.Header().Hash() != header.Hash() { 362 t.Fatalf("HeaderByNumber returned wrong header: want %v got %v", block.Header().Hash().Hex(), header.Hash().Hex()) 363 } 364 // Get header by hash 365 headerH, err := ec.HeaderByHash(context.Background(), block.Hash()) 366 if err != nil { 367 t.Fatalf("unexpected error: %v", err) 368 } 369 if block.Header().Hash() != headerH.Hash() { 370 t.Fatalf("HeaderByHash returned wrong header: want %v got %v", block.Header().Hash().Hex(), headerH.Hash().Hex()) 371 } 372 } 373 374 func testStatusFunctions(t *testing.T, client *rpc.Client) { 375 ec := ethclient.NewClient(client) 376 377 // Sync progress 378 progress, err := ec.SyncProgress(context.Background()) 379 if err != nil { 380 t.Fatalf("unexpected error: %v", err) 381 } 382 if progress != nil { 383 t.Fatalf("unexpected progress: %v", progress) 384 } 385 386 // NetworkID 387 networkID, err := ec.NetworkID(context.Background()) 388 if err != nil { 389 t.Fatalf("unexpected error: %v", err) 390 } 391 if networkID.Cmp(big.NewInt(1337)) != 0 { 392 t.Fatalf("unexpected networkID: %v", networkID) 393 } 394 395 // SuggestGasPrice 396 gasPrice, err := ec.SuggestGasPrice(context.Background()) 397 if err != nil { 398 t.Fatalf("unexpected error: %v", err) 399 } 400 if gasPrice.Cmp(big.NewInt(1000000000)) != 0 { 401 t.Fatalf("unexpected gas price: %v", gasPrice) 402 } 403 404 // SuggestGasTipCap 405 gasTipCap, err := ec.SuggestGasTipCap(context.Background()) 406 if err != nil { 407 t.Fatalf("unexpected error: %v", err) 408 } 409 if gasTipCap.Cmp(big.NewInt(234375000)) != 0 { 410 t.Fatalf("unexpected gas tip cap: %v", gasTipCap) 411 } 412 413 // BlobBaseFee 414 blobBaseFee, err := ec.BlobBaseFee(context.Background()) 415 if err != nil { 416 t.Fatalf("unexpected error: %v", err) 417 } 418 if blobBaseFee.Cmp(big.NewInt(1)) != 0 { 419 t.Fatalf("unexpected blob base fee: %v", blobBaseFee) 420 } 421 422 // FeeHistory 423 history, err := ec.FeeHistory(context.Background(), 1, big.NewInt(2), []float64{95, 99}) 424 if err != nil { 425 t.Fatalf("unexpected error: %v", err) 426 } 427 want := ðereum.FeeHistory{ 428 OldestBlock: big.NewInt(2), 429 Reward: [][]*big.Int{ 430 { 431 big.NewInt(234375000), 432 big.NewInt(234375000), 433 }, 434 }, 435 BaseFee: []*big.Int{ 436 big.NewInt(765625000), 437 big.NewInt(671627818), 438 }, 439 GasUsedRatio: []float64{0.008912678667376286}, 440 } 441 if !reflect.DeepEqual(history, want) { 442 t.Fatalf("FeeHistory result doesn't match expected: (got: %v, want: %v)", history, want) 443 } 444 } 445 446 func testCallContractAtHash(t *testing.T, client *rpc.Client) { 447 ec := ethclient.NewClient(client) 448 449 // EstimateGas 450 msg := ethereum.CallMsg{ 451 From: testAddr, 452 To: &common.Address{}, 453 Gas: 21000, 454 Value: big.NewInt(1), 455 } 456 gas, err := ec.EstimateGas(context.Background(), msg) 457 if err != nil { 458 t.Fatalf("unexpected error: %v", err) 459 } 460 if gas != 21000 { 461 t.Fatalf("unexpected gas price: %v", gas) 462 } 463 block, err := ec.HeaderByNumber(context.Background(), big.NewInt(1)) 464 if err != nil { 465 t.Fatalf("BlockByNumber error: %v", err) 466 } 467 // CallContract 468 if _, err := ec.CallContractAtHash(context.Background(), msg, block.Hash()); err != nil { 469 t.Fatalf("unexpected error: %v", err) 470 } 471 } 472 473 func testCallContract(t *testing.T, client *rpc.Client) { 474 ec := ethclient.NewClient(client) 475 476 // EstimateGas 477 msg := ethereum.CallMsg{ 478 From: testAddr, 479 To: &common.Address{}, 480 Gas: 21000, 481 Value: big.NewInt(1), 482 } 483 gas, err := ec.EstimateGas(context.Background(), msg) 484 if err != nil { 485 t.Fatalf("unexpected error: %v", err) 486 } 487 if gas != 21000 { 488 t.Fatalf("unexpected gas price: %v", gas) 489 } 490 // CallContract 491 if _, err := ec.CallContract(context.Background(), msg, big.NewInt(1)); err != nil { 492 t.Fatalf("unexpected error: %v", err) 493 } 494 // PendingCallContract 495 if _, err := ec.PendingCallContract(context.Background(), msg); err != nil { 496 t.Fatalf("unexpected error: %v", err) 497 } 498 } 499 500 func testAtFunctions(t *testing.T, client *rpc.Client) { 501 ec := ethclient.NewClient(client) 502 503 block, err := ec.HeaderByNumber(context.Background(), big.NewInt(1)) 504 if err != nil { 505 t.Fatalf("BlockByNumber error: %v", err) 506 } 507 508 // send a transaction for some interesting pending status 509 // and wait for the transaction to be included in the pending block 510 sendTransaction(ec) 511 512 // wait for the transaction to be included in the pending block 513 for { 514 // Check pending transaction count 515 pending, err := ec.PendingTransactionCount(context.Background()) 516 if err != nil { 517 t.Fatalf("unexpected error: %v", err) 518 } 519 if pending == 1 { 520 break 521 } 522 time.Sleep(100 * time.Millisecond) 523 } 524 525 // Query balance 526 balance, err := ec.BalanceAt(context.Background(), testAddr, nil) 527 if err != nil { 528 t.Fatalf("unexpected error: %v", err) 529 } 530 hashBalance, err := ec.BalanceAtHash(context.Background(), testAddr, block.Hash()) 531 if err != nil { 532 t.Fatalf("unexpected error: %v", err) 533 } 534 if balance.Cmp(hashBalance) == 0 { 535 t.Fatalf("unexpected balance at hash: %v %v", balance, hashBalance) 536 } 537 penBalance, err := ec.PendingBalanceAt(context.Background(), testAddr) 538 if err != nil { 539 t.Fatalf("unexpected error: %v", err) 540 } 541 if balance.Cmp(penBalance) == 0 { 542 t.Fatalf("unexpected balance: %v %v", balance, penBalance) 543 } 544 // NonceAt 545 nonce, err := ec.NonceAt(context.Background(), testAddr, nil) 546 if err != nil { 547 t.Fatalf("unexpected error: %v", err) 548 } 549 hashNonce, err := ec.NonceAtHash(context.Background(), testAddr, block.Hash()) 550 if err != nil { 551 t.Fatalf("unexpected error: %v", err) 552 } 553 if hashNonce == nonce { 554 t.Fatalf("unexpected nonce at hash: %v %v", nonce, hashNonce) 555 } 556 penNonce, err := ec.PendingNonceAt(context.Background(), testAddr) 557 if err != nil { 558 t.Fatalf("unexpected error: %v", err) 559 } 560 if penNonce != nonce+1 { 561 t.Fatalf("unexpected nonce: %v %v", nonce, penNonce) 562 } 563 // StorageAt 564 storage, err := ec.StorageAt(context.Background(), testAddr, common.Hash{}, nil) 565 if err != nil { 566 t.Fatalf("unexpected error: %v", err) 567 } 568 hashStorage, err := ec.StorageAtHash(context.Background(), testAddr, common.Hash{}, block.Hash()) 569 if err != nil { 570 t.Fatalf("unexpected error: %v", err) 571 } 572 if !bytes.Equal(storage, hashStorage) { 573 t.Fatalf("unexpected storage at hash: %v %v", storage, hashStorage) 574 } 575 penStorage, err := ec.PendingStorageAt(context.Background(), testAddr, common.Hash{}) 576 if err != nil { 577 t.Fatalf("unexpected error: %v", err) 578 } 579 if !bytes.Equal(storage, penStorage) { 580 t.Fatalf("unexpected storage: %v %v", storage, penStorage) 581 } 582 // CodeAt 583 code, err := ec.CodeAt(context.Background(), testAddr, nil) 584 if err != nil { 585 t.Fatalf("unexpected error: %v", err) 586 } 587 hashCode, err := ec.CodeAtHash(context.Background(), common.Address{}, block.Hash()) 588 if err != nil { 589 t.Fatalf("unexpected error: %v", err) 590 } 591 if !bytes.Equal(code, hashCode) { 592 t.Fatalf("unexpected code at hash: %v %v", code, hashCode) 593 } 594 penCode, err := ec.PendingCodeAt(context.Background(), testAddr) 595 if err != nil { 596 t.Fatalf("unexpected error: %v", err) 597 } 598 if !bytes.Equal(code, penCode) { 599 t.Fatalf("unexpected code: %v %v", code, penCode) 600 } 601 // Use HeaderByNumber to get a header for EstimateGasAtBlock and EstimateGasAtBlockHash 602 latestHeader, err := ec.HeaderByNumber(context.Background(), nil) 603 if err != nil { 604 t.Fatalf("unexpected error: %v", err) 605 } 606 // EstimateGasAtBlock 607 msg := ethereum.CallMsg{ 608 From: testAddr, 609 To: &common.Address{}, 610 Gas: 21000, 611 Value: big.NewInt(1), 612 } 613 gas, err := ec.EstimateGasAtBlock(context.Background(), msg, latestHeader.Number) 614 if err != nil { 615 t.Fatalf("unexpected error: %v", err) 616 } 617 if gas != 21000 { 618 t.Fatalf("unexpected gas limit: %v", gas) 619 } 620 // EstimateGasAtBlockHash 621 gas, err = ec.EstimateGasAtBlockHash(context.Background(), msg, latestHeader.Hash()) 622 if err != nil { 623 t.Fatalf("unexpected error: %v", err) 624 } 625 if gas != 21000 { 626 t.Fatalf("unexpected gas limit: %v", gas) 627 } 628 629 // Verify that sender address of pending transaction is saved in cache. 630 pendingBlock, err := ec.BlockByNumber(context.Background(), big.NewInt(int64(rpc.PendingBlockNumber))) 631 if err != nil { 632 t.Fatalf("unexpected error: %v", err) 633 } 634 // No additional RPC should be required, ensure the server is not asked by 635 // canceling the context. 636 sender, err := ec.TransactionSender(newCanceledContext(), pendingBlock.Transactions()[0], pendingBlock.Hash(), 0) 637 if err != nil { 638 t.Fatal("unable to recover sender:", err) 639 } 640 if sender != testAddr { 641 t.Fatal("wrong sender:", sender) 642 } 643 } 644 645 func testTransactionSender(t *testing.T, client *rpc.Client) { 646 ec := ethclient.NewClient(client) 647 ctx := context.Background() 648 649 // Retrieve testTx1 via RPC. 650 block2, err := ec.HeaderByNumber(ctx, big.NewInt(2)) 651 if err != nil { 652 t.Fatal("can't get block 1:", err) 653 } 654 tx1, err := ec.TransactionInBlock(ctx, block2.Hash(), 0) 655 if err != nil { 656 t.Fatal("can't get tx:", err) 657 } 658 if tx1.Hash() != testTx1.Hash() { 659 t.Fatalf("wrong tx hash %v, want %v", tx1.Hash(), testTx1.Hash()) 660 } 661 662 // The sender address is cached in tx1, so no additional RPC should be required in 663 // TransactionSender. Ensure the server is not asked by canceling the context here. 664 sender1, err := ec.TransactionSender(newCanceledContext(), tx1, block2.Hash(), 0) 665 if err != nil { 666 t.Fatal(err) 667 } 668 if sender1 != testAddr { 669 t.Fatal("wrong sender:", sender1) 670 } 671 672 // Now try to get the sender of testTx2, which was not fetched through RPC. 673 // TransactionSender should query the server here. 674 sender2, err := ec.TransactionSender(ctx, testTx2, block2.Hash(), 1) 675 if err != nil { 676 t.Fatal(err) 677 } 678 if sender2 != testAddr { 679 t.Fatal("wrong sender:", sender2) 680 } 681 } 682 683 func newCanceledContext() context.Context { 684 ctx, cancel := context.WithCancel(context.Background()) 685 cancel() 686 <-ctx.Done() // Ensure the close of the Done channel 687 return ctx 688 } 689 690 func sendTransaction(ec *ethclient.Client) error { 691 chainID, err := ec.ChainID(context.Background()) 692 if err != nil { 693 return err 694 } 695 nonce, err := ec.NonceAt(context.Background(), testAddr, nil) 696 if err != nil { 697 return err 698 } 699 700 signer := types.LatestSignerForChainID(chainID) 701 tx, err := types.SignNewTx(testKey, signer, &types.LegacyTx{ 702 Nonce: nonce, 703 To: &common.Address{2}, 704 Value: big.NewInt(1), 705 Gas: 22000, 706 GasPrice: big.NewInt(params.InitialBaseFee), 707 }) 708 if err != nil { 709 return err 710 } 711 return ec.SendTransaction(context.Background(), tx) 712 } 713 714 // Here we show how to get the error message of reverted contract call. 715 func ExampleRevertErrorData() { 716 // First create an ethclient.Client instance. 717 ctx := context.Background() 718 ec, _ := ethclient.DialContext(ctx, exampleNode.HTTPEndpoint()) 719 720 // Call the contract. 721 // Note we expect the call to return an error. 722 contract := common.HexToAddress("290f1b36649a61e369c6276f6d29463335b4400c") 723 call := ethereum.CallMsg{To: &contract, Gas: 30000} 724 result, err := ec.CallContract(ctx, call, nil) 725 if len(result) > 0 { 726 panic("got result") 727 } 728 if err == nil { 729 panic("call did not return error") 730 } 731 732 // Extract the low-level revert data from the error. 733 revertData, ok := ethclient.RevertErrorData(err) 734 if !ok { 735 panic("unpacking revert failed") 736 } 737 fmt.Printf("revert: %x\n", revertData) 738 739 // Parse the revert data to obtain the error message. 740 message, err := abi.UnpackRevert(revertData) 741 if err != nil { 742 panic("parsing ABI error failed: " + err.Error()) 743 } 744 fmt.Println("message:", message) 745 746 // Output: 747 // revert: 08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000a75736572206572726f72 748 // message: user error 749 }