Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions ethclient/simulated/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ import (
var _ bind.ContractBackend = (Client)(nil)

var (
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
testKey2, _ = crypto.HexToECDSA("7ee346e3f7efc685250053bfbafbfc880d58dc6145247053d4fb3cb0f66dfcb2")
testAddr2 = crypto.PubkeyToAddress(testKey2.PublicKey)
)

const callableAbi = "[{\"anonymous\":false,\"inputs\":[],\"name\":\"Called\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
Expand Down
99 changes: 99 additions & 0 deletions ethclient/simulated/rollback_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package simulated

import (
"context"
"crypto/ecdsa"
"math/big"
"testing"
"time"

"github.com/XinFinOrg/XDPoSChain/core/types"
)

// TestTransactionRollbackBehavior tests that calling Rollback on the simulated backend doesn't prevent subsequent
// addition of new transactions
func TestTransactionRollbackBehavior(t *testing.T) {
sim := New(
types.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000000000)},
testAddr2: {Balance: big.NewInt(10000000000000000)},
},
10000000,
)
defer sim.Close()
client := sim.Client()

btx0 := testSendSignedTx(t, testKey, sim)
tx0 := testSendSignedTx(t, testKey2, sim)
tx1 := testSendSignedTx(t, testKey2, sim)

sim.Rollback()

if pendingStateHasTx(client, btx0) || pendingStateHasTx(client, tx0) || pendingStateHasTx(client, tx1) {
t.Fatalf("all transactions were not rolled back")
}
Comment on lines +30 to +34
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rollback assertion here isn’t actually validating rollback: pendingStateHasTx relies on TransactionReceipt, which only exists after a block is committed, so these checks will always be false before Commit() even if the txs are still in the pending block. Consider validating rollback by calling client.TransactionByHash(tx.Hash()) (expect pending=true before rollback, ErrNotFound after rollback), and/or by committing after sending the post-rollback txs and asserting the pre-rollback tx receipts are still ErrNotFound after the commit.

Copilot uses AI. Check for mistakes.

btx2 := testSendSignedTx(t, testKey, sim)
tx2 := testSendSignedTx(t, testKey2, sim)
tx3 := testSendSignedTx(t, testKey2, sim)

sim.Commit()

if !pendingStateHasTx(client, btx2) || !pendingStateHasTx(client, tx2) || !pendingStateHasTx(client, tx3) {
t.Fatalf("all post-rollback transactions were not included")
}
}

// testSendSignedTx sends a signed transaction to the simulated backend.
// It does not commit the block.
func testSendSignedTx(t *testing.T, key *ecdsa.PrivateKey, sim *Backend) *types.Transaction {
t.Helper()
client := sim.Client()
ctx := context.Background()

var (
err error
signedTx *types.Transaction
)
signedTx, err = newTx(sim, key)
if err != nil {
t.Fatalf("failed to create transaction: %v", err)
}

if err = client.SendTransaction(ctx, signedTx); err != nil {
t.Fatalf("failed to send transaction: %v", err)
}

return signedTx
}

// pendingStateHasTx returns true if a given transaction was successfully included as of the latest pending state.
func pendingStateHasTx(client Client, tx *types.Transaction) bool {
ctx := context.Background()

var (
receipt *types.Receipt
err error
)

// Poll for receipt with timeout
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
receipt, err = client.TransactionReceipt(ctx, tx.Hash())
if err == nil && receipt != nil {
break
}
time.Sleep(100 * time.Millisecond)
}
Comment on lines +70 to +87
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pendingStateHasTx is both misleading (it checks mined receipts, not pending state) and potentially slow/flaky: when a receipt is not found it will sleep/poll up to 2s, and this is called multiple times (can add several seconds to this test). In the simulated backend Commit() is synchronous, so after commit you can query the receipt once (no polling), and for pre-commit/pending checks use TransactionByHash instead.

Copilot uses AI. Check for mistakes.

if err != nil {
return false
}
if receipt == nil {
return false
}
if receipt.Status != types.ReceiptStatusSuccessful {
return false
}
return true
}
Loading