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

     1  // Copyright 2025 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  	"math/big"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/ethereum/go-ethereum/core/types"
    27  )
    28  
    29  // TestTransactionRollbackBehavior tests that calling Rollback on the simulated backend doesn't prevent subsequent
    30  // addition of new transactions
    31  func TestTransactionRollbackBehavior(t *testing.T) {
    32  	sim := NewBackend(
    33  		types.GenesisAlloc{
    34  			testAddr:  {Balance: big.NewInt(10000000000000000)},
    35  			testAddr2: {Balance: big.NewInt(10000000000000000)},
    36  		},
    37  	)
    38  	defer sim.Close()
    39  	client := sim.Client()
    40  
    41  	btx0 := testSendSignedTx(t, testKey, sim, true)
    42  	tx0 := testSendSignedTx(t, testKey2, sim, false)
    43  	tx1 := testSendSignedTx(t, testKey2, sim, false)
    44  
    45  	sim.Rollback()
    46  
    47  	if pendingStateHasTx(client, btx0) || pendingStateHasTx(client, tx0) || pendingStateHasTx(client, tx1) {
    48  		t.Fatalf("all transactions were not rolled back")
    49  	}
    50  
    51  	btx2 := testSendSignedTx(t, testKey, sim, true)
    52  	tx2 := testSendSignedTx(t, testKey2, sim, false)
    53  	tx3 := testSendSignedTx(t, testKey2, sim, false)
    54  
    55  	sim.Commit()
    56  
    57  	if !pendingStateHasTx(client, btx2) || !pendingStateHasTx(client, tx2) || !pendingStateHasTx(client, tx3) {
    58  		t.Fatalf("all post-rollback transactions were not included")
    59  	}
    60  }
    61  
    62  // testSendSignedTx sends a signed transaction to the simulated backend.
    63  // It does not commit the block.
    64  func testSendSignedTx(t *testing.T, key *ecdsa.PrivateKey, sim *Backend, isBlobTx bool) *types.Transaction {
    65  	t.Helper()
    66  	client := sim.Client()
    67  	ctx := context.Background()
    68  
    69  	var (
    70  		err      error
    71  		signedTx *types.Transaction
    72  	)
    73  	if isBlobTx {
    74  		signedTx, err = newBlobTx(sim, key)
    75  	} else {
    76  		signedTx, err = newTx(sim, key)
    77  	}
    78  	if err != nil {
    79  		t.Fatalf("failed to create transaction: %v", err)
    80  	}
    81  
    82  	if err = client.SendTransaction(ctx, signedTx); err != nil {
    83  		t.Fatalf("failed to send transaction: %v", err)
    84  	}
    85  
    86  	return signedTx
    87  }
    88  
    89  // pendingStateHasTx returns true if a given transaction was successfully included as of the latest pending state.
    90  func pendingStateHasTx(client Client, tx *types.Transaction) bool {
    91  	ctx := context.Background()
    92  
    93  	var (
    94  		receipt *types.Receipt
    95  		err     error
    96  	)
    97  
    98  	// Poll for receipt with timeout
    99  	deadline := time.Now().Add(2 * time.Second)
   100  	for time.Now().Before(deadline) {
   101  		receipt, err = client.TransactionReceipt(ctx, tx.Hash())
   102  		if err == nil && receipt != nil {
   103  			break
   104  		}
   105  		time.Sleep(100 * time.Millisecond)
   106  	}
   107  
   108  	if err != nil {
   109  		return false
   110  	}
   111  	if receipt == nil {
   112  		return false
   113  	}
   114  	if receipt.Status != types.ReceiptStatusSuccessful {
   115  		return false
   116  	}
   117  	return true
   118  }