Skip to content

Commit 60403ae

Browse files
fix: Make EVM Execution more robust (#2327)
<!-- Please read and fill out this form before submitting your PR. Please make sure you have reviewed our contributors guide before submitting your first PR. NOTE: PR titles should follow semantic commits: https://www.conventionalcommits.org/en/v1.0.0/ --> ## Overview - Use a mutex lock in EVM execution methods in EngineClient - Call fork choice updated method correctly in EVM execution - Add E2E test for running an EVM sequencer - Refactor evm helper methods for use in e2e test - Modify docker compose files to expose ws port and genesis json to include address for testing <!-- Please provide an explanation of the PR, including the appropriate context, background, goal, and rationale. If there is an issue with this information, please provide a tl;dr and link the issue. Ex: Closes #<issue number> --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new end-to-end test for the EVM aggregator, enabling comprehensive integration testing with a local data availability service, EVM node, and aggregator node. - Added helper utilities for automated testing of EVM interactions in Docker environments, including transaction creation, submission, and verification. - Enabled WebSocket support for the EVM node in Docker Compose configurations. - Added new accounts with balances to EVM genesis configurations. - **Bug Fixes** - Improved transaction execution reliability by allowing invalid transactions to be skipped without crashing the node. - **Refactor** - Simplified and modularized test setup by delegating container orchestration and transaction helpers to external functions. - Enhanced concurrency safety by tracking forkchoice block hashes with mutex protection. - Adjusted test assertions to better verify block height consistency. - **Chores** - Updated and expanded dependencies across multiple modules to support enhanced testing, cloud, container, and telemetry features. - Added a new Go module for end-to-end testing with relevant dependencies and configuration. - Updated test scripts to support new build and test flows. - Updated example commands and environment variables to reflect new genesis hash values. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 973aac3 commit 60403ae

17 files changed

Lines changed: 2283 additions & 222 deletions

File tree

apps/evm/based/go.mod

Lines changed: 156 additions & 1 deletion
Large diffs are not rendered by default.

apps/evm/based/go.sum

Lines changed: 176 additions & 7 deletions
Large diffs are not rendered by default.

apps/evm/single/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ This directory contains the implementation of a single EVM sequencer using Rollk
3131
```bash
3232
./evm-single start \
3333
--evm.jwt-secret $(cat <path_to>/execution/evm/docker/jwttoken/jwt.hex) \
34-
--evm.genesis-hash 0xe720f8ec96a43a741b1ab34819acfeb029ce4f083fe73c5a08c1f6a7b17a8568 \
34+
--evm.genesis-hash 0x2b8bbb1ea1e04f9c9809b4b278a8687806edc061a356c7dbc491930d8e922503 \
3535
--rollkit.node.block_time 1s \
3636
--rollkit.node.aggregator=true \
3737
--rollkit.signer.passphrase secret

apps/evm/single/chain/genesis.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
},
3232
"0x944fDcD1c868E3cC566C78023CcB38A32cDA836E": {
3333
"balance": "0x4a47e3c12448f4ad000000"
34+
},
35+
"0x4567BF59F76c18cEa2131BDA24A7b70744308f54": {
36+
"balance": "0x4a47e3c12448f4ad000000"
3437
}
3538
},
3639
"number": "0x0",

apps/evm/single/docker-compose.yml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ services:
66
restart: unless-stopped
77
image: ghcr.io/paradigmxyz/reth:v1.4.6
88
ports:
9-
- "9001:9001"
10-
- "30303:30303"
11-
- "8545:8545"
12-
- "8551:8551"
9+
- "9001:9001" # metrics
10+
- "30303:30303" # eth/66 peering
11+
- "8545:8545" # rpc
12+
- "8551:8551" # engine
13+
- "8546:8546" # ws
1314
volumes:
1415
- ./chain:/root/chain:ro
1516
- ./jwttoken:/root/jwt:ro
@@ -26,9 +27,10 @@ services:
2627
--authrpc.jwtsecret /root/jwt/jwt.hex \
2728
--http --http.addr 0.0.0.0 --http.port 8545 \
2829
--http.api "eth,net,web3,txpool" \
30+
--ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.api eth,net,web3 \
2931
--engine.persistence-threshold 0 \
3032
--engine.memory-block-buffer-target 0 \
31-
--disable-discovery
33+
--disable-discovery \
3234
--txpool.pending-max-count 200000 \
3335
--txpool.pending-max-size 200 \
3436
--txpool.queued-max-count 200000 \
@@ -48,7 +50,7 @@ services:
4850
- rollkit-network
4951

5052
rollkit-evm-single:
51-
image: ghcr.io/rollkit/rollkit-evm-single:v0.1.4
53+
image: ghcr.io/rollkit/rollkit-evm-single:v0.1.5
5254
depends_on:
5355
reth:
5456
condition: service_started
@@ -61,7 +63,7 @@ services:
6163
- EVM_ENGINE_URL=http://reth:8551
6264
- EVM_ETH_URL=http://reth:8545
6365
- EVM_JWT_SECRET=f747494bb0fb338a0d71f5f9fe5b5034c17cc988c229b59fd71e005ee692e9bf
64-
- EVM_GENESIS_HASH=0xe720f8ec96a43a741b1ab34819acfeb029ce4f083fe73c5a08c1f6a7b17a8568
66+
- EVM_GENESIS_HASH=0x2b8bbb1ea1e04f9c9809b4b278a8687806edc061a356c7dbc491930d8e922503
6567
- EVM_BLOCK_TIME=1s
6668
- EVM_SIGNER_PASSPHRASE=secret
6769
- DA_ADDRESS=http://local-da:7980 # http://localhost:26658 (Use if not using local-da)

apps/evm/single/go.mod

Lines changed: 156 additions & 1 deletion
Large diffs are not rendered by default.

apps/evm/single/go.sum

Lines changed: 176 additions & 7 deletions
Large diffs are not rendered by default.

execution/evm/docker/chain/genesis.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
},
3232
"0x944fDcD1c868E3cC566C78023CcB38A32cDA836E": {
3333
"balance": "0x4a47e3c12448f4ad000000"
34+
},
35+
"0x4567BF59F76c18cEa2131BDA24A7b70744308f54": {
36+
"balance": "0x4a47e3c12448f4ad000000"
3437
}
3538
},
3639
"number": "0x0",

execution/evm/docker/docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ services:
3030
- "30303:30303" # eth/66 peering
3131
- "8545:8545" # rpc
3232
- "8551:8551" # engine
33+
- "8546:8546" # ws
3334
volumes:
3435
- ./chain:/root/chain:ro
3536
- ./jwttoken:/root/jwt:ro
@@ -46,6 +47,7 @@ services:
4647
--authrpc.jwtsecret /root/jwt/jwt.hex \
4748
--http --http.addr 0.0.0.0 --http.port 8545 \
4849
--http.api "eth,net,web3,txpool" \
50+
--ws --ws.addr 0.0.0.0 --ws.port 8546 --ws.api eth,net,web3 \
4951
--engine.persistence-threshold 0 \
5052
--engine.memory-block-buffer-target 0 \
5153
--disable-discovery \

execution/evm/execution.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"math/big"
1010
"net/http"
1111
"strings"
12+
"sync"
1213
"time"
1314

1415
"github.com/ethereum/go-ethereum/beacon/engine"
@@ -40,6 +41,11 @@ type EngineClient struct {
4041
genesisHash common.Hash // Hash of the genesis block
4142
initialHeight uint64
4243
feeRecipient common.Address // Address to receive transaction fees
44+
45+
mu sync.Mutex // Mutex to protect concurrent access to block hashes
46+
currentHeadBlockHash common.Hash // Store last non-finalized HeadBlockHash
47+
currentSafeBlockHash common.Hash // Store last non-finalized SafeBlockHash
48+
currentFinalizedBlockHash common.Hash // Store last finalized block hash
4349
}
4450

4551
// NewEngineExecutionClient creates a new instance of EngineAPIExecutionClient
@@ -168,7 +174,8 @@ func (c *EngineClient) ExecuteTxs(ctx context.Context, txs [][]byte, blockHeight
168174
}
169175
err = c.ethClient.SendTransaction(context.Background(), ethTxs[i])
170176
if err != nil {
171-
return nil, 0, fmt.Errorf("failed to send transaction: %w", err)
177+
// Ignore the error if the transaction fails to be sent since transactions can be invalid and an invalid transaction sent by a client should not crash the node.
178+
continue
172179
}
173180
}
174181

@@ -264,13 +271,22 @@ func (c *EngineClient) ExecuteTxs(ctx context.Context, txs [][]byte, blockHeight
264271
}
265272

266273
func (c *EngineClient) setFinal(ctx context.Context, blockHash common.Hash, isFinal bool) error {
267-
args := engine.ForkchoiceStateV1{
268-
HeadBlockHash: blockHash,
269-
SafeBlockHash: blockHash,
270-
}
274+
c.mu.Lock()
275+
// Update block hashes based on finalization status
271276
if isFinal {
272-
args.FinalizedBlockHash = blockHash
277+
c.currentFinalizedBlockHash = blockHash
278+
} else {
279+
c.currentHeadBlockHash = blockHash
280+
c.currentSafeBlockHash = blockHash
281+
}
282+
283+
// Construct forkchoice state
284+
args := engine.ForkchoiceStateV1{
285+
HeadBlockHash: c.currentHeadBlockHash,
286+
SafeBlockHash: c.currentSafeBlockHash,
287+
FinalizedBlockHash: c.currentFinalizedBlockHash,
273288
}
289+
c.mu.Unlock()
274290

275291
var forkchoiceResult engine.ForkChoiceResponse
276292
err := c.engineClient.CallContext(ctx, &forkchoiceResult, "engine_forkchoiceUpdatedV3",
@@ -311,6 +327,7 @@ func (c *EngineClient) getBlockInfo(ctx context.Context, height uint64) (common.
311327
return header.Hash(), header.Root, header.GasLimit, header.Time, nil
312328
}
313329

330+
// decodeSecret decodes a hex-encoded JWT secret string into a byte slice.
314331
func decodeSecret(jwtSecret string) ([]byte, error) {
315332
secret, err := hex.DecodeString(strings.TrimPrefix(jwtSecret, "0x"))
316333
if err != nil {
@@ -319,6 +336,7 @@ func decodeSecret(jwtSecret string) ([]byte, error) {
319336
return secret, nil
320337
}
321338

339+
// getAuthToken creates a JWT token signed with the provided secret, valid for 1 hour.
322340
func getAuthToken(jwtSecret []byte) (string, error) {
323341
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
324342
"exp": time.Now().Add(time.Hour * 1).Unix(), // Expires in 1 hour

0 commit comments

Comments
 (0)