diff --git a/CHANGELOG.md b/CHANGELOG.md index dedf0313f..fa54b2c34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # Changelog + +## v0.5.5 + +This release confirm the time of Mainnet Fourier Hardfork, effectively reducing the block time from 500 milliseconds to an impressive 250 milliseconds. + +- Mainnet: Jan-07-2026 03:00:00 AM +UTC + +All mainnet and nodes must upgrade to this release before the hardfork time. +Also note that the `op-geth` should be upgraded to v0.5.9 accordingly, check [this](https://github.com/bnb-chain/op-geth/releases/tag/v0.5.9) for more details. + +### What's Changed + +* [\#319](https://github.com/bnb-chain/opbnb/pull/319) feat: replace l1 head with l1 finalized block as sequencer and derivation head +* [\#320](https://github.com/bnb-chain/opbnb/pull/320) fix: support compile opbnb in golang1.24.x and Windows OS +* [\#324](https://github.com/bnb-chain/opbnb/pull/324) fix: safe stop during long time stop +* [\#325](https://github.com/bnb-chain/opbnb/pull/325) fix: p2p node derivation occur empty root +* [\#326](https://github.com/bnb-chain/opbnb/pull/326) fix: solve ref-metrics map concurrent use +* [\#328](https://github.com/bnb-chain/opbnb/pull/328) fix: add Mainnet Fourier hardfork timestamp + +### Docker Images + +- ghcr.io/bnb-chain/op-node:v0.5.5 +- ghcr.io/bnb-chain/op-batcher:v0.5.5 +- ghcr.io/bnb-chain/op-proposer:v0.5.5 + +**Full Changelog**: https://github.com/bnb-chain/opbnb/compare/v0.5.4...v0.5.5 + + ## v0.5.4 This release introduces the implementation of Fourier Hardfork, effectively reducing the block time from 500 milliseconds to an impressive 250 milliseconds. diff --git a/go.mod b/go.mod index 742137179..10dbf5c82 100644 --- a/go.mod +++ b/go.mod @@ -109,7 +109,6 @@ require ( github.com/fatih/color v1.15.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/ferranbt/fastssz v0.0.0-20210905181407-59cf6761a7d5 // indirect - github.com/fjl/memsize v0.0.2 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect @@ -247,7 +246,7 @@ require ( github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/status-im/keycard-go v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/supranational/blst v0.3.11 // indirect + github.com/supranational/blst v0.3.14 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect @@ -277,7 +276,7 @@ require ( rsc.io/tmplfunc v0.0.3 // indirect ) -replace github.com/ethereum/go-ethereum v1.13.15 => github.com/bnb-chain/op-geth v1.101315.2-0.0.20250418091555-2b39d61b7cbf +replace github.com/ethereum/go-ethereum v1.13.15 => github.com/bnb-chain/op-geth v1.101315.2-0.0.20251118081547-f1c2b5af43c5 replace github.com/cometbft/cometbft => github.com/bnb-chain/greenfield-cometbft v1.0.0 diff --git a/go.sum b/go.sum index cdea5c24d..e5a58b702 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,8 @@ github.com/bnb-chain/fastssz v0.1.2 h1:vTcXw5SwCtRYnl/BEclujiml7GXiVOZ74tub4GHpv github.com/bnb-chain/fastssz v0.1.2/go.mod h1:KcabV+OEw2QwgyY8Fc88ZG79CKYkFdu0kKWyfA3dI6o= github.com/bnb-chain/greenfield-cometbft v1.0.0 h1:0r6hOJWD/+es0gxP/exKuN/krgXAr3LCn5/XlcgDWr8= github.com/bnb-chain/greenfield-cometbft v1.0.0/go.mod h1:f35mk/r5ab6yvzlqEWZt68LfUje68sYgMpVlt2CUYMk= -github.com/bnb-chain/op-geth v1.101315.2-0.0.20250418091555-2b39d61b7cbf h1:7Ry/NtfhCFelZRVhw5cPwveX8uVFchalL+qhUcn5CMA= -github.com/bnb-chain/op-geth v1.101315.2-0.0.20250418091555-2b39d61b7cbf/go.mod h1:hyHrrcHkUe3lRwfJs+JGrbOHp+pRdheRk+ren4TPhF8= +github.com/bnb-chain/op-geth v1.101315.2-0.0.20251118081547-f1c2b5af43c5 h1:FVG+wugCxufwMTL7HdnaxUDWa7AGAK4TkQjNTFRL/RM= +github.com/bnb-chain/op-geth v1.101315.2-0.0.20251118081547-f1c2b5af43c5/go.mod h1:T1sVGwAA96KwS72B62aRTvlM/a+byeIAx9Akh18eWDQ= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -404,8 +404,6 @@ github.com/ferranbt/fastssz v0.0.0-20210120143747-11b9eff30ea9/go.mod h1:DyEu2iu github.com/ferranbt/fastssz v0.0.0-20210905181407-59cf6761a7d5 h1:6dVcS0LktRSyEEgldFY4N9J17WjUoiJStttH+RZj0Wo= github.com/ferranbt/fastssz v0.0.0-20210905181407-59cf6761a7d5/go.mod h1:S8yiDeAXy8f88W4Ul+0dBMPx49S05byYbmZD6Uv94K4= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= -github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= @@ -1621,8 +1619,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/supranational/blst v0.3.5/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= +github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index 0df382ac1..76eebc311 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -330,7 +330,7 @@ func (l *BatchSubmitter) loop() { case <-economicDATicker.C: newEconomicDAType, err := l.getEconomicDAType(l.shutdownCtx) if err != nil { - l.Log.Error("getEconomicDAType failed: %w", err) + l.Log.Error("Failed to get economic DA type", "err", err) continue } if newEconomicDAType != economicDAType { diff --git a/op-e2e/actions/l2_verifier.go b/op-e2e/actions/l2_verifier.go index 7130f943b..291362a17 100644 --- a/op-e2e/actions/l2_verifier.go +++ b/op-e2e/actions/l2_verifier.go @@ -82,7 +82,7 @@ func NewL2Verifier(t Testing, log log.Logger, l1 derive.L1Fetcher, blobsSrc deri finalizer = finality.NewFinalizer(log, cfg, l1, engine) } - attributesHandler := attributes.NewAttributesHandler(log, cfg, engine, eng) + attributesHandler := attributes.NewAttributesHandler(log, cfg, engine, eng, false) pipeline := derive.NewDerivationPipeline(log, cfg, l1, blobsSrc, plasmaSrc, eng, engine, metrics, syncCfg, safeHeadListener, finalizer, attributesHandler) diff --git a/op-node/chaincfg/chains.go b/op-node/chaincfg/chains.go index 9ee8bbc97..75a29cbe5 100644 --- a/op-node/chaincfg/chains.go +++ b/op-node/chaincfg/chains.go @@ -144,6 +144,7 @@ var OPBNBMainnet = rollup.Config{ EcotoneTime: u64Ptr(1718871600), // Jun-20-2024 08:20 AM +UTC FjordTime: u64Ptr(1727157600), // Sep-24-2024 06:00 AM +UTC VoltaTime: u64Ptr(1745204400), // Apr-21-2025 03:00 AM +UTC + FourierTime: u64Ptr(1767754800), // Jan-07-2026 03:00 AM +UTC } var OPBNBTestnet = rollup.Config{ diff --git a/op-node/flags/flags.go b/op-node/flags/flags.go index 811e0fba2..0dbd9c4d2 100644 --- a/op-node/flags/flags.go +++ b/op-node/flags/flags.go @@ -226,6 +226,13 @@ var ( Value: 15, Category: L1RPCCategory, } + L1FinalizedConfDepth = &cli.BoolFlag{ + Name: "l1-finalized-confs", + Usage: "Use L1 finalized block as the latest head for opBNB sequencer and derivation. When enabled, verifier.l1-confs and sequencer.l1-confs will be ignored.", + EnvVars: prefixEnvVars("L1_FINALIZED_CONFS"), + Value: false, + Category: L1RPCCategory, + } SequencerEnabledFlag = &cli.BoolFlag{ Name: "sequencer.enabled", Usage: "Enable sequencing of new L2 blocks. A separate batch submitter has to be deployed to publish the data for verifiers.", @@ -268,7 +275,7 @@ var ( 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.", EnvVars: prefixEnvVars("L1_EPOCH_POLL_INTERVAL"), - Value: time.Second * 3 * 15, + Value: time.Second * 1, Category: L1RPCCategory, } RuntimeConfigReloadIntervalFlag = &cli.DurationFlag{ @@ -409,6 +416,13 @@ var ( Value: time.Second * 1, Category: SequencerCategory, } + IsP2PNodeFlag = &cli.BoolFlag{ + Name: "l2.p2p-node", + Usage: "active the op-geth a P2P node.", + EnvVars: prefixEnvVars("L2_P2P_NODE"), + Value: false, + Category: OperationsCategory, + } ) var requiredFlags = []cli.Flag{ @@ -439,6 +453,8 @@ var optionalFlags = []cli.Flag{ L1BlobRpcRateLimit, L1BlobRpcMaxBatchSize, VerifierL1Confs, + L1FinalizedConfDepth, + IsP2PNodeFlag, SequencerEnabledFlag, SequencerStoppedFlag, SequencerMaxSafeLagFlag, diff --git a/op-node/rollup/attributes/attributes.go b/op-node/rollup/attributes/attributes.go index a2ca56ff1..c180d4060 100644 --- a/op-node/rollup/attributes/attributes.go +++ b/op-node/rollup/attributes/attributes.go @@ -42,15 +42,19 @@ type AttributesHandler struct { ec Engine l2 L2 + l2P2PNode bool + attributes *derive.AttributesWithParent } -func NewAttributesHandler(log log.Logger, cfg *rollup.Config, ec Engine, l2 L2) *AttributesHandler { +func NewAttributesHandler(log log.Logger, cfg *rollup.Config, ec Engine, l2 L2, l2P2PNode bool) *AttributesHandler { + log.Info("new attributes handler", "l2_p2p_node", l2P2PNode) return &AttributesHandler{ log: log, cfg: cfg, ec: ec, l2: l2, + l2P2PNode: l2P2PNode, attributes: nil, } } @@ -93,6 +97,12 @@ func (eq *AttributesHandler) Proceed(ctx context.Context) error { eq.attributes = nil return nil } else if eq.ec.PendingSafeL2Head().Number == eq.ec.UnsafeL2Head().Number { + if eq.l2P2PNode { + eq.log.Warn("pending_safe_l2_head_number is equal to unsafe_l2_head_number for p2p node waiting l2 block from gossip", + "p2p_node", eq.l2P2PNode, "pending_safe_l2_head_number", eq.ec.PendingSafeL2Head().Number, + "unsafe_l2_head_number", eq.ec.UnsafeL2Head().Number) + return nil + } if err := eq.forceNextSafeAttributes(ctx, eq.attributes); err != nil { return err } diff --git a/op-node/rollup/attributes/attributes_test.go b/op-node/rollup/attributes/attributes_test.go index 99510c485..e36ca60aa 100644 --- a/op-node/rollup/attributes/attributes_test.go +++ b/op-node/rollup/attributes/attributes_test.go @@ -182,7 +182,7 @@ func TestAttributesHandler(t *testing.T) { logger := testlog.Logger(t, log.LevelInfo) eng := &testutils.MockEngine{} ec := derive.NewEngineController(eng, logger, metrics.NoopMetrics, cfg, &sync.Config{SyncMode: sync.CLSync}, false) - ah := NewAttributesHandler(logger, cfg, ec, eng) + ah := NewAttributesHandler(logger, cfg, ec, eng, false) defer eng.AssertExpectations(t) ec.SetPendingSafeL2Head(refA1Alt) @@ -196,7 +196,7 @@ func TestAttributesHandler(t *testing.T) { logger := testlog.Logger(t, log.LevelInfo) eng := &testutils.MockEngine{} ec := derive.NewEngineController(eng, logger, metrics.NoopMetrics, cfg, &sync.Config{SyncMode: sync.CLSync}, false) - ah := NewAttributesHandler(logger, cfg, ec, eng) + ah := NewAttributesHandler(logger, cfg, ec, eng, false) defer eng.AssertExpectations(t) ec.SetPendingSafeL2Head(refA0Alt) @@ -211,7 +211,7 @@ func TestAttributesHandler(t *testing.T) { logger := testlog.Logger(t, log.LevelInfo) eng := &testutils.MockEngine{} ec := derive.NewEngineController(eng, logger, metrics.NoopMetrics, cfg, &sync.Config{SyncMode: sync.CLSync}, false) - ah := NewAttributesHandler(logger, cfg, ec, eng) + ah := NewAttributesHandler(logger, cfg, ec, eng, false) ec.SetUnsafeHead(refA1) ec.SetSafeHead(refA0) @@ -265,7 +265,7 @@ func TestAttributesHandler(t *testing.T) { logger := testlog.Logger(t, log.LevelInfo) eng := &testutils.MockEngine{} ec := derive.NewEngineController(eng, logger, metrics.NoopMetrics, cfg, &sync.Config{SyncMode: sync.CLSync}, false) - ah := NewAttributesHandler(logger, cfg, ec, eng) + ah := NewAttributesHandler(logger, cfg, ec, eng, false) ec.SetUnsafeHead(refA1) ec.SetSafeHead(refA0) @@ -324,7 +324,7 @@ func TestAttributesHandler(t *testing.T) { logger := testlog.Logger(t, log.LevelInfo) eng := &testutils.MockEngine{} ec := derive.NewEngineController(eng, logger, metrics.NoopMetrics, cfg, &sync.Config{SyncMode: sync.CLSync}, false) - ah := NewAttributesHandler(logger, cfg, ec, eng) + ah := NewAttributesHandler(logger, cfg, ec, eng, false) ec.SetUnsafeHead(refA0) ec.SetSafeHead(refA0) @@ -375,7 +375,7 @@ func TestAttributesHandler(t *testing.T) { logger := testlog.Logger(t, log.LevelInfo) eng := &testutils.MockEngine{} ec := derive.NewEngineController(eng, logger, metrics.NoopMetrics, cfg, &sync.Config{SyncMode: sync.CLSync}, false) - ah := NewAttributesHandler(logger, cfg, ec, eng) + ah := NewAttributesHandler(logger, cfg, ec, eng, false) ec.SetUnsafeHead(refA0) ec.SetSafeHead(refA0) @@ -399,7 +399,7 @@ func TestAttributesHandler(t *testing.T) { logger := testlog.Logger(t, log.LevelInfo) eng := &testutils.MockEngine{} ec := derive.NewEngineController(eng, logger, metrics.NoopMetrics, cfg, &sync.Config{SyncMode: sync.CLSync}, false) - ah := NewAttributesHandler(logger, cfg, ec, eng) + ah := NewAttributesHandler(logger, cfg, ec, eng, false) defer eng.AssertExpectations(t) require.Equal(t, ah.Proceed(context.Background()), io.EOF, "no attributes to process") diff --git a/op-node/rollup/derive/batches.go b/op-node/rollup/derive/batches.go index 6ae24aae0..07fe80813 100644 --- a/op-node/rollup/derive/batches.go +++ b/op-node/rollup/derive/batches.go @@ -216,39 +216,39 @@ func checkSpanBatch(ctx context.Context, cfg *rollup.Config, log log.Logger, l1B if batch.GetTimestamp() < nextMilliTimestamp { if batch.GetTimestamp() > l2SafeHead.MillisecondTimestamp() { // batch timestamp cannot be between safe head and next timestamp - log.Warn("batch has misaligned timestamp, block time is too short") + log.Warn("batch has misaligned timestamp, block time is too short", "batch_timestamp", batch.GetTimestamp(), "next_timestamp", nextMilliTimestamp, "l2_safe_head_timestamp", l2SafeHead.MillisecondTimestamp(), "parent_block", parentBlock) return BatchDrop } milliSecondsDistance := l2SafeHead.MillisecondTimestamp() - batch.GetTimestamp() if !cfg.IsFourier(l2SafeHead.MillisecondTimestamp() / 1000) { if milliSecondsDistance%rollup.MillisecondBlockIntervalVolta != 0 { - log.Warn("batch has misaligned timestamp, not overlapped exactly") + log.Warn("batch has misaligned timestamp, not overlapped exactly", "batch_timestamp", batch.GetTimestamp(), "next_timestamp", nextMilliTimestamp, "l2_safe_head_timestamp", l2SafeHead.MillisecondTimestamp(), "parent_block", parentBlock) return BatchDrop } } else { // block interval has changed after fourier fork if milliSecondsDistance%rollup.MillisecondBlockIntervalFourier != 0 { - log.Warn("batch has misaligned timestamp after fourier fork, not overlapped exactly") + log.Warn("batch has misaligned timestamp after fourier fork, not overlapped exactly", "batch_timestamp", batch.GetTimestamp(), "next_timestamp", nextMilliTimestamp, "l2_safe_head_timestamp", l2SafeHead.MillisecondTimestamp(), "parent_block", parentBlock) return BatchDrop } } currentNum, err := cfg.TargetBlockNumber(batch.GetTimestamp()) if err != nil { - log.Warn("failed to computer batch number", "batch_ms_time", batch.GetTimestamp(), "err", err) + log.Warn("failed to computer batch number", "batch_ms_time", batch.GetTimestamp(), "err", err, "parent_block", parentBlock) // unable to validate the batch for now. retry later. return BatchUndecided } parentNum = currentNum - 1 parentBlock, err = l2Fetcher.L2BlockRefByNumber(ctx, parentNum) if err != nil { - log.Warn("failed to fetch L2 block", "number", parentNum, "err", err) + log.Warn("failed to fetch L2 block", "number", parentNum, "err", err, "parent_block", parentBlock) // unable to validate the batch for now. retry later. return BatchUndecided } } if !batch.CheckParentHash(parentBlock.Hash) { - log.Warn("ignoring batch with mismatching parent hash", "parent_block", parentBlock.Hash) + log.Warn("ignoring batch with mismatching parent hash", "parent_block", parentBlock, "l2_safe_head", l2SafeHead) return BatchDrop } diff --git a/op-node/rollup/driver/conf_depth.go b/op-node/rollup/driver/conf_depth.go index 194692bf3..7de4ef18c 100644 --- a/op-node/rollup/driver/conf_depth.go +++ b/op-node/rollup/driver/conf_depth.go @@ -4,6 +4,7 @@ import ( "context" "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -43,3 +44,41 @@ func (c *confDepth) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1B } var _ derive.L1Fetcher = (*confDepth)(nil) + +// confDepth is an util that wraps the L1 input fetcher used in the pipeline, +// and hides the part of the L1 chain with insufficient confirmations. +// +// At 0 depth the l1 head is completely ignored. +type confDepthByL1Finalized struct { + // everything fetched by hash is trusted already, so we implement those by embedding the fetcher + derive.L1Fetcher + l1Finalized func() eth.L1BlockRef + depth uint64 +} + +func NewConfDepthByL1Finalized(depth uint64, l1Finalized func() eth.L1BlockRef, fetcher derive.L1Fetcher) *confDepthByL1Finalized { + return &confDepthByL1Finalized{L1Fetcher: fetcher, l1Finalized: l1Finalized, depth: depth} +} + +// L1BlockRefByNumber is used for L1 traversal and for finding a safe common point between the L2 engine and L1 chain. +// Any block numbers that are within confirmation depth of the L1 head are mocked to be "not found", +// effectively hiding the uncertain part of the L1 chain. +func (c *confDepthByL1Finalized) L1BlockRefByNumber(ctx context.Context, num uint64) (eth.L1BlockRef, error) { + // TODO: performance optimization: buffer the l1Unsafe, invalidate any reorged previous buffer content, + // and instantly return the origin by number from the buffer if we can. + + // Don't apply the conf depth if l1Head is empty (as it is during the startup case before the l1State is initialized). + l1Finalized := c.l1Finalized() + if l1Finalized == (eth.L1BlockRef{}) { + // if l1Finalized is empty, wait for it to be set, temporarily return not found + log.Warn("Conf depth is waiting for L1 finalized block to be set") + return eth.L1BlockRef{}, ethereum.NotFound + } + + if num+c.depth <= l1Finalized.Number { + return c.L1Fetcher.L1BlockRefByNumber(ctx, num) + } + return eth.L1BlockRef{}, ethereum.NotFound +} + +var _ derive.L1Fetcher = (*confDepthByL1Finalized)(nil) diff --git a/op-node/rollup/driver/config.go b/op-node/rollup/driver/config.go index 42ebe6f63..eecade746 100644 --- a/op-node/rollup/driver/config.go +++ b/op-node/rollup/driver/config.go @@ -1,7 +1,12 @@ package driver type Config struct { + // L1FinalizedConfDepth enables using L1 finalized block as the latest head for opBNB sequencer and derivation. + // When enabled, VerifierConfDepth and SequencerConfDepth will be ignored. + L1FinalizedConfDepth bool `json:"l1_finalized_conf_depth"` + // VerifierConfDepth is the distance to keep from the L1 head when reading L1 data for L2 derivation. + // Ignored when L1FinalizedConfDepth is enabled. VerifierConfDepth uint64 `json:"verifier_conf_depth"` // SequencerConfDepth is the distance to keep from the L1 head as origin when sequencing new L2 blocks. @@ -9,6 +14,7 @@ type Config struct { // - not adopt a L1 origin within the allowed time (rollup.Config.MaxSequencerDrift) // - not adopt a L1 origin that can be included on L1 within the allowed range (rollup.Config.SeqWindowSize) // and thus fail to produce a block with anything more than deposits. + // Ignored when L1FinalizedConfDepth is enabled. SequencerConfDepth uint64 `json:"sequencer_conf_depth"` // SequencerEnabled is true when the driver should sequence new blocks. @@ -25,4 +31,7 @@ type Config struct { SequencerPriority bool `json:"sequencer_priority"` SequencerCombinedEngine bool `json:"sequencer_combined_engine"` + + // L2P2PNode is true when the op-geth is a P2P node. + L2P2PNode bool `json:"l2_p2p_node"` } diff --git a/op-node/rollup/driver/driver.go b/op-node/rollup/driver/driver.go index 7b127eccb..cdf89dbfe 100644 --- a/op-node/rollup/driver/driver.go +++ b/op-node/rollup/driver/driver.go @@ -154,9 +154,18 @@ func NewDriver( ) *Driver { l1 = NewMeteredL1Fetcher(l1, metrics) l1State := NewL1State(log, metrics) - sequencerConfDepth := NewConfDepth(driverCfg.SequencerConfDepth, l1State.L1Head, l1) + + var sequencerConfDepth derive.L1Fetcher + var verifConfDepth derive.L1Fetcher + if driverCfg.L1FinalizedConfDepth { + sequencerConfDepth = NewConfDepthByL1Finalized(driverCfg.SequencerConfDepth, l1State.L1Finalized, l1) + verifConfDepth = NewConfDepthByL1Finalized(driverCfg.VerifierConfDepth, l1State.L1Finalized, l1) + } else { + sequencerConfDepth = NewConfDepth(driverCfg.SequencerConfDepth, l1State.L1Head, l1) + verifConfDepth = NewConfDepth(driverCfg.VerifierConfDepth, l1State.L1Head, l1) + } + findL1Origin := NewL1OriginSelector(log, cfg, sequencerConfDepth) - verifConfDepth := NewConfDepth(driverCfg.VerifierConfDepth, l1State.L1Head, l1) engine := derive.NewEngineController(l2, log, metrics, cfg, syncCfg, driverCfg.SequencerCombinedEngine) clSync := clsync.NewCLSync(log, cfg, metrics, engine) @@ -167,7 +176,7 @@ func NewDriver( finalizer = finality.NewFinalizer(log, cfg, l1, engine) } - attributesHandler := attributes.NewAttributesHandler(log, cfg, engine, l2) + attributesHandler := attributes.NewAttributesHandler(log, cfg, engine, l2, driverCfg.L2P2PNode) derivationPipeline := derive.NewDerivationPipeline(log, cfg, verifConfDepth, l1Blobs, plasma, l2, engine, metrics, syncCfg, safeHeadListener, finalizer, attributesHandler) attrBuilder := derive.NewFetchingAttributesBuilder(cfg, l1, l2) diff --git a/op-node/rollup/driver/l1_state.go b/op-node/rollup/driver/l1_state.go index aa8a85abd..a931fd96c 100644 --- a/op-node/rollup/driver/l1_state.go +++ b/op-node/rollup/driver/l1_state.go @@ -60,6 +60,11 @@ func (s *L1State) HandleNewL1SafeBlock(safe eth.L1BlockRef) { func (s *L1State) HandleNewL1FinalizedBlock(finalized eth.L1BlockRef) { s.log.Info("New L1 finalized block", "l1_finalized", finalized) s.metrics.RecordL1Ref("l1_finalized", finalized) + + if s.l1Finalized.Number > finalized.Number { + s.log.Error("New L1 finalized block is less than the current finalized block", "l1_finalized", finalized, "current_l1_finalized", s.l1Finalized) + } + s.l1Finalized = finalized } diff --git a/op-node/rollup/driver/state.go b/op-node/rollup/driver/state.go index 10501f6d9..ca4d042f2 100644 --- a/op-node/rollup/driver/state.go +++ b/op-node/rollup/driver/state.go @@ -202,6 +202,8 @@ func (s *Driver) eventLoop() { defer s.driverCancel() + go s.l1FinalizedEventLoop() + // stepReqCh is used to request that the driver attempts to step forward by one L1 block. stepReqCh := make(chan struct{}, 1) @@ -250,6 +252,7 @@ func (s *Driver) eventLoop() { if len(sequencerCh) > 0 { // empty if not already drained before resetting <-sequencerCh } + log.Info("Plan sequencer action", "delay", delay) sequencerTimer.Reset(delay) } @@ -409,12 +412,12 @@ func (s *Driver) eventLoop() { case newL1Safe := <-s.l1SafeSig: s.l1State.HandleNewL1SafeBlock(newL1Safe) // no step, justified L1 information does not do anything for L2 derivation or status - case newL1Finalized := <-s.l1FinalizedSig: - s.l1State.HandleNewL1FinalizedBlock(newL1Finalized) - ctx, cancel := context.WithTimeout(s.driverCtx, time.Second*5) - s.finalizer.Finalize(ctx, newL1Finalized) - cancel() - reqStep() // we may be able to mark more L2 data as finalized now + // case newL1Finalized := <-s.l1FinalizedSig: + // s.l1State.HandleNewL1FinalizedBlock(newL1Finalized) + // ctx, cancel := context.WithTimeout(s.driverCtx, time.Second*5) + // s.finalizer.Finalize(ctx, newL1Finalized) + // cancel() + // reqStep() // we may be able to mark more L2 data as finalized now case <-delayedStepReq: delayedStepReq = nil step() @@ -507,6 +510,20 @@ func (s *Driver) eventLoop() { } } +func (s *Driver) l1FinalizedEventLoop() { + for { + select { + case newL1Finalized := <-s.l1FinalizedSig: + s.l1State.HandleNewL1FinalizedBlock(newL1Finalized) + ctx, cancel := context.WithTimeout(s.driverCtx, time.Second*5) + s.finalizer.Finalize(ctx, newL1Finalized) + cancel() + case <-s.driverCtx.Done(): + return + } + } +} + func (s *Driver) syncStep(ctx context.Context) error { // If we don't need to call FCU to restore unsafeHead using backupUnsafe, keep going b/c // this was a no-op(except correcting invalid state when backupUnsafe is empty but TryBackupUnsafeReorg called). diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go index 5355cea05..5f1f946ac 100644 --- a/op-node/rollup/types.go +++ b/op-node/rollup/types.go @@ -215,11 +215,11 @@ func (c *Config) VoltaBlockNumber() int64 { // FourierBlockNumber return fourier block number func (c *Config) FourierBlockNumber() int64 { voltaBlockNumber := c.VoltaBlockNumber() - if voltaBlockNumber > 0 { + if voltaBlockNumber >= 0 { if c.FourierTime == nil || *c.FourierTime < c.Genesis.L2Time { return -1 } - return voltaBlockNumber + int64((*c.FourierTime-*c.VoltaTime)/MillisecondBlockIntervalVolta) + return voltaBlockNumber + int64((*c.FourierTime*1000-*c.VoltaTime*1000)/MillisecondBlockIntervalVolta) } else { return -1 } @@ -270,41 +270,62 @@ func (cfg *Config) ValidateL2Config(ctx context.Context, client L2Client, skipL2 func (cfg *Config) MillisecondTimestampForBlock(blockNumber uint64) uint64 { voltaBlockNumber := cfg.VoltaBlockNumber() - if voltaBlockNumber < 0 { // not active volta hardfork - return cfg.Genesis.L2Time*1000 + (blockNumber-cfg.Genesis.L2.Number)*cfg.BlockTime*1000 - } else if voltaBlockNumber == 0 { // active volta hardfork in genesis - return *cfg.VoltaTime*1000 + blockNumber*MillisecondBlockIntervalVolta - } else if blockNumber <= uint64(voltaBlockNumber) { // block number before volta hardfork + + // Volta not active or block number before Volta hardfork, use original block time + if voltaBlockNumber < 0 || blockNumber <= uint64(voltaBlockNumber) { return cfg.Genesis.L2Time*1000 + (blockNumber-cfg.Genesis.L2.Number)*cfg.BlockTime*1000 + } + + // Volta is active, calculate time from Volta activation + voltaBaseTime := *cfg.VoltaTime * 1000 + + // Calculate blocks since Volta activation + // If Volta activates at genesis (voltaBlockNumber == 0), use Genesis.L2.Number as base + // Otherwise, use voltaBlockNumber as base + var blocksSinceVolta uint64 + if voltaBlockNumber == 0 { + blocksSinceVolta = blockNumber - cfg.Genesis.L2.Number } else { - // After Volta: default to 500ms cadence, but switch to 250ms after Fourier - fourierBlockNumber := cfg.FourierBlockNumber() - if fourierBlockNumber < 0 || blockNumber <= uint64(fourierBlockNumber) { - return *cfg.VoltaTime*1000 + (blockNumber-uint64(voltaBlockNumber))*MillisecondBlockIntervalVolta - } - // Time at Fourier boundary, then 250ms cadence afterwards - boundaryMs := *cfg.VoltaTime*1000 + (uint64(fourierBlockNumber)-uint64(voltaBlockNumber))*MillisecondBlockIntervalVolta - return boundaryMs + (blockNumber-uint64(fourierBlockNumber))*MillisecondBlockIntervalFourier + blocksSinceVolta = blockNumber - uint64(voltaBlockNumber) } + + fourierBlockNumber := cfg.FourierBlockNumber() + // Check Fourier activation + if fourierBlockNumber < 0 || blockNumber <= uint64(fourierBlockNumber) { + // Fourier not active, use Volta's 500ms cadence + return voltaBaseTime + blocksSinceVolta*MillisecondBlockIntervalVolta + } + + // After Fourier activation, use Fourier's 250ms cadence + // Calculate blocks from Volta to Fourier + var blocksToFourier uint64 + if voltaBlockNumber == 0 { + blocksToFourier = uint64(fourierBlockNumber) - cfg.Genesis.L2.Number + } else { + blocksToFourier = uint64(fourierBlockNumber) - uint64(voltaBlockNumber) + } + boundaryMs := voltaBaseTime + blocksToFourier*MillisecondBlockIntervalVolta + return boundaryMs + (blockNumber-uint64(fourierBlockNumber))*MillisecondBlockIntervalFourier } func (cfg *Config) TargetBlockNumber(milliTimestamp uint64) (num uint64, err error) { + genesisMilliTimestamp := cfg.Genesis.L2Time * 1000 + if milliTimestamp < genesisMilliTimestamp { + return 0, fmt.Errorf("did not reach genesis time (%d) yet", genesisMilliTimestamp) + } + voltaBlockNumber := cfg.VoltaBlockNumber() if voltaBlockNumber < 0 || milliTimestamp < *cfg.VoltaTime*1000 { // subtract genesis time from timestamp to get the time elapsed since genesis, and then divide that // difference by the block time to get the expected L2 block number at the current time. If the // unsafe head does not have this block number, then there is a gap in the queue. - genesisMilliTimestamp := cfg.Genesis.L2Time * 1000 - if milliTimestamp < genesisMilliTimestamp { - return 0, fmt.Errorf("did not reach genesis time (%d) yet", genesisMilliTimestamp) - } wallClockGenesisDiff := milliTimestamp - genesisMilliTimestamp // Note: round down, we should not request blocks into the future. blocksSinceGenesis := wallClockGenesisDiff / (cfg.BlockTime * 1000) return cfg.Genesis.L2.Number + blocksSinceGenesis, nil } else { fourierBlockNumber := cfg.FourierBlockNumber() - if fourierBlockNumber > 0 && milliTimestamp >= *cfg.FourierTime*1000 { + if fourierBlockNumber >= 0 && milliTimestamp >= *cfg.FourierTime*1000 { // Fourier fork is active fourierMilliTimestamp := *cfg.FourierTime * 1000 wallClockFourierDiff := milliTimestamp - fourierMilliTimestamp diff --git a/op-node/rollup/types_test.go b/op-node/rollup/types_test.go index 62b0212c9..779b21317 100644 --- a/op-node/rollup/types_test.go +++ b/op-node/rollup/types_test.go @@ -569,6 +569,219 @@ func TestTimestampForBlock(t *testing.T) { } } +func TestMillisecondTimestampForBlock(t *testing.T) { + tests := []struct { + name string + genesisL2Time uint64 + genesisL2Number uint64 + blockTime uint64 + voltaTime *uint64 + fourierTime *uint64 + blockNumber uint64 + expectedMilliTime uint64 + description string + }{ + // Case 1: Volta not active + { + name: "VoltaNotActive_GenesisBlock", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: nil, + fourierTime: nil, + blockNumber: 0, + expectedMilliTime: 1000000, // 1000 * 1000 + description: "Volta not active, genesis block", + }, + { + name: "VoltaNotActive_AfterGenesis", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: nil, + fourierTime: nil, + blockNumber: 5, + expectedMilliTime: 1005000, // 1000*1000 + 5*1*1000 + description: "Volta not active, block 5", + }, + { + name: "VoltaNotActive_NonZeroGenesis", + genesisL2Time: 1000, + genesisL2Number: 100, + blockTime: 1, + voltaTime: nil, + fourierTime: nil, + blockNumber: 105, + expectedMilliTime: 1005000, // 1000*1000 + (105-100)*1*1000 + description: "Volta not active, genesis block 100, query block 105", + }, + + // Case 2: Volta activates at genesis (voltaBlockNumber == 0) + { + name: "VoltaAtGenesis_FourierNotActive_GenesisBlock", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(1000), // Same as genesis + fourierTime: nil, + blockNumber: 0, + expectedMilliTime: 1000000, // 1000*1000 + (0-0)*500 + description: "Volta at genesis, Fourier not active, genesis block", + }, + { + name: "VoltaAtGenesis_FourierNotActive_AfterGenesis", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(1000), + fourierTime: nil, + blockNumber: 10, + expectedMilliTime: 1005000, // 1000*1000 + (10-0)*500 = 1000000 + 5000 + description: "Volta at genesis, Fourier not active, block 10 (500ms cadence)", + }, + { + name: "VoltaAtGenesis_FourierAtGenesis_GenesisBlock", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(1000), + fourierTime: uint64Ptr(1000), // Same as genesis + blockNumber: 0, + expectedMilliTime: 1000000, // 1000*1000 + (0-0)*250 + description: "Both Volta and Fourier at genesis, genesis block", + }, + { + name: "VoltaAtGenesis_FourierAtGenesis_AfterGenesis", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(1000), + fourierTime: uint64Ptr(1000), + blockNumber: 10, + expectedMilliTime: 1002500, // 1000*1000 + (10-0)*250 = 1000000 + 2500 + description: "Both Volta and Fourier at genesis, block 10 (250ms cadence)", + }, + { + name: "VoltaAtGenesis_FourierLater_BeforeFourier", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(1000), + fourierTime: uint64Ptr(2000), // Fourier at block 2000 (0 + (2000*1000-1000*1000)/500) + blockNumber: 1000, + expectedMilliTime: 1500000, // 1000*1000 + (1000-0)*500 + description: "Volta at genesis, Fourier later, block 1000 (before Fourier, 500ms cadence)", + }, + { + name: "VoltaAtGenesis_FourierLater_AfterFourier", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(1000), + fourierTime: uint64Ptr(2000), // Fourier at block 2000 (0 + (2000*1000-1000*1000)/500) + blockNumber: 2100, + expectedMilliTime: 2025000, // 1000*1000 + (2000-0)*500 + (2100-2000)*250 = 1000000 + 1000000 + 25000 + description: "Volta at genesis, Fourier later, block 2100 (after Fourier, 250ms cadence)", + }, + { + name: "VoltaAtGenesis_FourierLater_NonZeroGenesis", + genesisL2Time: 1000, + genesisL2Number: 100, + blockTime: 1, + voltaTime: uint64Ptr(1000), + fourierTime: uint64Ptr(2000), // Fourier at block 2100 (100 + (2000*1000-1000*1000)/500) + blockNumber: 1000, + expectedMilliTime: 1450000, // 1000*1000 + (1000-100)*500 + description: "Volta at genesis, Fourier later, genesis block 100, query block 1000 (before Fourier)", + }, + + // Case 3: Volta activates after genesis (voltaBlockNumber > 0) + { + name: "VoltaAfterGenesis_BeforeVolta", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(2000), // Volta at 2000 seconds = block 1000 (1s cadence: (2000-1000)/1) + fourierTime: nil, + blockNumber: 500, + expectedMilliTime: 1500000, // 1000*1000 + (500-0)*1*1000 + description: "Volta after genesis, block 500 (before Volta, 1s cadence)", + }, + { + name: "VoltaAfterGenesis_AtVolta", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(2000), // Volta at block 1000 + fourierTime: nil, + blockNumber: 1000, + expectedMilliTime: 2000000, // 2000*1000 + description: "Volta after genesis, block 1000 (at Volta activation)", + }, + { + name: "VoltaAfterGenesis_AfterVolta_NoFourier", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(2000), // Volta at block 1000 + fourierTime: nil, + blockNumber: 1500, + expectedMilliTime: 2250000, // 2000*1000 + (1500-1000)*500 + description: "Volta after genesis, block 1500 (after Volta, no Fourier, 500ms cadence)", + }, + { + name: "VoltaAfterGenesis_AfterVolta_BeforeFourier", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(2000), // Volta at block 1000 + fourierTime: uint64Ptr(3000), // Fourier at block 1000 + (3000*1000-2000*1000)/500 = 3000 + blockNumber: 2000, + expectedMilliTime: 2500000, // 2000*1000 + (2000-1000)*500 + description: "Volta after genesis, block 2000 (after Volta, before Fourier, 500ms cadence)", + }, + { + name: "VoltaAfterGenesis_AfterVolta_AfterFourier", + genesisL2Time: 1000, + genesisL2Number: 0, + blockTime: 1, + voltaTime: uint64Ptr(2000), // Volta at block 1000 + fourierTime: uint64Ptr(3000), // Fourier at block 3000 + blockNumber: 3010, + expectedMilliTime: 3002500, // 2000*1000 + (3000-1000)*500 + (3010-3000)*250 = 2000000 + 1000000 + 2500 + description: "Volta after genesis, block 3010 (after Volta, after Fourier, 250ms cadence)", + }, + { + name: "VoltaAfterGenesis_NonZeroGenesis", + genesisL2Time: 1000, + genesisL2Number: 100, + blockTime: 1, + voltaTime: uint64Ptr(2000), // Volta at block 100 + (2000-1000)/1 = 1100 + fourierTime: nil, + blockNumber: 1200, + expectedMilliTime: 2050000, // 2000*1000 + (1200-1100)*500 + description: "Volta after genesis, genesis block 100, query block 1200", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + config := &Config{ + Genesis: Genesis{ + L2Time: test.genesisL2Time, + L2: eth.BlockID{Number: test.genesisL2Number}, + }, + BlockTime: test.blockTime, + VoltaTime: test.voltaTime, + FourierTime: test.fourierTime, + } + + result := config.MillisecondTimestampForBlock(test.blockNumber) + assert.Equal(t, test.expectedMilliTime, result, test.description) + }) + } +} + func TestForkchoiceUpdatedVersion(t *testing.T) { config := randConfig() tests := []struct { @@ -761,7 +974,7 @@ func TestTargetBlockNumber(t *testing.T) { voltaTime: uint64Ptr(2000), // Volta at 2000 seconds fourierTime: uint64Ptr(3000), // Fourier at 3000 seconds milliTimestamp: 3500000, // 3500 seconds, after Fourier - expectedBlockNumber: 2602, // FourierBlockNumber=602 (600+2), then 602 + (500*1000)/250 = 602 + 2000 = 2602 + expectedBlockNumber: 4600, // FourierBlockNumber=2600 (600+(3000*1000-2000*1000)/500), then 2600 + (500*1000)/250 = 2600 + 2000 = 4600 expectError: false, }, { diff --git a/op-node/service.go b/op-node/service.go index 134a916ee..e162834c5 100644 --- a/op-node/service.go +++ b/op-node/service.go @@ -8,6 +8,7 @@ import ( "io" "os" "strings" + "time" "github.com/ethereum-optimism/optimism/op-node/chaincfg" plasma "github.com/ethereum-optimism/optimism/op-plasma" @@ -91,10 +92,12 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ListenAddr: ctx.String(flags.MetricsAddrFlag.Name), ListenPort: ctx.Int(flags.MetricsPortFlag.Name), }, - Pprof: oppprof.ReadCLIConfig(ctx), - P2P: p2pConfig, - P2PSigner: p2pSignerSetup, - L1EpochPollInterval: ctx.Duration(flags.L1EpochPollIntervalFlag.Name), + Pprof: oppprof.ReadCLIConfig(ctx), + P2P: p2pConfig, + P2PSigner: p2pSignerSetup, + // BSC block interval is 450 ms, so we set the poll interval to 1 second + // L1EpochPollInterval: ctx.Duration(flags.L1EpochPollIntervalFlag.Name), + L1EpochPollInterval: time.Second * 1, RuntimeConfigReloadInterval: ctx.Duration(flags.RuntimeConfigReloadIntervalFlag.Name), Heartbeat: node.HeartbeatConfig{ Enabled: ctx.Bool(flags.HeartbeatEnabledFlag.Name), @@ -204,6 +207,7 @@ func NewConfigPersistence(ctx *cli.Context) node.ConfigPersistence { func NewDriverConfig(ctx *cli.Context) *driver.Config { return &driver.Config{ + L1FinalizedConfDepth: ctx.Bool(flags.L1FinalizedConfDepth.Name), VerifierConfDepth: ctx.Uint64(flags.VerifierL1Confs.Name), SequencerConfDepth: ctx.Uint64(flags.SequencerL1Confs.Name), SequencerEnabled: ctx.Bool(flags.SequencerEnabledFlag.Name), @@ -211,6 +215,7 @@ func NewDriverConfig(ctx *cli.Context) *driver.Config { SequencerMaxSafeLag: ctx.Uint64(flags.SequencerMaxSafeLagFlag.Name), SequencerPriority: ctx.Bool(flags.SequencerPriorityFlag.Name), SequencerCombinedEngine: ctx.Bool(flags.SequencerCombinedEngineFlag.Name), + L2P2PNode: ctx.Bool(flags.IsP2PNodeFlag.Name), } } diff --git a/op-program/client/driver/driver.go b/op-program/client/driver/driver.go index eab8a41de..6a676b066 100644 --- a/op-program/client/driver/driver.go +++ b/op-program/client/driver/driver.go @@ -59,7 +59,7 @@ func NewDriver(logger log.Logger, cfg *rollup.Config, l1Source derive.L1Fetcher, SkipSyncStartCheck: false, ELTriggerGap: 0, }, false) - attributesHandler := attributes.NewAttributesHandler(logger, cfg, engine, l2Source) + attributesHandler := attributes.NewAttributesHandler(logger, cfg, engine, l2Source, false) pipeline := derive.NewDerivationPipeline(logger, cfg, l1Source, l1BlobsSource, plasma.Disabled, l2Source, engine, metrics.NoopMetrics, &sync.Config{}, safedb.Disabled, NoopFinalizer{}, attributesHandler) pipeline.Reset() return &Driver{ diff --git a/op-service/metrics/ref_metrics.go b/op-service/metrics/ref_metrics.go index 19d8badb9..a25966740 100644 --- a/op-service/metrics/ref_metrics.go +++ b/op-service/metrics/ref_metrics.go @@ -2,6 +2,7 @@ package metrics import ( "encoding/binary" + "sync" "time" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -29,6 +30,7 @@ type RefMetrics struct { // hash of the last seen block per name, so we don't reduce/increase latency on updates of the same data, // and only count the first occurrence LatencySeen map[string]common.Hash + mu *sync.Mutex // by pointer reference, since RefMetrics is copied } var _ RefMetricer = (*RefMetrics)(nil) @@ -81,10 +83,14 @@ func MakeRefMetrics(ns string, factory Factory) RefMetrics { "type", }), LatencySeen: make(map[string]common.Hash), + mu: new(sync.Mutex), } } func (m *RefMetrics) RecordRef(layer string, name string, num uint64, timestamp uint64, h common.Hash) { + m.mu.Lock() + defer m.mu.Unlock() + m.RefsNumber.WithLabelValues(layer, name).Set(float64(num)) if timestamp != 0 { m.RefsTime.WithLabelValues(layer, name).Set(float64(timestamp))