Skip to content

Commit 42f454b

Browse files
authored
fix: add flag to recover from nil payload (#2620)
<!-- 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 This flag allows the canonical header hash to be the same as the previous one in the case of a restart. its a weird edge case we hit recently on a testnet
1 parent f79548b commit 42f454b

File tree

9 files changed

+117
-75
lines changed

9 files changed

+117
-75
lines changed

apps/evm/single/cmd/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func InitCmd() *cobra.Command {
8484

8585
// Add flags to the command
8686
rollconf.AddFlags(initCmd)
87-
initCmd.Flags().String(rollgenesis.ChainIDFlag, "rollkit-test", "chain ID")
87+
initCmd.Flags().String(rollgenesis.ChainIDFlag, "evolve-test", "chain ID")
8888

8989
return initCmd
9090
}

apps/evm/single/cmd/run.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,22 @@ var RunCmd = &cobra.Command{
3030
Aliases: []string{"node", "run"},
3131
Short: "Run the evolve node with EVM execution client",
3232
RunE: func(cmd *cobra.Command, args []string) error {
33-
executor, err := createExecutionClient(cmd)
34-
if err != nil {
35-
return err
36-
}
37-
38-
nodeConfig, err := rollcmd.ParseConfig(cmd)
39-
if err != nil {
40-
return err
41-
}
42-
43-
logger := rollcmd.SetupLogger(nodeConfig.Log)
33+
executor, err := createExecutionClient(cmd)
34+
if err != nil {
35+
return err
36+
}
37+
38+
nodeConfig, err := rollcmd.ParseConfig(cmd)
39+
if err != nil {
40+
return err
41+
}
42+
43+
logger := rollcmd.SetupLogger(nodeConfig.Log)
44+
45+
// Attach logger to the EVM engine client if available
46+
if ec, ok := executor.(*evm.EngineClient); ok {
47+
ec.SetLogger(logger.With().Str("module", "engine_client").Logger())
48+
}
4449

4550
headerNamespace := da.PrepareNamespace([]byte(nodeConfig.DA.HeaderNamespace))
4651
dataNamespace := da.PrepareNamespace([]byte(nodeConfig.DA.DataNamespace))

apps/evm/single/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func main() {
2727
rootCmd.AddCommand(
2828
cmd.InitCmd(),
2929
cmd.RunCmd,
30+
cmd.NewRollbackCmd(),
3031
rollcmd.VersionCmd,
3132
rollcmd.NetInfoCmd,
3233
rollcmd.StoreUnsafeCleanCmd,

execution/evm/docker/docker-compose-full-node.yml

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,29 @@ services:
4040
entrypoint: /bin/sh -c
4141
command:
4242
- |
43-
ev-reth node \
44-
--chain /root/chain/genesis.json \
45-
--datadir /home/reth/eth-home \
46-
--metrics 0.0.0.0:9001 \
47-
--authrpc.addr 0.0.0.0 \
48-
--authrpc.port 8551 \
49-
--authrpc.jwtsecret /root/jwt/jwt.hex \
50-
--http --http.addr 0.0.0.0 --http.port 8545 \
51-
--http.api eth,net,web3,txpool \
52-
--ws --ws.addr 0.0.0.0 --ws.port 8546 \
53-
--ws.api eth,net,web3 \
54-
--engine.persistence-threshold 0 \
55-
--engine.memory-block-buffer-target 0 \
56-
--disable-discovery \
57-
--txpool.pending-max-count 200000 \
58-
--txpool.pending-max-size 200 \
59-
--txpool.queued-max-count 200000 \
60-
--txpool.queued-max-size 200 \
61-
--txpool.max-account-slots 2048 \
62-
--txpool.max-new-txns 2048 \
63-
--txpool.additional-validation-tasks 16 \
64-
--ev-reth.enable
43+
ev-reth node \
44+
--chain /root/chain/genesis.json \
45+
--datadir /home/reth/eth-home \
46+
--metrics 0.0.0.0:9001 \
47+
--authrpc.addr 0.0.0.0 \
48+
--authrpc.port 8551 \
49+
--authrpc.jwtsecret /root/jwt/jwt.hex \
50+
--http --http.addr 0.0.0.0 --http.port 8545 \
51+
--http.api eth,net,web3,txpool \
52+
--ws --ws.addr 0.0.0.0 --ws.port 8546 \
53+
--ws.api eth,net,web3 \
54+
--engine.persistence-threshold 0 \
55+
--engine.memory-block-buffer-target 0 \
56+
--disable-discovery \
57+
--txpool.pending-max-count 200000 \
58+
--txpool.pending-max-size 200 \
59+
--txpool.queued-max-count 200000 \
60+
--txpool.queued-max-size 200 \
61+
--txpool.max-account-slots 2048 \
62+
--txpool.max-new-txns 2048 \
63+
--txpool.additional-validation-tasks 16 \
64+
--engine.always-process-payload-attributes-on-canonical-head \
65+
--ev-reth.enable
6566
6667
volumes:
6768
reth:

execution/evm/docker/docker-compose.yml

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ services:
2626
jwt-init:
2727
condition: service_completed_successfully
2828
ports:
29-
- "9001:9001" # metrics
29+
- "9001:9001" # metrics
3030
- "30303:30303" # eth/66 peering
31-
- "8545:8545" # HTTP RPC
32-
- "8551:8551" # Engine API (authenticated)
33-
- "8546:8546" # WebSocket RPC
31+
- "8545:8545" # HTTP RPC
32+
- "8551:8551" # Engine API (authenticated)
33+
- "8546:8546" # WebSocket RPC
3434
volumes:
3535
- ./chain:/root/chain:ro
3636
- ./jwttoken:/root/jwt:ro
@@ -40,28 +40,29 @@ services:
4040
entrypoint: /bin/sh -c
4141
command:
4242
- |
43-
ev-reth node \
44-
--chain /root/chain/genesis.json \
45-
--datadir /home/reth/eth-home \
46-
--metrics 0.0.0.0:9001 \
47-
--authrpc.addr 0.0.0.0 \
48-
--authrpc.port 8551 \
49-
--authrpc.jwtsecret /root/jwt/jwt.hex \
50-
--http --http.addr 0.0.0.0 --http.port 8545 \
51-
--http.api eth,net,web3,txpool \
52-
--ws --ws.addr 0.0.0.0 --ws.port 8546 \
53-
--ws.api eth,net,web3 \
54-
--engine.persistence-threshold 0 \
55-
--engine.memory-block-buffer-target 0 \
56-
--disable-discovery \
57-
--txpool.pending-max-count 200000 \
58-
--txpool.pending-max-size 200 \
59-
--txpool.queued-max-count 200000 \
60-
--txpool.queued-max-size 200 \
61-
--txpool.max-account-slots 2048 \
62-
--txpool.max-new-txns 2048 \
63-
--txpool.additional-validation-tasks 16 \
64-
--ev-reth.enable
43+
ev-reth node \
44+
--chain /root/chain/genesis.json \
45+
--datadir /home/reth/eth-home \
46+
--metrics 0.0.0.0:9001 \
47+
--authrpc.addr 0.0.0.0 \
48+
--authrpc.port 8551 \
49+
--authrpc.jwtsecret /root/jwt/jwt.hex \
50+
--http --http.addr 0.0.0.0 --http.port 8545 \
51+
--http.api eth,net,web3,txpool \
52+
--ws --ws.addr 0.0.0.0 --ws.port 8546 \
53+
--ws.api eth,net,web3 \
54+
--engine.persistence-threshold 0 \
55+
--engine.memory-block-buffer-target 0 \
56+
--disable-discovery \
57+
--txpool.pending-max-count 200000 \
58+
--txpool.pending-max-size 200 \
59+
--txpool.queued-max-count 200000 \
60+
--txpool.queued-max-size 200 \
61+
--txpool.max-account-slots 2048 \
62+
--txpool.max-new-txns 2048 \
63+
--txpool.additional-validation-tasks 16 \
64+
--ev-reth.enable \
65+
--engine.always-process-payload-attributes-on-canonical-head \
6566
6667
volumes:
6768
reth:

execution/evm/execution.go

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@ import (
1717
"github.com/ethereum/go-ethereum/ethclient"
1818
"github.com/ethereum/go-ethereum/rpc"
1919
"github.com/golang-jwt/jwt/v5"
20+
"github.com/rs/zerolog"
2021

2122
"github.com/evstack/ev-node/core/execution"
2223
)
2324

2425
var (
25-
// ErrNilPayloadStatus indicates that PayloadID returned by EVM was nil
26-
ErrNilPayloadStatus = errors.New("nil payload status")
2726
// ErrInvalidPayloadStatus indicates that EVM returned status != VALID
2827
ErrInvalidPayloadStatus = errors.New("invalid payload status")
2928
)
@@ -45,6 +44,8 @@ type EngineClient struct {
4544
currentHeadBlockHash common.Hash // Store last non-finalized HeadBlockHash
4645
currentSafeBlockHash common.Hash // Store last non-finalized SafeBlockHash
4746
currentFinalizedBlockHash common.Hash // Store last finalized block hash
47+
48+
logger zerolog.Logger
4849
}
4950

5051
// NewEngineExecutionClient creates a new instance of EngineAPIExecutionClient
@@ -89,9 +90,15 @@ func NewEngineExecutionClient(
8990
currentHeadBlockHash: genesisHash,
9091
currentSafeBlockHash: genesisHash,
9192
currentFinalizedBlockHash: genesisHash,
93+
logger: zerolog.Nop(),
9294
}, nil
9395
}
9496

97+
// SetLogger allows callers to attach a structured logger.
98+
func (c *EngineClient) SetLogger(l zerolog.Logger) {
99+
c.logger = l
100+
}
101+
95102
// InitChain initializes the blockchain with the given genesis parameters
96103
func (c *EngineClient) InitChain(ctx context.Context, genesisTime time.Time, initialHeight uint64, chainID string) ([]byte, uint64, error) {
97104
if initialHeight != 1 {
@@ -166,9 +173,7 @@ func (c *EngineClient) ExecuteTxs(ctx context.Context, txs [][]byte, blockHeight
166173
}
167174

168175
// update forkchoice to get the next payload id
169-
var forkchoiceResult engine.ForkChoiceResponse
170-
171-
// Create evolve-compatible payload attributes with flattened structure
176+
// Create evolve-compatible payloadtimestamp.Unix()
172177
evPayloadAttrs := map[string]interface{}{
173178
// Standard Ethereum payload attributes (flattened) - using camelCase as expected by JSON
174179
"timestamp": timestamp.Unix(),
@@ -182,16 +187,29 @@ func (c *EngineClient) ExecuteTxs(ctx context.Context, txs [][]byte, blockHeight
182187
"gasLimit": prevGasLimit, // Use camelCase to match JSON conventions
183188
}
184189

185-
err = c.engineClient.CallContext(ctx, &forkchoiceResult, "engine_forkchoiceUpdatedV3",
186-
args,
187-
evPayloadAttrs,
188-
)
190+
c.logger.Debug().
191+
Uint64("height", blockHeight).
192+
Int("tx_count", len(txs)).
193+
Msg("engine_forkchoiceUpdatedV3")
194+
195+
var forkchoiceResult engine.ForkChoiceResponse
196+
err = c.engineClient.CallContext(ctx, &forkchoiceResult, "engine_forkchoiceUpdatedV3", args, evPayloadAttrs)
189197
if err != nil {
190198
return nil, 0, fmt.Errorf("forkchoice update failed: %w", err)
191199
}
192-
193200
if forkchoiceResult.PayloadID == nil {
194-
return nil, 0, ErrNilPayloadStatus
201+
c.logger.Error().
202+
Str("status", string(forkchoiceResult.PayloadStatus.Status)).
203+
Str("latestValidHash", forkchoiceResult.PayloadStatus.LatestValidHash.Hex()).
204+
Interface("validationError", forkchoiceResult.PayloadStatus.ValidationError).
205+
Interface("forkchoiceState", args).
206+
Interface("payloadAttributes", evPayloadAttrs).
207+
Uint64("blockHeight", blockHeight).
208+
Msg("returned nil PayloadID")
209+
210+
return nil, 0, fmt.Errorf("returned nil PayloadID - (status: %s, latestValidHash: %s)",
211+
forkchoiceResult.PayloadStatus.Status,
212+
forkchoiceResult.PayloadStatus.LatestValidHash.Hex())
195213
}
196214

197215
// get payload
@@ -214,6 +232,11 @@ func (c *EngineClient) ExecuteTxs(ctx context.Context, txs [][]byte, blockHeight
214232
}
215233

216234
if newPayloadResult.Status != engine.VALID {
235+
c.logger.Warn().
236+
Str("status", string(newPayloadResult.Status)).
237+
Str("latestValidHash", newPayloadResult.LatestValidHash.Hex()).
238+
Interface("validationError", newPayloadResult.ValidationError).
239+
Msg("engine_newPayloadV4 returned non-VALID status")
217240
return nil, 0, ErrInvalidPayloadStatus
218241
}
219242

@@ -246,15 +269,17 @@ func (c *EngineClient) setFinal(ctx context.Context, blockHash common.Hash, isFi
246269
c.mu.Unlock()
247270

248271
var forkchoiceResult engine.ForkChoiceResponse
249-
err := c.engineClient.CallContext(ctx, &forkchoiceResult, "engine_forkchoiceUpdatedV3",
250-
args,
251-
nil,
252-
)
272+
err := c.engineClient.CallContext(ctx, &forkchoiceResult, "engine_forkchoiceUpdatedV3", args, nil)
253273
if err != nil {
254274
return fmt.Errorf("forkchoice update failed with error: %w", err)
255275
}
256276

257277
if forkchoiceResult.PayloadStatus.Status != engine.VALID {
278+
c.logger.Warn().
279+
Str("status", string(forkchoiceResult.PayloadStatus.Status)).
280+
Str("latestValidHash", forkchoiceResult.PayloadStatus.LatestValidHash.Hex()).
281+
Interface("validationError", forkchoiceResult.PayloadStatus.ValidationError).
282+
Msg("forkchoiceUpdatedV3 returned non-VALID status")
258283
return ErrInvalidPayloadStatus
259284
}
260285

execution/evm/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/ethereum/go-ethereum v1.16.2
1111
github.com/evstack/ev-node/core v0.0.0-20250312114929-104787ba1a4c
1212
github.com/golang-jwt/jwt/v5 v5.3.0
13+
github.com/rs/zerolog v1.34.0
1314
github.com/stretchr/testify v1.10.0
1415
github.com/testcontainers/testcontainers-go/modules/compose v0.38.0
1516
)

execution/evm/go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRq
139139
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
140140
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
141141
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
142+
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
142143
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
143144
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
144145
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
@@ -246,6 +247,7 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
246247
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
247248
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
248249
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
250+
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
249251
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
250252
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
251253
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -379,6 +381,7 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
379381
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
380382
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
381383
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
384+
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
382385
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
383386
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
384387
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -526,6 +529,9 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR
526529
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
527530
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
528531
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
532+
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
533+
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
534+
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
529535
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
530536
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
531537
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -741,6 +747,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
741747
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
742748
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
743749
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
750+
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
744751
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
745752
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
746753
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=

scripts/run-evm-nodes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ func (nm *nodeManager) startEVMDocker(name string, rpcPort, enginePort, wsPort i
293293
"--txpool.max-account-slots", "2048",
294294
"--txpool.max-new-txns", "2048",
295295
"--txpool.additional-validation-tasks", "16",
296+
"--engine.always-process-payload-attributes-on-canonical-head",
296297
"--ev-reth.enable",
297298
}
298299

0 commit comments

Comments
 (0)