1+ //go:build evm
2+ // +build evm
3+
14package evm
25
36import (
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