Skip to content

Commit ba3e4d6

Browse files
Copilottac0turtle
andcommitted
Enhance EVM rollback test with real integration testing
Co-authored-by: tac0turtle <24299864+tac0turtle@users.noreply.github.com>
1 parent c8c8584 commit ba3e4d6

1 file changed

Lines changed: 173 additions & 57 deletions

File tree

execution/evm/rollback_test.go

Lines changed: 173 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,189 @@
1+
//go:build evm
2+
// +build evm
3+
14
package evm
25

36
import (
47
"context"
58
"testing"
9+
"time"
610

711
"github.com/ethereum/go-ethereum/common"
8-
"github.com/stretchr/testify/assert"
12+
ethTypes "github.com/ethereum/go-ethereum/core/types"
913
"github.com/stretchr/testify/require"
1014
)
1115

12-
func TestEngineClient_Rollback(t *testing.T) {
13-
tests := []struct {
14-
name string
15-
currentHeight uint64
16-
expectError bool
17-
expectedErrorMsg string
18-
}{
19-
{
20-
name: "cannot rollback from height 1",
21-
currentHeight: 1,
22-
expectError: true,
23-
expectedErrorMsg: "cannot rollback from height 1: must be > 1",
24-
},
25-
{
26-
name: "cannot rollback from height 0",
27-
currentHeight: 0,
28-
expectError: true,
29-
expectedErrorMsg: "cannot rollback from height 0: must be > 1",
30-
},
31-
}
16+
// TestEngineClient_RollbackIntegration tests the rollback functionality using a real Reth engine.
17+
// This test builds a chain with real blocks and transactions, then tests rollback to ensure
18+
// it correctly reverts the execution state to the previous block.
19+
func TestEngineClient_RollbackIntegration(t *testing.T) {
20+
// Setup test environment
21+
jwtSecret := SetupTestRethEngine(t, DOCKER_PATH, JWT_FILENAME)
22+
23+
executionClient, err := NewEngineExecutionClient(
24+
TEST_ETH_URL,
25+
TEST_ENGINE_URL,
26+
jwtSecret,
27+
common.HexToHash(GENESIS_HASH),
28+
common.Address{},
29+
)
30+
require.NoError(t, err)
31+
32+
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second)
33+
defer cancel()
34+
35+
// Initialize chain
36+
initialHeight := uint64(1)
37+
genesisTime := time.Now().UTC().Truncate(time.Second)
38+
genesisStateRoot := common.HexToHash(GENESIS_STATEROOT)
39+
rollkitGenesisStateRoot := genesisStateRoot[:]
40+
41+
stateRoot, gasLimit, err := executionClient.InitChain(ctx, genesisTime, initialHeight, CHAIN_ID)
42+
require.NoError(t, err)
43+
require.Equal(t, rollkitGenesisStateRoot, stateRoot)
44+
require.NotZero(t, gasLimit)
45+
46+
// Build chain with multiple blocks
47+
var allStateRoots [][]byte
48+
var lastNonce uint64
49+
prevStateRoot := rollkitGenesisStateRoot
50+
baseTimestamp := time.Now()
51+
52+
// Store genesis state root
53+
allStateRoots = append(allStateRoots, prevStateRoot)
3254

33-
for _, tt := range tests {
34-
t.Run(tt.name, func(t *testing.T) {
35-
// Create a mock EVM client
36-
client := &EngineClient{
37-
genesisHash: common.HexToHash("0x1234"),
38-
feeRecipient: common.HexToAddress("0x5678"),
39-
currentHeadBlockHash: common.HexToHash("0xabcd"),
40-
currentSafeBlockHash: common.HexToHash("0xabcd"),
41-
currentFinalizedBlockHash: common.HexToHash("0x1234"),
42-
}
43-
44-
// Execute rollback
45-
prevStateRoot, err := client.Rollback(context.Background(), tt.currentHeight)
46-
47-
// Verify results
48-
if tt.expectError {
49-
require.Error(t, err)
50-
if tt.expectedErrorMsg != "" {
51-
assert.Contains(t, err.Error(), tt.expectedErrorMsg)
52-
}
53-
assert.Nil(t, prevStateRoot)
54-
} else {
55-
// This case won't be tested without real EVM connection
56-
t.Skip("Requires real EVM connection for success case")
57-
}
58-
})
55+
// Build blocks 1, 2, and 3
56+
for blockHeight := initialHeight; blockHeight <= 3; blockHeight++ {
57+
nTxs := 2 // Use 2 transactions per block for simplicity
58+
59+
// Create and submit transactions
60+
txs := make([]*ethTypes.Transaction, nTxs)
61+
for i := range txs {
62+
txs[i] = GetRandomTransaction(t, TEST_PRIVATE_KEY, TEST_TO_ADDRESS, CHAIN_ID, 22000, &lastNonce)
63+
SubmitTransaction(t, txs[i])
64+
}
65+
66+
// Get payload from mempool
67+
payload, err := executionClient.GetTxs(ctx)
68+
require.NoError(t, err)
69+
require.Len(t, payload, nTxs)
70+
71+
// Execute block
72+
blockTimestamp := baseTimestamp.Add(time.Duration(blockHeight-initialHeight) * time.Second)
73+
newStateRoot, maxBytes, err := executionClient.ExecuteTxs(ctx, payload, blockHeight, blockTimestamp, prevStateRoot)
74+
require.NoError(t, err)
75+
require.NotZero(t, maxBytes)
76+
require.NotEqual(t, prevStateRoot, newStateRoot, "State root should change when transactions are executed")
77+
78+
// Finalize block
79+
err = executionClient.SetFinal(ctx, blockHeight)
80+
require.NoError(t, err)
81+
82+
// Store state root and verify block was processed
83+
allStateRoots = append(allStateRoots, newStateRoot)
84+
lastHeight, lastHash, lastTxs := checkLatestBlock(t, ctx)
85+
require.Equal(t, blockHeight, lastHeight)
86+
require.NotEmpty(t, lastHash.Hex())
87+
require.Equal(t, nTxs, lastTxs)
88+
89+
t.Logf("Built block %d: state root %x", blockHeight, newStateRoot)
90+
prevStateRoot = newStateRoot
5991
}
60-
}
6192

62-
func TestEngineClient_RollbackValidation(t *testing.T) {
63-
client := &EngineClient{}
93+
// Test rollback validation - should fail for invalid heights
94+
t.Run("Rollback validation", func(t *testing.T) {
95+
// Test rollback from height 1 (should fail)
96+
_, err := executionClient.Rollback(ctx, 1)
97+
require.Error(t, err)
98+
require.Contains(t, err.Error(), "cannot rollback from height 1: must be > 1")
99+
100+
// Test rollback from height 0 (should fail)
101+
_, err = executionClient.Rollback(ctx, 0)
102+
require.Error(t, err)
103+
require.Contains(t, err.Error(), "cannot rollback from height 0: must be > 1")
104+
})
105+
106+
// Test successful rollback from height 3 to height 2
107+
t.Run("Successful rollback", func(t *testing.T) {
108+
// Verify we're currently at block 3
109+
currentHeight, currentHash, currentTxs := checkLatestBlock(t, ctx)
110+
require.Equal(t, uint64(3), currentHeight)
111+
require.NotEmpty(t, currentHash.Hex())
112+
require.Equal(t, 2, currentTxs)
113+
114+
// Perform rollback from block 3 to block 2
115+
prevStateRoot, err := executionClient.Rollback(ctx, 3)
116+
require.NoError(t, err)
117+
118+
// Verify rollback returned the correct previous state root (block 2)
119+
expectedPrevStateRoot := allStateRoots[2] // State root of block 2
120+
require.Equal(t, expectedPrevStateRoot, prevStateRoot,
121+
"Rollback should return state root of block 2. Expected: %x, Got: %x",
122+
expectedPrevStateRoot, prevStateRoot)
123+
124+
t.Logf("Successfully performed rollback from block 3 to block 2")
125+
t.Logf("Returned state root: %x", prevStateRoot)
126+
127+
// Test that we can continue building on the rolled-back state
128+
// Create and submit a new transaction
129+
newTx := GetRandomTransaction(t, TEST_PRIVATE_KEY, TEST_TO_ADDRESS, CHAIN_ID, 22000, &lastNonce)
130+
SubmitTransaction(t, newTx)
131+
132+
// Get payload from mempool
133+
payload, err := executionClient.GetTxs(ctx)
134+
require.NoError(t, err)
135+
require.Len(t, payload, 1)
136+
137+
// Execute new block building on the rolled-back state
138+
blockTimestamp := baseTimestamp.Add(4 * time.Second)
139+
newStateRoot, maxBytes, err := executionClient.ExecuteTxs(ctx, payload, 4, blockTimestamp, prevStateRoot)
140+
require.NoError(t, err)
141+
require.NotZero(t, maxBytes)
142+
require.NotEqual(t, prevStateRoot, newStateRoot, "State root should change when building new block")
143+
144+
// Finalize the new block
145+
err = executionClient.SetFinal(ctx, 4)
146+
require.NoError(t, err)
147+
148+
// Verify we can query the new block
149+
finalHeight, finalHash, finalTxs := checkLatestBlock(t, ctx)
150+
require.Equal(t, uint64(4), finalHeight)
151+
require.NotEmpty(t, finalHash.Hex())
152+
require.Equal(t, 1, finalTxs)
153+
154+
t.Logf("Successfully built new block 4 on rolled-back state")
155+
t.Logf("New block hash: %s, state root: %x", finalHash.Hex(), newStateRoot)
156+
})
157+
158+
// Test rollback functionality without checking chain height
159+
t.Run("Rollback functionality verification", func(t *testing.T) {
160+
// Perform rollback from current height to height 3
161+
prevStateRoot, err := executionClient.Rollback(ctx, 4)
162+
require.NoError(t, err)
163+
164+
// The previous state root should be from block 3 (since we're rolling back from block 4)
165+
expectedPrevStateRoot := allStateRoots[3] // State root of block 3
166+
require.Equal(t, expectedPrevStateRoot, prevStateRoot,
167+
"Rollback should return state root of block 3. Expected: %x, Got: %x",
168+
expectedPrevStateRoot, prevStateRoot)
169+
170+
t.Logf("Successfully performed rollback from block 4 to block 3")
171+
t.Logf("Returned state root: %x", prevStateRoot)
172+
173+
// Verify we can continue building on this rolled-back state
174+
newTx := GetRandomTransaction(t, TEST_PRIVATE_KEY, TEST_TO_ADDRESS, CHAIN_ID, 22000, &lastNonce)
175+
SubmitTransaction(t, newTx)
176+
177+
payload, err := executionClient.GetTxs(ctx)
178+
require.NoError(t, err)
179+
require.Len(t, payload, 1)
64180

65-
// Test validation without network calls
66-
_, err := client.Rollback(context.Background(), 1)
67-
require.Error(t, err)
68-
assert.Contains(t, err.Error(), "cannot rollback from height 1: must be > 1")
181+
blockTimestamp := baseTimestamp.Add(5 * time.Second)
182+
newStateRoot, maxBytes, err := executionClient.ExecuteTxs(ctx, payload, 5, blockTimestamp, prevStateRoot)
183+
require.NoError(t, err)
184+
require.NotZero(t, maxBytes)
185+
require.NotEqual(t, prevStateRoot, newStateRoot)
69186

70-
_, err = client.Rollback(context.Background(), 0)
71-
require.Error(t, err)
72-
assert.Contains(t, err.Error(), "cannot rollback from height 0: must be > 1")
187+
t.Logf("Successfully built new block on rolled-back state with state root: %x", newStateRoot)
188+
})
73189
}

0 commit comments

Comments
 (0)