github.com/ethereum/go-ethereum@v1.16.1/ethclient/simulated/backend_test.go (about)

     1  // Copyright 2024 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 simulated
    18  
    19  import (
    20  	"context"
    21  	"crypto/ecdsa"
    22  	"crypto/sha256"
    23  	"math/big"
    24  	"math/rand"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    29  	"github.com/ethereum/go-ethereum/common"
    30  	"github.com/ethereum/go-ethereum/core/types"
    31  	"github.com/ethereum/go-ethereum/crypto"
    32  	"github.com/ethereum/go-ethereum/crypto/kzg4844"
    33  	"github.com/ethereum/go-ethereum/params"
    34  	"github.com/holiman/uint256"
    35  	"go.uber.org/goleak"
    36  )
    37  
    38  var _ bind.ContractBackend = (Client)(nil)
    39  
    40  var (
    41  	testKey, _  = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    42  	testAddr    = crypto.PubkeyToAddress(testKey.PublicKey)
    43  	testKey2, _ = crypto.HexToECDSA("7ee346e3f7efc685250053bfbafbfc880d58dc6145247053d4fb3cb0f66dfcb2")
    44  	testAddr2   = crypto.PubkeyToAddress(testKey2.PublicKey)
    45  )
    46  
    47  func simTestBackend(testAddr common.Address) *Backend {
    48  	return NewBackend(
    49  		types.GenesisAlloc{
    50  			testAddr: {Balance: big.NewInt(10000000000000000)},
    51  		},
    52  	)
    53  }
    54  
    55  func newBlobTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) {
    56  	client := sim.Client()
    57  
    58  	testBlob := &kzg4844.Blob{0x00}
    59  	testBlobCommit, _ := kzg4844.BlobToCommitment(testBlob)
    60  	testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit)
    61  	testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit)
    62  
    63  	head, _ := client.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
    64  	gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei))
    65  	gasPriceU256, _ := uint256.FromBig(gasPrice)
    66  	gasTipCapU256, _ := uint256.FromBig(big.NewInt(params.GWei))
    67  
    68  	addr := crypto.PubkeyToAddress(key.PublicKey)
    69  	chainid, _ := client.ChainID(context.Background())
    70  	nonce, err := client.PendingNonceAt(context.Background(), addr)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	chainidU256, _ := uint256.FromBig(chainid)
    76  	tx := types.NewTx(&types.BlobTx{
    77  		ChainID:    chainidU256,
    78  		GasTipCap:  gasTipCapU256,
    79  		GasFeeCap:  gasPriceU256,
    80  		BlobFeeCap: uint256.NewInt(1),
    81  		Gas:        21000,
    82  		Nonce:      nonce,
    83  		To:         addr,
    84  		AccessList: nil,
    85  		BlobHashes: []common.Hash{testBlobVHash},
    86  		Sidecar: &types.BlobTxSidecar{
    87  			Blobs:       []kzg4844.Blob{*testBlob},
    88  			Commitments: []kzg4844.Commitment{testBlobCommit},
    89  			Proofs:      []kzg4844.Proof{testBlobProof},
    90  		},
    91  	})
    92  	return types.SignTx(tx, types.LatestSignerForChainID(chainid), key)
    93  }
    94  
    95  func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) {
    96  	client := sim.Client()
    97  
    98  	// create a signed transaction to send
    99  	head, _ := client.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
   100  	gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei))
   101  	addr := crypto.PubkeyToAddress(key.PublicKey)
   102  	chainid, _ := client.ChainID(context.Background())
   103  	nonce, err := client.PendingNonceAt(context.Background(), addr)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	tx := types.NewTx(&types.DynamicFeeTx{
   108  		ChainID:   chainid,
   109  		Nonce:     nonce,
   110  		GasTipCap: big.NewInt(params.GWei),
   111  		GasFeeCap: gasPrice,
   112  		Gas:       21000,
   113  		To:        &addr,
   114  	})
   115  
   116  	return types.SignTx(tx, types.LatestSignerForChainID(chainid), key)
   117  }
   118  
   119  func TestNewBackend(t *testing.T) {
   120  	sim := NewBackend(types.GenesisAlloc{})
   121  	defer sim.Close()
   122  
   123  	client := sim.Client()
   124  	num, err := client.BlockNumber(context.Background())
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  	if num != 0 {
   129  		t.Fatalf("expected 0 got %v", num)
   130  	}
   131  	// Create a block
   132  	sim.Commit()
   133  	num, err = client.BlockNumber(context.Background())
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  	if num != 1 {
   138  		t.Fatalf("expected 1 got %v", num)
   139  	}
   140  }
   141  
   142  func TestAdjustTime(t *testing.T) {
   143  	sim := NewBackend(types.GenesisAlloc{})
   144  	defer sim.Close()
   145  
   146  	client := sim.Client()
   147  	block1, _ := client.BlockByNumber(context.Background(), nil)
   148  
   149  	// Create a block
   150  	if err := sim.AdjustTime(time.Minute); err != nil {
   151  		t.Fatal(err)
   152  	}
   153  	block2, _ := client.BlockByNumber(context.Background(), nil)
   154  	prevTime := block1.Time()
   155  	newTime := block2.Time()
   156  	if newTime-prevTime != 60 {
   157  		t.Errorf("adjusted time not equal to 60 seconds. prev: %v, new: %v", prevTime, newTime)
   158  	}
   159  }
   160  
   161  func TestSendTransaction(t *testing.T) {
   162  	sim := simTestBackend(testAddr)
   163  	defer sim.Close()
   164  
   165  	client := sim.Client()
   166  	ctx := context.Background()
   167  
   168  	signedTx, err := newTx(sim, testKey)
   169  	if err != nil {
   170  		t.Errorf("could not create transaction: %v", err)
   171  	}
   172  	// send tx to simulated backend
   173  	err = client.SendTransaction(ctx, signedTx)
   174  	if err != nil {
   175  		t.Errorf("could not add tx to pending block: %v", err)
   176  	}
   177  	sim.Commit()
   178  	block, err := client.BlockByNumber(ctx, big.NewInt(1))
   179  	if err != nil {
   180  		t.Errorf("could not get block at height 1: %v", err)
   181  	}
   182  
   183  	if signedTx.Hash() != block.Transactions()[0].Hash() {
   184  		t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash())
   185  	}
   186  }
   187  
   188  // TestFork check that the chain length after a reorg is correct.
   189  // Steps:
   190  //  1. Save the current block which will serve as parent for the fork.
   191  //  2. Mine n blocks with n ∈ [0, 20].
   192  //  3. Assert that the chain length is n.
   193  //  4. Fork by using the parent block as ancestor.
   194  //  5. Mine n+1 blocks which should trigger a reorg.
   195  //  6. Assert that the chain length is n+1.
   196  //     Since Commit() was called 2n+1 times in total,
   197  //     having a chain length of just n+1 means that a reorg occurred.
   198  func TestFork(t *testing.T) {
   199  	t.Parallel()
   200  	testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
   201  	sim := simTestBackend(testAddr)
   202  	defer sim.Close()
   203  
   204  	client := sim.Client()
   205  	ctx := context.Background()
   206  
   207  	// 1.
   208  	parent, _ := client.HeaderByNumber(ctx, nil)
   209  
   210  	// 2.
   211  	n := int(rand.Int31n(21))
   212  	for i := 0; i < n; i++ {
   213  		sim.Commit()
   214  	}
   215  
   216  	// 3.
   217  	b, _ := client.BlockNumber(ctx)
   218  	if b != uint64(n) {
   219  		t.Error("wrong chain length")
   220  	}
   221  
   222  	// 4.
   223  	sim.Fork(parent.Hash())
   224  
   225  	// 5.
   226  	for i := 0; i < n+1; i++ {
   227  		sim.Commit()
   228  	}
   229  
   230  	// 6.
   231  	b, _ = client.BlockNumber(ctx)
   232  	if b != uint64(n+1) {
   233  		t.Error("wrong chain length")
   234  	}
   235  }
   236  
   237  // TestForkResendTx checks that re-sending a TX after a fork
   238  // is possible and does not cause a "nonce mismatch" panic.
   239  // Steps:
   240  //  1. Save the current block which will serve as parent for the fork.
   241  //  2. Send a transaction.
   242  //  3. Check that the TX is included in block 1.
   243  //  4. Fork by using the parent block as ancestor.
   244  //  5. Mine a block. We expect the out-forked tx to have trickled to the pool, and into the new block.
   245  //  6. Check that the TX is now included in (the new) block 1.
   246  func TestForkResendTx(t *testing.T) {
   247  	t.Parallel()
   248  	testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
   249  	sim := simTestBackend(testAddr)
   250  	defer sim.Close()
   251  
   252  	client := sim.Client()
   253  	ctx := context.Background()
   254  
   255  	// 1.
   256  	parent, _ := client.HeaderByNumber(ctx, nil)
   257  
   258  	// 2.
   259  	tx, err := newTx(sim, testKey)
   260  	if err != nil {
   261  		t.Fatalf("could not create transaction: %v", err)
   262  	}
   263  	if err := client.SendTransaction(ctx, tx); err != nil {
   264  		t.Fatalf("sending transaction: %v", err)
   265  	}
   266  	sim.Commit()
   267  
   268  	// 3.
   269  	receipt, _ := client.TransactionReceipt(ctx, tx.Hash())
   270  	if h := receipt.BlockNumber.Uint64(); h != 1 {
   271  		t.Errorf("TX included in wrong block: %d", h)
   272  	}
   273  
   274  	// 4.
   275  	if err := sim.Fork(parent.Hash()); err != nil {
   276  		t.Errorf("forking: %v", err)
   277  	}
   278  
   279  	// 5.
   280  	sim.Commit()
   281  	receipt, _ = client.TransactionReceipt(ctx, tx.Hash())
   282  	if h := receipt.BlockNumber.Uint64(); h != 1 {
   283  		t.Errorf("TX included in wrong block: %d", h)
   284  	}
   285  }
   286  
   287  func TestCommitReturnValue(t *testing.T) {
   288  	t.Parallel()
   289  	testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
   290  	sim := simTestBackend(testAddr)
   291  	defer sim.Close()
   292  
   293  	client := sim.Client()
   294  	ctx := context.Background()
   295  
   296  	// Test if Commit returns the correct block hash
   297  	h1 := sim.Commit()
   298  	cur, _ := client.HeaderByNumber(ctx, nil)
   299  	if h1 != cur.Hash() {
   300  		t.Error("Commit did not return the hash of the last block.")
   301  	}
   302  
   303  	// Create a block in the original chain (containing a transaction to force different block hashes)
   304  	tx, _ := newTx(sim, testKey)
   305  	if err := client.SendTransaction(ctx, tx); err != nil {
   306  		t.Errorf("sending transaction: %v", err)
   307  	}
   308  
   309  	h2 := sim.Commit()
   310  
   311  	// Create another block in the original chain
   312  	sim.Commit()
   313  
   314  	// Fork at the first bock
   315  	if err := sim.Fork(h1); err != nil {
   316  		t.Errorf("forking: %v", err)
   317  	}
   318  
   319  	// Test if Commit returns the correct block hash after the reorg
   320  	h2fork := sim.Commit()
   321  	if h2 == h2fork {
   322  		t.Error("The block in the fork and the original block are the same block!")
   323  	}
   324  	if header, err := client.HeaderByHash(ctx, h2fork); err != nil || header == nil {
   325  		t.Error("Could not retrieve the just created block (side-chain)")
   326  	}
   327  }
   328  
   329  // TestAdjustTimeAfterFork ensures that after a fork, AdjustTime uses the pending fork
   330  // block's parent rather than the canonical head's parent.
   331  func TestAdjustTimeAfterFork(t *testing.T) {
   332  	t.Parallel()
   333  	testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
   334  	sim := simTestBackend(testAddr)
   335  	defer sim.Close()
   336  
   337  	client := sim.Client()
   338  	ctx := context.Background()
   339  
   340  	sim.Commit() // h1
   341  	h1, _ := client.HeaderByNumber(ctx, nil)
   342  
   343  	sim.Commit() // h2
   344  	sim.Fork(h1.Hash())
   345  	sim.AdjustTime(1 * time.Second)
   346  	sim.Commit()
   347  
   348  	head, _ := client.HeaderByNumber(ctx, nil)
   349  	if head.Number.Uint64() == 2 && head.ParentHash != h1.Hash() {
   350  		t.Errorf("failed to build block on fork")
   351  	}
   352  }
   353  
   354  func createAndCloseSimBackend() {
   355  	genesisData := types.GenesisAlloc{}
   356  	simulatedBackend := NewBackend(genesisData)
   357  	defer simulatedBackend.Close()
   358  }
   359  
   360  // TestCheckSimBackendGoroutineLeak checks whether creation of a simulated backend leaks go-routines.  Any long-lived go-routines
   361  // spawned by global variables are not considered leaked.
   362  func TestCheckSimBackendGoroutineLeak(t *testing.T) {
   363  	createAndCloseSimBackend()
   364  	ignoreCur := goleak.IgnoreCurrent()
   365  	// ignore this leveldb function:  this go-routine is guaranteed to be terminated 1 second after closing db handle
   366  	ignoreLdb := goleak.IgnoreAnyFunction("github.com/syndtr/goleveldb/leveldb.(*DB).mpoolDrain")
   367  	createAndCloseSimBackend()
   368  	goleak.VerifyNone(t, ignoreCur, ignoreLdb)
   369  }