Skip to content

Commit

Permalink
op-node: Add option to only use finalized blocks as l1origin in seque…
Browse files Browse the repository at this point in the history
…ncer (#209)
  • Loading branch information
palango authored Sep 23, 2024
1 parent cb878ed commit ecc4700
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 7 deletions.
8 changes: 8 additions & 0 deletions op-node/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ var (
Value: 4,
Category: SequencerCategory,
}
SequencerUseFinalizedL1Flag = &cli.BoolFlag{
Name: "sequencer.use-finalized",
Usage: "Enable use of only finalized L1 blocks as L1 origin. Overwrites the value of 'sequencer.l1-confs'.",
EnvVars: prefixEnvVars("SEQUENCER_USE_FINALIZED"),
Value: false,
Category: SequencerCategory,
}
L1EpochPollIntervalFlag = &cli.DurationFlag{
Name: "l1.epoch-poll-interval",
Usage: "Poll interval for retrieving new L1 epoch updates such as safe and finalized block changes. Disabled if 0 or negative.",
Expand Down Expand Up @@ -395,6 +402,7 @@ var optionalFlags = []cli.Flag{
L1RPCMaxBatchSize,
L1RPCMaxConcurrency,
L1HTTPPollInterval,
SequencerUseFinalizedL1Flag,
VerifierL1Confs,
SequencerEnabledFlag,
SequencerStoppedFlag,
Expand Down
4 changes: 4 additions & 0 deletions op-node/rollup/driver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ type Config struct {
// SequencerMaxSafeLag is the maximum number of L2 blocks for restricting the distance between L2 safe and unsafe.
// Disabled if 0.
SequencerMaxSafeLag uint64 `json:"sequencer_max_safe_lag"`

// SequencerUseFinalized is true when sequencer should use only finalized L1 blocks as origin.
// If this is set to true, the value of `SequencerConfDepth` is ignored.
SequencerUseFinalized bool `json:"sequencer_use_finalized"`
}
11 changes: 9 additions & 2 deletions op-node/rollup/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/engine"
"github.com/ethereum-optimism/optimism/op-node/rollup/event"
"github.com/ethereum-optimism/optimism/op-node/rollup/finality"
"github.com/ethereum-optimism/optimism/op-node/rollup/finalized"
"github.com/ethereum-optimism/optimism/op-node/rollup/sequencing"
"github.com/ethereum-optimism/optimism/op-node/rollup/status"
"github.com/ethereum-optimism/optimism/op-node/rollup/sync"
Expand Down Expand Up @@ -239,8 +240,14 @@ func NewDriver(
if driverCfg.SequencerEnabled {
asyncGossiper := async.NewAsyncGossiper(driverCtx, network, log, metrics)
attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1, l2)
sequencerConfDepth := confdepth.NewConfDepth(driverCfg.SequencerConfDepth, statusTracker.L1Head, l1)
findL1Origin := sequencing.NewL1OriginSelector(log, cfg, sequencerConfDepth)

var seqL1Blocks sequencing.L1Blocks
if driverCfg.SequencerUseFinalized {
seqL1Blocks = finalized.NewFinalized(statusTracker.L1Finalized, l1)
} else {
seqL1Blocks = confdepth.NewConfDepth(driverCfg.SequencerConfDepth, statusTracker.L1Head, l1)
}
findL1Origin := sequencing.NewL1OriginSelector(log, cfg, seqL1Blocks)
sequencer = sequencing.NewSequencer(driverCtx, log, cfg, attrBuilder, findL1Origin,
sequencerStateListener, sequencerConductor, asyncGossiper, metrics)
sys.Register("sequencer", sequencer, opts)
Expand Down
29 changes: 29 additions & 0 deletions op-node/rollup/finalized/finalized.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package finalized

import (
"context"

"github.com/ethereum/go-ethereum"

"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/eth"
)

type finalized struct {
derive.L1Fetcher
l1Finalized func() eth.L1BlockRef
}

func NewFinalized(l1Finalized func() eth.L1BlockRef, fetcher derive.L1Fetcher) *finalized {
return &finalized{L1Fetcher: fetcher, l1Finalized: l1Finalized}
}

func (f *finalized) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1BlockRef, error) {
l1Finalized := f.l1Finalized()
if num == 0 || num <= l1Finalized.Number {
return f.L1Fetcher.L1BlockRefByNumber(ctx, num)
}
return eth.L1BlockRef{}, ethereum.NotFound
}

var _ derive.L1Fetcher = (*finalized)(nil)
58 changes: 58 additions & 0 deletions op-node/rollup/finalized/finalized_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package finalized

import (
"context"
"testing"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"

"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testutils"
)

var testFinalHash = common.Hash{0x01}

type finalizedTest struct {
name string
final uint64
hash common.Hash // hash of finalized block
req uint64
pass bool
}

func (ft *finalizedTest) Run(t *testing.T) {
l1Fetcher := &testutils.MockL1Source{}
l1Finalized := eth.L1BlockRef{Number: ft.final, Hash: ft.hash}
l1FinalizedGetter := func() eth.L1BlockRef { return l1Finalized }

f := NewFinalized(l1FinalizedGetter, l1Fetcher)

if ft.pass {
// no calls to the l1Fetcher are made if the block number is not finalized yet
l1Fetcher.ExpectL1BlockRefByNumber(ft.req, eth.L1BlockRef{Number: ft.req}, nil)
}

out, err := f.L1BlockRefByNumber(context.Background(), ft.req)
l1Fetcher.AssertExpectations(t)

if ft.pass {
require.NoError(t, err)
require.Equal(t, out, eth.L1BlockRef{Number: ft.req})
} else {
require.Equal(t, ethereum.NotFound, err)
}
}

func TestFinalized(t *testing.T) {
testCases := []finalizedTest{
{name: "finalized", final: 10, hash: testFinalHash, req: 10, pass: true},
{name: "finalized past", final: 10, hash: testFinalHash, req: 8, pass: true},
{name: "not finalized", final: 10, hash: testFinalHash, req: 11, pass: false},
{name: "no L1 state", req: 10, pass: false},
}
for _, tc := range testCases {
t.Run(tc.name, tc.Run)
}
}
5 changes: 5 additions & 0 deletions op-node/rollup/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,8 @@ func (st *StatusTracker) SyncStatus() *eth.SyncStatus {
func (st *StatusTracker) L1Head() eth.L1BlockRef {
return st.SyncStatus().HeadL1
}

// L1Finalized is a helper function to get the latest known finalized L1 block.
func (st *StatusTracker) L1Finalized() eth.L1BlockRef {
return st.SyncStatus().FinalizedL1
}
11 changes: 6 additions & 5 deletions op-node/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,12 @@ 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),
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),
SequencerUseFinalized: ctx.Bool(flags.SequencerUseFinalizedL1Flag.Name),
}
}

Expand Down

0 comments on commit ecc4700

Please sign in to comment.