Skip to content

Commit 0954334

Browse files
ImTeispacesailor24
authored andcommitted
Use engine API that matches hardfork version (#9253)
* Use engine API that matches HF * Simplify engine client code by using EngineAPIVersion * Return full method string from engine api version methods * FCUVersion takes payload attributes * Use a proper method * Handle switch default case explicitly * Return fcuV3 if attrs is nil * Add test cases for engine api version methods
1 parent d73dfe1 commit 0954334

File tree

12 files changed

+251
-51
lines changed

12 files changed

+251
-51
lines changed

op-e2e/actions/l2_engine_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) {
154154
engine.ActL2IncludeTx(dp.Addresses.Alice)(t)
155155
}
156156

157-
envelope, err := l2Cl.GetPayload(t.Ctx(), *fcRes.PayloadID)
157+
envelope, err := l2Cl.GetPayload(t.Ctx(), eth.PayloadInfo{ID: *fcRes.PayloadID, Timestamp: uint64(nextBlockTime)})
158158
payload := envelope.ExecutionPayload
159159
require.NoError(t, err)
160160
require.Equal(t, parent.Hash(), payload.ParentHash, "block builds on parent block")

op-e2e/op_geth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func (d *OpGeth) AddL2Block(ctx context.Context, txs ...*types.Transaction) (*et
146146
return nil, err
147147
}
148148

149-
envelope, err := d.l2Engine.GetPayload(ctx, *res.PayloadID)
149+
envelope, err := d.l2Engine.GetPayload(ctx, eth.PayloadInfo{ID: *res.PayloadID, Timestamp: uint64(attrs.Timestamp)})
150150
payload := envelope.ExecutionPayload
151151

152152
if err != nil {

op-e2e/op_geth_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func TestGethOnlyPendingBlockIsLatest(t *testing.T) {
196196
time.Sleep(time.Second * 4) // conservatively wait 4 seconds, CI might lag during block building.
197197

198198
// retrieve the block
199-
envelope, err := opGeth.l2Engine.GetPayload(ctx, *res.PayloadID)
199+
envelope, err := opGeth.l2Engine.GetPayload(ctx, eth.PayloadInfo{ID: *res.PayloadID, Timestamp: uint64(attrs.Timestamp)})
200200
require.NoError(t, err)
201201

202202
payload := envelope.ExecutionPayload

op-node/rollup/derive/engine_controller.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ var _ EngineControl = (*EngineController)(nil)
3838
var _ LocalEngineControl = (*EngineController)(nil)
3939

4040
type ExecEngine interface {
41-
GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth.ExecutionPayloadEnvelope, error)
41+
GetPayload(ctx context.Context, payloadInfo eth.PayloadInfo) (*eth.ExecutionPayloadEnvelope, error)
4242
ForkchoiceUpdate(ctx context.Context, state *eth.ForkchoiceState, attr *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error)
4343
NewPayload(ctx context.Context, payload *eth.ExecutionPayload, parentBeaconBlockRoot *common.Hash) (*eth.PayloadStatusV1, error)
4444
L2BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L2BlockRef, error)
@@ -63,7 +63,7 @@ type EngineController struct {
6363

6464
// Building State
6565
buildingOnto eth.L2BlockRef
66-
buildingID eth.PayloadID
66+
buildingInfo eth.PayloadInfo
6767
buildingSafe bool
6868
safeAttrs *AttributesWithParent
6969
}
@@ -104,7 +104,7 @@ func (e *EngineController) Finalized() eth.L2BlockRef {
104104
}
105105

106106
func (e *EngineController) BuildingPayload() (eth.L2BlockRef, eth.PayloadID, bool) {
107-
return e.buildingOnto, e.buildingID, e.buildingSafe
107+
return e.buildingOnto, e.buildingInfo.ID, e.buildingSafe
108108
}
109109

110110
func (e *EngineController) IsEngineSyncing() bool {
@@ -146,8 +146,8 @@ func (e *EngineController) StartPayload(ctx context.Context, parent eth.L2BlockR
146146
if e.IsEngineSyncing() {
147147
return BlockInsertTemporaryErr, fmt.Errorf("engine is in progess of p2p sync")
148148
}
149-
if e.buildingID != (eth.PayloadID{}) {
150-
e.log.Warn("did not finish previous block building, starting new building now", "prev_onto", e.buildingOnto, "prev_payload_id", e.buildingID, "new_onto", parent)
149+
if e.buildingInfo != (eth.PayloadInfo{}) {
150+
e.log.Warn("did not finish previous block building, starting new building now", "prev_onto", e.buildingOnto, "prev_payload_id", e.buildingInfo.ID, "new_onto", parent)
151151
// TODO(8841): maybe worth it to force-cancel the old payload ID here.
152152
}
153153
fc := eth.ForkchoiceState{
@@ -161,7 +161,7 @@ func (e *EngineController) StartPayload(ctx context.Context, parent eth.L2BlockR
161161
return errTyp, err
162162
}
163163

164-
e.buildingID = id
164+
e.buildingInfo = eth.PayloadInfo{ID: id, Timestamp: uint64(attrs.attributes.Timestamp)}
165165
e.buildingSafe = updateSafe
166166
e.buildingOnto = parent
167167
if updateSafe {
@@ -172,7 +172,7 @@ func (e *EngineController) StartPayload(ctx context.Context, parent eth.L2BlockR
172172
}
173173

174174
func (e *EngineController) ConfirmPayload(ctx context.Context, agossip async.AsyncGossiper, sequencerConductor conductor.SequencerConductor) (out *eth.ExecutionPayloadEnvelope, errTyp BlockInsertionErrType, err error) {
175-
if e.buildingID == (eth.PayloadID{}) {
175+
if e.buildingInfo == (eth.PayloadInfo{}) {
176176
return nil, BlockInsertPrestateErr, fmt.Errorf("cannot complete payload building: not currently building a payload")
177177
}
178178
if e.buildingOnto.Hash != e.unsafeHead.Hash { // E.g. when safe-attributes consolidation fails, it will drop the existing work.
@@ -185,9 +185,9 @@ func (e *EngineController) ConfirmPayload(ctx context.Context, agossip async.Asy
185185
}
186186
// Update the safe head if the payload is built with the last attributes in the batch.
187187
updateSafe := e.buildingSafe && e.safeAttrs != nil && e.safeAttrs.isLastInSpan
188-
envelope, errTyp, err := confirmPayload(ctx, e.log, e.engine, fc, e.buildingID, updateSafe, agossip, sequencerConductor)
188+
envelope, errTyp, err := confirmPayload(ctx, e.log, e.engine, fc, e.buildingInfo, updateSafe, agossip, sequencerConductor)
189189
if err != nil {
190-
return nil, errTyp, fmt.Errorf("failed to complete building on top of L2 chain %s, id: %s, error (%d): %w", e.buildingOnto, e.buildingID, errTyp, err)
190+
return nil, errTyp, fmt.Errorf("failed to complete building on top of L2 chain %s, id: %s, error (%d): %w", e.buildingOnto, e.buildingInfo.ID, errTyp, err)
191191
}
192192
ref, err := PayloadToBlockRef(e.rollupCfg, envelope.ExecutionPayload)
193193
if err != nil {
@@ -211,14 +211,14 @@ func (e *EngineController) ConfirmPayload(ctx context.Context, agossip async.Asy
211211
}
212212

213213
func (e *EngineController) CancelPayload(ctx context.Context, force bool) error {
214-
if e.buildingID == (eth.PayloadID{}) { // only cancel if there is something to cancel.
214+
if e.buildingInfo == (eth.PayloadInfo{}) { // only cancel if there is something to cancel.
215215
return nil
216216
}
217217
// the building job gets wrapped up as soon as the payload is retrieved, there's no explicit cancel in the Engine API
218-
e.log.Error("cancelling old block sealing job", "payload", e.buildingID)
219-
_, err := e.engine.GetPayload(ctx, e.buildingID)
218+
e.log.Error("cancelling old block sealing job", "payload", e.buildingInfo.ID)
219+
_, err := e.engine.GetPayload(ctx, e.buildingInfo)
220220
if err != nil {
221-
e.log.Error("failed to cancel block building job", "payload", e.buildingID, "err", err)
221+
e.log.Error("failed to cancel block building job", "payload", e.buildingInfo.ID, "err", err)
222222
if !force {
223223
return err
224224
}
@@ -228,7 +228,7 @@ func (e *EngineController) CancelPayload(ctx context.Context, force bool) error
228228
}
229229

230230
func (e *EngineController) resetBuildingState() {
231-
e.buildingID = eth.PayloadID{}
231+
e.buildingInfo = eth.PayloadInfo{}
232232
e.buildingOnto = eth.L2BlockRef{}
233233
e.buildingSafe = false
234234
e.safeAttrs = nil

op-node/rollup/derive/engine_update.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ func confirmPayload(
124124
log log.Logger,
125125
eng ExecEngine,
126126
fc eth.ForkchoiceState,
127-
id eth.PayloadID,
127+
payloadInfo eth.PayloadInfo,
128128
updateSafe bool,
129129
agossip async.AsyncGossiper,
130130
sequencerConductor conductor.SequencerConductor,
@@ -140,7 +140,7 @@ func confirmPayload(
140140
"parent", envelope.ExecutionPayload.ParentHash,
141141
"txs", len(envelope.ExecutionPayload.Transactions))
142142
} else {
143-
envelope, err = eng.GetPayload(ctx, id)
143+
envelope, err = eng.GetPayload(ctx, payloadInfo)
144144
}
145145
if err != nil {
146146
// even if it is an input-error (unknown payload ID), it is temporary, since we will re-attempt the full payload building, not just the retrieval of the payload.

op-node/rollup/types.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,46 @@ func (c *Config) IsInterop(timestamp uint64) bool {
353353
return c.InteropTime != nil && timestamp >= *c.InteropTime
354354
}
355355

356+
// ForkchoiceUpdatedVersion returns the EngineAPIMethod suitable for the chain hard fork version.
357+
func (c *Config) ForkchoiceUpdatedVersion(attr *eth.PayloadAttributes) eth.EngineAPIMethod {
358+
if attr == nil {
359+
// Don't begin payload build process.
360+
return eth.FCUV3
361+
}
362+
ts := uint64(attr.Timestamp)
363+
if c.IsEcotone(ts) {
364+
// Cancun
365+
return eth.FCUV3
366+
} else if c.IsCanyon(ts) {
367+
// Shanghai
368+
return eth.FCUV2
369+
} else {
370+
// According to Ethereum engine API spec, we can use fcuV2 here,
371+
// but upstream Geth v1.13.11 does not accept V2 before Shanghai.
372+
return eth.FCUV1
373+
}
374+
}
375+
376+
// NewPayloadVersion returns the EngineAPIMethod suitable for the chain hard fork version.
377+
func (c *Config) NewPayloadVersion(timestamp uint64) eth.EngineAPIMethod {
378+
if c.IsEcotone(timestamp) {
379+
// Cancun
380+
return eth.NewPayloadV3
381+
} else {
382+
return eth.NewPayloadV2
383+
}
384+
}
385+
386+
// GetPayloadVersion returns the EngineAPIMethod suitable for the chain hard fork version.
387+
func (c *Config) GetPayloadVersion(timestamp uint64) eth.EngineAPIMethod {
388+
if c.IsEcotone(timestamp) {
389+
// Cancun
390+
return eth.GetPayloadV3
391+
} else {
392+
return eth.GetPayloadV2
393+
}
394+
}
395+
356396
// Description outputs a banner describing the important parts of rollup configuration in a human-readable form.
357397
// Optionally provide a mapping of L2 chain IDs to network names to label the L2 chain with if not unknown.
358398
// The config should be config.Check()-ed before creating a description.

op-node/rollup/types_test.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,3 +508,118 @@ func TestTimestampForBlock(t *testing.T) {
508508
}
509509

510510
}
511+
512+
func TestForkchoiceUpdatedVersion(t *testing.T) {
513+
config := randConfig()
514+
tests := []struct {
515+
name string
516+
canyonTime uint64
517+
ecotoneTime uint64
518+
attrs *eth.PayloadAttributes
519+
expectedMethod eth.EngineAPIMethod
520+
}{
521+
{
522+
name: "NoAttrs",
523+
canyonTime: 10,
524+
ecotoneTime: 20,
525+
attrs: nil,
526+
expectedMethod: eth.FCUV3,
527+
},
528+
{
529+
name: "BeforeCanyon",
530+
canyonTime: 10,
531+
ecotoneTime: 20,
532+
attrs: &eth.PayloadAttributes{Timestamp: 5},
533+
expectedMethod: eth.FCUV1,
534+
},
535+
{
536+
name: "Canyon",
537+
canyonTime: 10,
538+
ecotoneTime: 20,
539+
attrs: &eth.PayloadAttributes{Timestamp: 15},
540+
expectedMethod: eth.FCUV2,
541+
},
542+
{
543+
name: "Ecotone",
544+
canyonTime: 10,
545+
ecotoneTime: 20,
546+
attrs: &eth.PayloadAttributes{Timestamp: 25},
547+
expectedMethod: eth.FCUV3,
548+
},
549+
}
550+
551+
for _, test := range tests {
552+
test := test
553+
t.Run(fmt.Sprintf("TestForkchoiceUpdatedVersion_%s", test.name), func(t *testing.T) {
554+
config.CanyonTime = &test.canyonTime
555+
config.EcotoneTime = &test.ecotoneTime
556+
assert.Equal(t, config.ForkchoiceUpdatedVersion(test.attrs), test.expectedMethod)
557+
})
558+
}
559+
}
560+
561+
func TestNewPayloadVersion(t *testing.T) {
562+
config := randConfig()
563+
canyonTime := uint64(0)
564+
config.CanyonTime = &canyonTime
565+
tests := []struct {
566+
name string
567+
ecotoneTime uint64
568+
payloadTime uint64
569+
expectedMethod eth.EngineAPIMethod
570+
}{
571+
{
572+
name: "BeforeEcotone",
573+
ecotoneTime: 10,
574+
payloadTime: 5,
575+
expectedMethod: eth.NewPayloadV2,
576+
},
577+
{
578+
name: "Ecotone",
579+
ecotoneTime: 10,
580+
payloadTime: 15,
581+
expectedMethod: eth.NewPayloadV3,
582+
},
583+
}
584+
585+
for _, test := range tests {
586+
test := test
587+
t.Run(fmt.Sprintf("TestNewPayloadVersion_%s", test.name), func(t *testing.T) {
588+
config.EcotoneTime = &test.ecotoneTime
589+
assert.Equal(t, config.NewPayloadVersion(test.payloadTime), test.expectedMethod)
590+
})
591+
}
592+
}
593+
594+
func TestGetPayloadVersion(t *testing.T) {
595+
config := randConfig()
596+
canyonTime := uint64(0)
597+
config.CanyonTime = &canyonTime
598+
tests := []struct {
599+
name string
600+
ecotoneTime uint64
601+
payloadTime uint64
602+
expectedMethod eth.EngineAPIMethod
603+
}{
604+
{
605+
name: "BeforeEcotone",
606+
ecotoneTime: 10,
607+
payloadTime: 5,
608+
expectedMethod: eth.GetPayloadV2,
609+
},
610+
{
611+
name: "Ecotone",
612+
ecotoneTime: 10,
613+
payloadTime: 15,
614+
expectedMethod: eth.GetPayloadV3,
615+
},
616+
}
617+
618+
for _, test := range tests {
619+
test := test
620+
t.Run(fmt.Sprintf("TestGetPayloadVersion_%s", test.name), func(t *testing.T) {
621+
config.EcotoneTime = &test.ecotoneTime
622+
assert.Equal(t, config.GetPayloadVersion(test.payloadTime), test.expectedMethod)
623+
})
624+
}
625+
}

op-program/client/l2/engine.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,44 @@ func (o *OracleEngine) L2OutputRoot(l2ClaimBlockNum uint64) (eth.Bytes32, error)
5050
return rollup.ComputeL2OutputRootV0(eth.HeaderBlockInfo(outBlock), withdrawalsTrie.Hash())
5151
}
5252

53-
func (o *OracleEngine) GetPayload(ctx context.Context, payloadId eth.PayloadID) (*eth.ExecutionPayloadEnvelope, error) {
54-
res, err := o.api.GetPayloadV3(ctx, payloadId)
53+
func (o *OracleEngine) GetPayload(ctx context.Context, payloadInfo eth.PayloadInfo) (*eth.ExecutionPayloadEnvelope, error) {
54+
var res *eth.ExecutionPayloadEnvelope
55+
var err error
56+
switch method := o.rollupCfg.GetPayloadVersion(payloadInfo.Timestamp); method {
57+
case eth.GetPayloadV3:
58+
res, err = o.api.GetPayloadV3(ctx, payloadInfo.ID)
59+
case eth.GetPayloadV2:
60+
res, err = o.api.GetPayloadV2(ctx, payloadInfo.ID)
61+
default:
62+
return nil, fmt.Errorf("unsupported GetPayload version: %s", method)
63+
}
5564
if err != nil {
5665
return nil, err
5766
}
5867
return res, nil
5968
}
6069

6170
func (o *OracleEngine) ForkchoiceUpdate(ctx context.Context, state *eth.ForkchoiceState, attr *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) {
62-
return o.api.ForkchoiceUpdatedV3(ctx, state, attr)
71+
switch method := o.rollupCfg.ForkchoiceUpdatedVersion(attr); method {
72+
case eth.FCUV3:
73+
return o.api.ForkchoiceUpdatedV3(ctx, state, attr)
74+
case eth.FCUV2:
75+
return o.api.ForkchoiceUpdatedV2(ctx, state, attr)
76+
case eth.FCUV1:
77+
return o.api.ForkchoiceUpdatedV1(ctx, state, attr)
78+
default:
79+
return nil, fmt.Errorf("unsupported ForkchoiceUpdated version: %s", method)
80+
}
6381
}
6482

6583
func (o *OracleEngine) NewPayload(ctx context.Context, payload *eth.ExecutionPayload, parentBeaconBlockRoot *common.Hash) (*eth.PayloadStatusV1, error) {
66-
if o.rollupCfg.IsEcotone(uint64(payload.Timestamp)) {
84+
switch method := o.rollupCfg.NewPayloadVersion(uint64(payload.Timestamp)); method {
85+
case eth.NewPayloadV3:
6786
return o.api.NewPayloadV3(ctx, payload, []common.Hash{}, parentBeaconBlockRoot)
68-
} else {
87+
case eth.NewPayloadV2:
6988
return o.api.NewPayloadV2(ctx, payload)
89+
default:
90+
return nil, fmt.Errorf("unsupported NewPayload version: %s", method)
7091
}
7192
}
7293

op-service/eth/types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ type Uint256Quantity = uint256.Int
153153
type Data = hexutil.Bytes
154154

155155
type PayloadID = engine.PayloadID
156+
type PayloadInfo struct {
157+
ID PayloadID
158+
Timestamp uint64
159+
}
156160

157161
type ExecutionPayloadEnvelope struct {
158162
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot,omitempty"`
@@ -454,3 +458,17 @@ func (v *Uint64String) UnmarshalText(b []byte) error {
454458
*v = Uint64String(n)
455459
return nil
456460
}
461+
462+
type EngineAPIMethod string
463+
464+
const (
465+
FCUV1 EngineAPIMethod = "engine_forkchoiceUpdatedV1"
466+
FCUV2 EngineAPIMethod = "engine_forkchoiceUpdatedV2"
467+
FCUV3 EngineAPIMethod = "engine_forkchoiceUpdatedV3"
468+
469+
NewPayloadV2 EngineAPIMethod = "engine_newPayloadV2"
470+
NewPayloadV3 EngineAPIMethod = "engine_newPayloadV3"
471+
472+
GetPayloadV2 EngineAPIMethod = "engine_getPayloadV2"
473+
GetPayloadV3 EngineAPIMethod = "engine_getPayloadV3"
474+
)

0 commit comments

Comments
 (0)