Skip to content

Commit

Permalink
feat: support combined engine api
Browse files Browse the repository at this point in the history
  • Loading branch information
bnoieh committed Sep 30, 2024
1 parent afbb8b1 commit 769e7c8
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 10 deletions.
7 changes: 7 additions & 0 deletions op-node/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ var (
EnvVars: prefixEnvVars("SEQUENCER_PRIORITY"),
Category: SequencerCategory,
}
SequencerCombinedEngineFlag = &cli.BoolFlag{
Name: "sequencer.combined-engine",
Usage: "Enable sequencer select combined engine api when sealing payload.",
EnvVars: prefixEnvVars("SEQUENCER_COMBINED_ENGINE"),
Category: SequencerCategory,
}
SequencerL1Confs = &cli.Uint64Flag{
Name: "sequencer.l1-confs",
Usage: "Number of L1 blocks to keep distance from the L1 head as a sequencer for picking an L1 origin.",
Expand Down Expand Up @@ -437,6 +443,7 @@ var optionalFlags = []cli.Flag{
SequencerStoppedFlag,
SequencerMaxSafeLagFlag,
SequencerPriorityFlag,
SequencerCombinedEngineFlag,
SequencerL1Confs,
L1EpochPollIntervalFlag,
RuntimeConfigReloadIntervalFlag,
Expand Down
14 changes: 12 additions & 2 deletions op-node/rollup/derive/engine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type ExecEngine interface {
GetPayload(ctx context.Context, payloadInfo eth.PayloadInfo) (*eth.ExecutionPayloadEnvelope, error)
ForkchoiceUpdate(ctx context.Context, state *eth.ForkchoiceState, attr *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error)
NewPayload(ctx context.Context, payload *eth.ExecutionPayload, parentBeaconBlockRoot *common.Hash) (*eth.PayloadStatusV1, error)
SealPayload(ctx context.Context, payloadInfo eth.PayloadInfo, fc *eth.ForkchoiceState) (*eth.SealPayloadResponse, error)
L2BlockRefByLabel(ctx context.Context, label eth.BlockLabel) (eth.L2BlockRef, error)
}

Expand Down Expand Up @@ -84,9 +85,11 @@ type EngineController struct {
buildingInfo eth.PayloadInfo
buildingSafe bool
safeAttrs *AttributesWithParent

combinedAPI bool
}

func NewEngineController(engine ExecEngine, log log.Logger, metrics Metrics, rollupCfg *rollup.Config, syncConfig *sync.Config) *EngineController {
func NewEngineController(engine ExecEngine, log log.Logger, metrics Metrics, rollupCfg *rollup.Config, syncConfig *sync.Config, combinedAPI bool) *EngineController {
syncStatus := syncStatusCL
if syncConfig.SyncMode == sync.ELSync {
syncStatus = syncStatusWillStartEL
Expand All @@ -102,6 +105,7 @@ func NewEngineController(engine ExecEngine, log log.Logger, metrics Metrics, rol
elTriggerGap: syncConfig.ELTriggerGap,
syncStatus: syncStatus,
clock: clock.SystemClock,
combinedAPI: combinedAPI,
}
}

Expand Down Expand Up @@ -267,7 +271,13 @@ func (e *EngineController) ConfirmPayload(ctx context.Context, agossip async.Asy
}
// Update the safe head if the payload is built with the last attributes in the batch.
updateSafe := e.buildingSafe && e.safeAttrs != nil && e.safeAttrs.IsLastInSpan
envelope, errTyp, err := confirmPayload(ctx, e.log, e.engine, fc, e.buildingInfo, updateSafe, agossip, sequencerConductor, e.metrics)

var envelope *eth.ExecutionPayloadEnvelope
if e.combinedAPI {
envelope, errTyp, err = confirmPayloadCombined(ctx, e.log, e.engine, fc, e.buildingInfo, updateSafe, agossip, sequencerConductor, e.metrics)
} else {
envelope, errTyp, err = confirmPayload(ctx, e.log, e.engine, fc, e.buildingInfo, updateSafe, agossip, sequencerConductor, e.metrics)
}
if err != nil {
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)
}
Expand Down
55 changes: 55 additions & 0 deletions op-node/rollup/derive/engine_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,58 @@ func confirmPayload(
"txs", len(payload.Transactions), "update_safe", updateSafe)
return envelope, BlockInsertOK, nil
}

func confirmPayloadCombined(
ctx context.Context,
log log.Logger,
eng ExecEngine,
fc eth.ForkchoiceState,
payloadInfo eth.PayloadInfo,
updateSafe bool,
agossip async.AsyncGossiper,
sequencerConductor conductor.SequencerConductor,
metrics Metrics,
) (out *eth.ExecutionPayloadEnvelope, errTyp BlockInsertionErrType, err error) {
start := time.Now()
sealRes, err := eng.SealPayload(ctx, payloadInfo, &fc)

if err != nil {
var inputErr eth.InputError
if errors.As(err, &inputErr) {
switch inputErr.Code {
case eth.InvalidForkchoiceState:
return nil, BlockInsertPayloadErr, fmt.Errorf("post-block-creation forkchoice update was inconsistent with engine, need reset to resolve: %w", inputErr.Unwrap())
default:
return nil, BlockInsertPrestateErr, fmt.Errorf("unexpected error code in forkchoice-updated response: %w", err)
}
} else {
return nil, BlockInsertTemporaryErr, fmt.Errorf("failed to make the new L2 block canonical via forkchoice: %w", err)
}
}

if sealRes.PayloadStatus.Status != eth.ExecutionValid {
switch sealRes.Stage {
case "newPayload":
if sealRes.PayloadStatus.Status == eth.ExecutionInvalid || sealRes.PayloadStatus.Status == eth.ExecutionInvalidBlockHash {
return nil, BlockInsertPayloadErr, fmt.Errorf("new payload BlockInsertPayloadErr %v", sealRes.PayloadStatus.ValidationError)
}
return nil, BlockInsertTemporaryErr, fmt.Errorf("new payload BlockInsertTemporaryErr %v", sealRes.PayloadStatus.ValidationError)
case "forkchoiceUpdate":
return nil, BlockInsertPayloadErr, fmt.Errorf("forkchoiceUpdate BlockInsertPayloadErr %v", sealRes.PayloadStatus.ValidationError)
}
}

if err := sequencerConductor.CommitUnsafePayload(ctx, sealRes.Payload); err != nil {
return nil, BlockInsertTemporaryErr, fmt.Errorf("failed to commit unsafe payload to conductor: %w", err)
}
agossip.Gossip(sealRes.Payload)
agossip.Clear()

payload := sealRes.Payload.ExecutionPayload
metrics.RecordSequencerStepTime("sealPayload", time.Since(start))
log.Info("inserted block", "hash", payload.BlockHash, "number", uint64(payload.BlockNumber),
"state_root", payload.StateRoot, "timestamp", uint64(payload.Timestamp), "parent", payload.ParentHash,
"prev_randao", payload.PrevRandao, "fee_recipient", payload.FeeRecipient,
"txs", len(payload.Transactions), "update_safe", updateSafe)
return sealRes.Payload, BlockInsertOK, nil
}
2 changes: 2 additions & 0 deletions op-node/rollup/driver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ type Config struct {

// SequencerPriority is true when sequencer step takes precedence over other steps.
SequencerPriority bool `json:"sequencer_priority"`

SequencerCombinedEngine bool `json:"sequencer_combined_engine"`
}
2 changes: 1 addition & 1 deletion op-node/rollup/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ func NewDriver(
sequencerConfDepth := NewConfDepth(driverCfg.SequencerConfDepth, l1State.L1Head, l1)
findL1Origin := NewL1OriginSelector(log, cfg, sequencerConfDepth)
verifConfDepth := NewConfDepth(driverCfg.VerifierConfDepth, l1State.L1Head, l1)
engine := derive.NewEngineController(l2, log, metrics, cfg, syncCfg)
engine := derive.NewEngineController(l2, log, metrics, cfg, syncCfg, driverCfg.SequencerCombinedEngine)
clSync := clsync.NewCLSync(log, cfg, metrics, engine)

var finalizer Finalizer
Expand Down
13 changes: 7 additions & 6 deletions op-node/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,13 @@ func NewConfigPersistence(ctx *cli.Context) node.ConfigPersistence {

func NewDriverConfig(ctx *cli.Context) *driver.Config {
return &driver.Config{
VerifierConfDepth: ctx.Uint64(flags.VerifierL1Confs.Name),
SequencerConfDepth: ctx.Uint64(flags.SequencerL1Confs.Name),
SequencerEnabled: ctx.Bool(flags.SequencerEnabledFlag.Name),
SequencerStopped: ctx.Bool(flags.SequencerStoppedFlag.Name),
SequencerMaxSafeLag: ctx.Uint64(flags.SequencerMaxSafeLagFlag.Name),
SequencerPriority: ctx.Bool(flags.SequencerPriorityFlag.Name),
VerifierConfDepth: ctx.Uint64(flags.VerifierL1Confs.Name),
SequencerConfDepth: ctx.Uint64(flags.SequencerL1Confs.Name),
SequencerEnabled: ctx.Bool(flags.SequencerEnabledFlag.Name),
SequencerStopped: ctx.Bool(flags.SequencerStoppedFlag.Name),
SequencerMaxSafeLag: ctx.Uint64(flags.SequencerMaxSafeLagFlag.Name),
SequencerPriority: ctx.Bool(flags.SequencerPriorityFlag.Name),
SequencerCombinedEngine: ctx.Bool(flags.SequencerCombinedEngineFlag.Name),
}
}

Expand Down
6 changes: 6 additions & 0 deletions op-service/eth/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,12 @@ type ForkchoiceUpdatedResult struct {
PayloadID *PayloadID `json:"payloadId"`
}

type SealPayloadResponse struct {
Stage string `json:"stage"`
PayloadStatus PayloadStatusV1 `json:"payloadStatus"`
Payload *ExecutionPayloadEnvelope `json:"payload"`
}

// SystemConfig represents the rollup system configuration that carries over in every L2 block,
// and may be changed through L1 system config events.
// The initial SystemConfig at rollup genesis is embedded in the rollup configuration.
Expand Down
33 changes: 32 additions & 1 deletion op-service/sources/engine_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (s *EngineAPIClient) NewPayload(ctx context.Context, payload *eth.Execution
e.Trace("Received payload execution result", "status", result.Status, "latestValidHash", result.LatestValidHash, "message", result.ValidationError)
if err != nil {
if strings.Contains(err.Error(), derive.ErrELSyncTriggerUnexpected.Error()) {
result.Status = eth.ExecutionSyncing
result.Status = eth.ExecutionSyncing // why?
return &result, err
}
e.Error("Payload execution failed", "err", err)
Expand Down Expand Up @@ -178,6 +178,37 @@ func (s *EngineAPIClient) GetPayload(ctx context.Context, payloadInfo eth.Payloa
return &result, nil
}

func (s *EngineAPIClient) SealPayload(ctx context.Context, payloadInfo eth.PayloadInfo, fc *eth.ForkchoiceState) (*eth.SealPayloadResponse, error) {
e := s.log.New("payload_id", payloadInfo.ID)
e.Trace("sealing payload")
var result eth.SealPayloadResponse
method := "engine_opSealPayload"
err := s.RPC.CallContext(ctx, &result, string(method), payloadInfo.ID, fc)
if err != nil {
e.Warn("Failed to seal payload", "payload_id", payloadInfo.ID, "err", err)
if rpcErr, ok := err.(rpc.Error); ok {
code := eth.ErrorCode(rpcErr.ErrorCode())
switch code {
case eth.UnknownPayload:
return nil, eth.InputError{
Inner: err,
Code: code,
}
case eth.InvalidForkchoiceState, eth.InvalidPayloadAttributes:
return nil, eth.InputError{
Inner: err,
Code: code,
}
default:
return nil, fmt.Errorf("unrecognized rpc error: %w", err)
}
}
return nil, err
}
e.Trace("Received payload")
return &result, nil
}

func (s *EngineAPIClient) SignalSuperchainV1(ctx context.Context, recommended, required params.ProtocolVersion) (params.ProtocolVersion, error) {
var result params.ProtocolVersion
err := s.RPC.CallContext(ctx, &result, "engine_signalSuperchainV1", &catalyst.SuperchainSignal{
Expand Down

0 comments on commit 769e7c8

Please sign in to comment.