From 99092ac54f78b923a89c1c446f739260aa3ce200 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Tue, 14 Jan 2025 10:56:08 +0800 Subject: [PATCH] accounts/abi/bind/backends: add TransactionByHash to SimulatedBackend (#19026) --- accounts/abi/bind/backends/simulated.go | 34 +++++++--- accounts/abi/bind/backends/simulated_test.go | 70 +++++++++++++++++--- 2 files changed, 87 insertions(+), 17 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 15d7edc7e1..b78262d213 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -26,7 +26,7 @@ import ( "sync" "time" - "github.com/XinFinOrg/XDPoSChain" + ethereum "github.com/XinFinOrg/XDPoSChain" "github.com/XinFinOrg/XDPoSChain/XDCx" "github.com/XinFinOrg/XDPoSChain/XDCxlending" "github.com/XinFinOrg/XDPoSChain/accounts" @@ -265,6 +265,24 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common return receipt, nil } +// TransactionByHash checks the pool of pending transactions in addition to the +// blockchain. The isPending return value indicates whether the transaction has been +// mined yet. Note that the transaction may not be part of the canonical chain even if +// it's not pending. +func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) { + tx = b.pendingBlock.Transaction(txHash) + if tx != nil { + return tx, true, nil + } + + tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash) + if tx != nil { + return tx, false, nil + } + + return nil, false, ethereum.ErrNotFound +} + // HeaderByNumber returns a block header from the current canonical chain. If number is // nil, the latest known header is returned. func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) { @@ -287,7 +305,7 @@ func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Ad } // CallContract executes a contract call. -func (b *SimulatedBackend) CallContract(ctx context.Context, call XDPoSChain.CallMsg, blockNumber *big.Int) ([]byte, error) { +func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { b.mu.Lock() defer b.mu.Unlock() @@ -306,7 +324,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call XDPoSChain.Cal } // PendingCallContract executes a contract call on the pending state. -func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call XDPoSChain.CallMsg) ([]byte, error) { +func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { b.mu.Lock() defer b.mu.Unlock() defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) @@ -344,7 +362,7 @@ func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, erro // EstimateGas executes the requested code against the currently pending block/state and // returns the used amount of gas. -func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.CallMsg) (uint64, error) { +func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { b.mu.Lock() defer b.mu.Unlock() @@ -422,7 +440,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call XDPoSChain.Call // callContract implements common code between normal and pending contract calls. // state is modified during execution, make sure to copy it if necessary. -func (b *SimulatedBackend) callContract(ctx context.Context, call XDPoSChain.CallMsg, block *types.Block, statedb *state.StateDB) (*core.ExecutionResult, error) { +func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) (*core.ExecutionResult, error) { // Gas prices post 1559 need to be initialized if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) { return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified") @@ -523,7 +541,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa // returning all the results in one batch. // // TODO(karalabe): Deprecate when the subscription one can return past data too. -func (b *SimulatedBackend) FilterLogs(ctx context.Context, query XDPoSChain.FilterQuery) ([]types.Log, error) { +func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { var filter *filters.Filter if query.BlockHash != nil { // Block filter requested, construct a single-shot filter @@ -555,7 +573,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query XDPoSChain.Filt // SubscribeFilterLogs creates a background log filtering operation, returning a // subscription immediately, which can be used to stream the found events. -func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query XDPoSChain.FilterQuery, ch chan<- types.Log) (XDPoSChain.Subscription, error) { +func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { // Subscribe to contract events sink := make(chan []*types.Log) @@ -612,7 +630,7 @@ func (b *SimulatedBackend) GetBlockChain() *core.BlockChain { // callMsg implements core.Message to allow passing it as a transaction simulator. type callMsg struct { - XDPoSChain.CallMsg + ethereum.CallMsg } func (m callMsg) From() common.Address { return m.CallMsg.From } diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go index 5681898523..8075552dc1 100644 --- a/accounts/abi/bind/backends/simulated_test.go +++ b/accounts/abi/bind/backends/simulated_test.go @@ -23,15 +23,67 @@ import ( "strings" "testing" - "github.com/XinFinOrg/XDPoSChain" + ethereum "github.com/XinFinOrg/XDPoSChain" "github.com/XinFinOrg/XDPoSChain/accounts/abi" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core" + "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/params" ) +func TestSimulatedBackend(t *testing.T) { + var gasLimit uint64 = 8000029 + key, _ := crypto.GenerateKey() // nolint: gosec + auth := bind.NewKeyedTransactor(key) + genAlloc := make(core.GenesisAlloc) + genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)} + + sim := NewSimulatedBackend(genAlloc, gasLimit) + + // should return an error if the tx is not found + txHash := common.HexToHash("2") + _, isPending, err := sim.TransactionByHash(context.Background(), txHash) + + if isPending { + t.Fatal("transaction should not be pending") + } + if err != ethereum.ErrNotFound { + t.Fatalf("err should be `ethereum.ErrNotFound` but received %v", err) + } + + // generate a transaction and confirm you can retrieve it + code := `6060604052600a8060106000396000f360606040526008565b00` + var gas uint64 = 3000000 + tx := types.NewContractCreation(0, big.NewInt(0), gas, big.NewInt(1), common.FromHex(code)) + tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key) + + err = sim.SendTransaction(context.Background(), tx) + if err != nil { + t.Fatal("error sending transaction") + } + + txHash = tx.Hash() + _, isPending, err = sim.TransactionByHash(context.Background(), txHash) + if err != nil { + t.Fatalf("error getting transaction with hash: %v", txHash.String()) + } + if !isPending { + t.Fatal("transaction should have pending status") + } + + sim.Commit() + tx, isPending, err = sim.TransactionByHash(context.Background(), txHash) + if err != nil { + t.Fatalf("error getting transaction with hash: %v", txHash.String()) + } + if isPending { + t.Fatal("transaction should not have pending status") + } + +} + func TestSimulatedBackend_EstimateGas(t *testing.T) { /* pragma solidity ^0.6.4; @@ -69,11 +121,11 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) { var cases = []struct { name string - message XDPoSChain.CallMsg + message ethereum.CallMsg expect uint64 expectError error }{ - {"plain transfer(valid)", XDPoSChain.CallMsg{ + {"plain transfer(valid)", ethereum.CallMsg{ From: addr, To: &addr, Gas: 0, @@ -82,7 +134,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) { Data: nil, }, params.TxGas, nil}, - {"plain transfer(invalid)", XDPoSChain.CallMsg{ + {"plain transfer(invalid)", ethereum.CallMsg{ From: addr, To: &contractAddr, Gas: 0, @@ -91,7 +143,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) { Data: nil, }, 0, errors.New("always failing transaction (execution reverted)")}, - {"Revert", XDPoSChain.CallMsg{ + {"Revert", ethereum.CallMsg{ From: addr, To: &contractAddr, Gas: 0, @@ -100,7 +152,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) { Data: common.Hex2Bytes("d8b98391"), }, 0, errors.New("always failing transaction (execution reverted) (revert reason)")}, - {"PureRevert", XDPoSChain.CallMsg{ + {"PureRevert", ethereum.CallMsg{ From: addr, To: &contractAddr, Gas: 0, @@ -109,7 +161,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) { Data: common.Hex2Bytes("aa8b1d30"), }, 0, errors.New("always failing transaction (execution reverted)")}, - {"OOG", XDPoSChain.CallMsg{ + {"OOG", ethereum.CallMsg{ From: addr, To: &contractAddr, Gas: 100000, @@ -118,7 +170,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) { Data: common.Hex2Bytes("50f6fe34"), }, 0, errors.New("gas required exceeds allowance (100000)")}, - {"Assert", XDPoSChain.CallMsg{ + {"Assert", ethereum.CallMsg{ From: addr, To: &contractAddr, Gas: 100000, @@ -127,7 +179,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) { Data: common.Hex2Bytes("b9b046f9"), }, 0, errors.New("always failing transaction (invalid opcode: INVALID)")}, - {"Valid", XDPoSChain.CallMsg{ + {"Valid", ethereum.CallMsg{ From: addr, To: &contractAddr, Gas: 100000,