Repository navigation
test: Add comprehensive EVM E2E tests #2394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
fa4eee2
bd71544
eed64f6
065b7ef
ca15034
5091f89
c350346
9ed2542
9d20daf
303c580
c4bca3f
81afb2d
f9855c8
7b7be7e
9ee6b46
3b2827c
fd7ab38
c674f5f
e15477d
bea5d7e
86f9f69
23ee053
1df4ebc
2e76463
5171bed
0c8d343
d6aee4b
badfe37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
This reverts commit 1ca8457.
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,8 +147,6 @@ type Manager struct { | |
| metrics *Metrics | ||
|
|
||
| exec coreexecutor.Executor | ||
| // executionMode caches the execution mode from the executor | ||
| executionMode coreexecutor.ExecutionMode | ||
|
|
||
| // daIncludedHeight is rollkit height at which all blocks have been included | ||
| // in the DA | ||
|
|
@@ -353,8 +351,6 @@ func NewManager( | |
| daH := atomic.Uint64{} | ||
| daH.Store(s.DAHeight) | ||
|
|
||
| executionMode := exec.GetExecutionMode() | ||
|
|
||
| m := &Manager{ | ||
| signer: signer, | ||
| config: config, | ||
|
|
@@ -384,7 +380,6 @@ func NewManager( | |
| metrics: seqMetrics, | ||
| sequencer: sequencer, | ||
| exec: exec, | ||
| executionMode: executionMode, | ||
| da: da, | ||
| gasPrice: gasPrice, | ||
| gasMultiplier: gasMultiplier, | ||
|
|
@@ -805,26 +800,22 @@ func (m *Manager) execValidate(lastState types.State, header *types.SignedHeader | |
| } | ||
|
|
||
| // // Verify that the header's timestamp is strictly greater than the last block's time | ||
| headerTime := header.Time() | ||
| if header.Height() > 1 && lastState.LastBlockTime.After(headerTime) { | ||
| return fmt.Errorf("block time must be strictly increasing: got %v, last block time was %v", | ||
| headerTime.UnixNano(), lastState.LastBlockTime) | ||
| } | ||
|
|
||
| // Validate AppHash based on execution mode | ||
| if m.executionMode == coreexecutor.ExecutionModeDelayed { | ||
| // In delayed mode, AppHash should match the last state's AppHash | ||
| if !bytes.Equal(header.AppHash, lastState.AppHash) { | ||
| return fmt.Errorf("appHash mismatch in delayed mode: expected %x, got %x", lastState.AppHash, header.AppHash) | ||
| } | ||
| } | ||
| // Note: For immediate mode, we can't validate AppHash against lastState because | ||
| // it represents the state after executing the current block's transactions | ||
| // headerTime := header.Time() | ||
| // if header.Height() > 1 && lastState.LastBlockTime.After(headerTime) { | ||
| // return fmt.Errorf("block time must be strictly increasing: got %v, last block time was %v", | ||
| // headerTime.UnixNano(), lastState.LastBlockTime) | ||
| // } | ||
|
|
||
| // // Validate that the header's AppHash matches the lastState's AppHash | ||
| // // Note: Assumes deferred execution | ||
| // if !bytes.Equal(header.AppHash, lastState.AppHash) { | ||
| // return fmt.Errorf("app hash mismatch: expected %x, got %x", lastState.AppHash, header.AppHash) | ||
| // } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (m *Manager) execCreateBlock(ctx context.Context, height uint64, lastSignature *types.Signature, lastHeaderHash types.Hash, lastState types.State, batchData *BatchData) (*types.SignedHeader, *types.Data, error) { | ||
| func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignature *types.Signature, lastHeaderHash types.Hash, _ types.State, batchData *BatchData) (*types.SignedHeader, *types.Data, error) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential race condition: Use the passed lastState parameter instead of m.lastState. The function ignores the Either:
-func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignature *types.Signature, lastHeaderHash types.Hash, _ types.State, batchData *BatchData) (*types.SignedHeader, *types.Data, error) {
+func (m *Manager) execCreateBlock(_ context.Context, height uint64, lastSignature *types.Signature, lastHeaderHash types.Hash, lastState types.State, batchData *BatchData) (*types.SignedHeader, *types.Data, error) {
// ...
header := &types.SignedHeader{
Header: types.Header{
Version: types.Version{
- Block: m.lastState.Version.Block,
- App: m.lastState.Version.App,
+ Block: lastState.Version.Block,
+ App: lastState.Version.App,
},
BaseHeader: types.BaseHeader{
- ChainID: m.lastState.ChainID,
+ ChainID: lastState.ChainID,
Height: height,
Time: uint64(batchData.UnixNano()),
},
LastHeaderHash: lastHeaderHash,
ConsensusHash: make(types.Hash, 32),
- AppHash: m.lastState.AppHash,
+ AppHash: lastState.AppHash,
ProposerAddress: m.genesis.ProposerAddress,
},Also applies to: 846-857 🤖 Prompt for AI Agents |
||
| // Use when batchData is set to data IDs from the DA layer | ||
| // batchDataIDs := convertBatchDataToBytes(batchData.Data) | ||
|
|
||
|
|
@@ -849,54 +840,21 @@ func (m *Manager) execCreateBlock(ctx context.Context, height uint64, lastSignat | |
| // Determine if this is an empty block | ||
| isEmpty := batchData.Batch == nil || len(batchData.Transactions) == 0 | ||
|
|
||
| // Create block data with appropriate transactions | ||
| blockData := &types.Data{ | ||
| Txs: make(types.Txs, 0), // Start with empty transaction list | ||
| } | ||
|
|
||
| // Only add transactions if this is not an empty block | ||
| if !isEmpty { | ||
| blockData.Txs = make(types.Txs, len(batchData.Transactions)) | ||
| for i := range batchData.Transactions { | ||
| blockData.Txs[i] = types.Tx(batchData.Transactions[i]) | ||
| } | ||
| } | ||
|
|
||
| // Determine AppHash based on execution mode | ||
| var appHash []byte | ||
| if m.executionMode == coreexecutor.ExecutionModeImmediate && !isEmpty { | ||
| // For immediate execution, execute transactions now to get the new state root | ||
| rawTxs := make([][]byte, len(blockData.Txs)) | ||
| for i := range blockData.Txs { | ||
| rawTxs[i] = blockData.Txs[i] | ||
| } | ||
|
|
||
| // Execute transactions | ||
| newStateRoot, _, err := m.exec.ExecuteTxs(ctx, rawTxs, height, batchData.Time, lastState.AppHash) | ||
| if err != nil { | ||
| return nil, nil, fmt.Errorf("failed to execute transactions for immediate mode: %w", err) | ||
| } | ||
| appHash = newStateRoot | ||
| } else { | ||
| // For delayed execution or empty blocks, use the app hash from last state | ||
| appHash = lastState.AppHash | ||
| } | ||
|
|
||
| header := &types.SignedHeader{ | ||
| Header: types.Header{ | ||
| Version: types.Version{ | ||
| Block: lastState.Version.Block, | ||
| App: lastState.Version.App, | ||
| Block: m.lastState.Version.Block, | ||
| App: m.lastState.Version.App, | ||
| }, | ||
| BaseHeader: types.BaseHeader{ | ||
| ChainID: lastState.ChainID, | ||
| ChainID: m.lastState.ChainID, | ||
| Height: height, | ||
| Time: uint64(batchData.UnixNano()), //nolint:gosec // why is time unix? (tac0turtle) | ||
| }, | ||
| LastHeaderHash: lastHeaderHash, | ||
| // DataHash is set below | ||
| // DataHash is set at the end of the function | ||
| ConsensusHash: make(types.Hash, 32), | ||
| AppHash: appHash, | ||
| AppHash: m.lastState.AppHash, | ||
| ProposerAddress: m.genesis.ProposerAddress, | ||
| }, | ||
| Signature: *lastSignature, | ||
|
|
@@ -906,8 +864,17 @@ func (m *Manager) execCreateBlock(ctx context.Context, height uint64, lastSignat | |
| }, | ||
| } | ||
|
|
||
| // Set DataHash | ||
| // Create block data with appropriate transactions | ||
| blockData := &types.Data{ | ||
| Txs: make(types.Txs, 0), // Start with empty transaction list | ||
| } | ||
|
|
||
| // Only add transactions if this is not an empty block | ||
| if !isEmpty { | ||
| blockData.Txs = make(types.Txs, len(batchData.Transactions)) | ||
| for i := range batchData.Transactions { | ||
| blockData.Txs[i] = types.Tx(batchData.Transactions[i]) | ||
| } | ||
| header.DataHash = blockData.DACommitment() | ||
| } else { | ||
| header.DataHash = dataHashForEmptyTxs | ||
|
|
@@ -917,61 +884,15 @@ func (m *Manager) execCreateBlock(ctx context.Context, height uint64, lastSignat | |
| } | ||
|
|
||
| func (m *Manager) execApplyBlock(ctx context.Context, lastState types.State, header *types.SignedHeader, data *types.Data) (types.State, error) { | ||
| var newStateRoot []byte | ||
|
|
||
| // Check if we need to execute transactions | ||
| if m.executionMode == coreexecutor.ExecutionModeImmediate { | ||
| // For immediate mode, the aggregator already executed during block creation | ||
| // But syncing nodes need to execute to verify the AppHash | ||
| // Check if we're the block creator by verifying we signed this block | ||
| isBlockCreator := false | ||
| if m.signer != nil { | ||
| myAddress, err := m.signer.GetAddress() | ||
| if err == nil && bytes.Equal(header.ProposerAddress, myAddress) { | ||
| // Also verify this is the current height we're producing | ||
| currentHeight, err := m.store.Height(ctx) | ||
| if err == nil && header.Height() == currentHeight+1 { | ||
| isBlockCreator = true | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if isBlockCreator { | ||
| // We created this block, so we already executed transactions in execCreateBlock | ||
| // Just use the AppHash from the header | ||
| newStateRoot = header.AppHash | ||
| } else { | ||
| // We're syncing this block, need to execute to verify AppHash | ||
| rawTxs := make([][]byte, len(data.Txs)) | ||
| for i := range data.Txs { | ||
| rawTxs[i] = data.Txs[i] | ||
| } | ||
|
|
||
| ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header) | ||
| computedStateRoot, _, err := m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash) | ||
| if err != nil { | ||
| return types.State{}, fmt.Errorf("failed to execute transactions: %w", err) | ||
| } | ||
|
|
||
| // Verify the AppHash matches | ||
| if !bytes.Equal(header.AppHash, computedStateRoot) { | ||
| return types.State{}, fmt.Errorf("AppHash mismatch in immediate mode: expected %x, got %x", computedStateRoot, header.AppHash) | ||
| } | ||
| newStateRoot = computedStateRoot | ||
| } | ||
| } else { | ||
| // For delayed mode, always execute transactions | ||
| rawTxs := make([][]byte, len(data.Txs)) | ||
| for i := range data.Txs { | ||
| rawTxs[i] = data.Txs[i] | ||
| } | ||
| rawTxs := make([][]byte, len(data.Txs)) | ||
| for i := range data.Txs { | ||
| rawTxs[i] = data.Txs[i] | ||
| } | ||
|
|
||
| ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header) | ||
| var err error | ||
| newStateRoot, _, err = m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash) | ||
| if err != nil { | ||
| return types.State{}, fmt.Errorf("failed to execute transactions: %w", err) | ||
| } | ||
| ctx = context.WithValue(ctx, types.SignedHeaderContextKey, header) | ||
| newStateRoot, _, err := m.exec.ExecuteTxs(ctx, rawTxs, header.Height(), header.Time(), lastState.AppHash) | ||
| if err != nil { | ||
| return types.State{}, fmt.Errorf("failed to execute transactions: %w", err) | ||
| } | ||
|
|
||
| s, err := lastState.NextState(header, newStateRoot) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Critical: Re-enable timestamp and AppHash validation to prevent consensus issues.
Commenting out these validations removes important safety checks:
These checks are fundamental to blockchain integrity and should not be disabled without a compelling reason.
Why were these validations disabled? If there's a specific issue being addressed, consider a more targeted solution rather than removing these critical checks entirely.
🏁 Script executed:
Length of output: 33513
🏁 Script executed:
Length of output: 7504
🏁 Script executed:
Length of output: 890
🏁 Script executed:
Length of output: 13830
Re-enable timestamp and AppHash validation in execValidate
Both checks were commented out in block/manager.go but are essential to prevent blocks with non-monotonic times or mismatched state hashes from being accepted:
• File: block/manager.go (func Manager.execValidate, ≈lines 802–813)
• File: block/manager_test.go (tests for “app hash mismatch” around lines 594–600)
Suggested diff in execValidate:
And restore or update the corresponding commented-out test in block/manager_test.go to assert that a bad AppHash or timestamp triggers an error.
🤖 Prompt for AI Agents