From 0ebf17347b7fce7af879803c6e802ad3068077e1 Mon Sep 17 00:00:00 2001 From: WorldDogs <33647825+WorldDogs@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:46:55 +0800 Subject: [PATCH 1/6] feat(tx-submitter): Add L1 Block Growth Monitoring Feature --- tx-submitter/entry.go | 5 +++ tx-submitter/flags/flags.go | 9 ++++ tx-submitter/l1checker/blockmonitor.go | 48 +++++++++++++++++++++ tx-submitter/l1checker/blockmonitor_test.go | 23 ++++++++++ tx-submitter/services/rollup.go | 8 ++++ tx-submitter/utils/config.go | 5 ++- 6 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 tx-submitter/l1checker/blockmonitor.go create mode 100644 tx-submitter/l1checker/blockmonitor_test.go diff --git a/tx-submitter/entry.go b/tx-submitter/entry.go index 1b9809b3..2687ed8e 100644 --- a/tx-submitter/entry.go +++ b/tx-submitter/entry.go @@ -16,6 +16,7 @@ import ( "morph-l2/tx-submitter/db" "morph-l2/tx-submitter/event" "morph-l2/tx-submitter/iface" + "morph-l2/tx-submitter/l1checker" "morph-l2/tx-submitter/metrics" "morph-l2/tx-submitter/services" "morph-l2/tx-submitter/utils" @@ -194,6 +195,9 @@ func Main() func(ctx *cli.Context) error { return fmt.Errorf("failed to connect leveldb: %w", err) } + // blockmonitor + bm := l1checker.NewBlockMonitor(cfg.BlockNotIncreasedThreshold, l1Client) + // new rollup service sr := services.NewRollup( ctx, @@ -211,6 +215,7 @@ func Main() func(ctx *cli.Context) error { rsaPriv, rotator, ldb, + bm, ) // metrics diff --git a/tx-submitter/flags/flags.go b/tx-submitter/flags/flags.go index 52254648..041cf2db 100644 --- a/tx-submitter/flags/flags.go +++ b/tx-submitter/flags/flags.go @@ -298,6 +298,14 @@ var ( EnvVar: prefixEnvVar("LEVELDB_PATH_NAME"), Value: "submitter-leveldb", } + + // l1 block not incremented threshold + BlockNotIncreasedThreshold = cli.Int64Flag{ + Name: "block_not_increased_threshold", + Usage: "The threshold for block not incremented", + Value: 5, + EnvVar: prefixEnvVar("BLOCK_NOT_INCREASED_THRESHOLD"), + } ) var requiredFlags = []cli.Flag{ @@ -352,6 +360,7 @@ var optionalFlags = []cli.Flag{ StakingEventStoreFileFlag, EventIndexStepFlag, LeveldbPathNameFlag, + BlockNotIncreasedThreshold, } // Flags contains the list of configuration options available to the binary. diff --git a/tx-submitter/l1checker/blockmonitor.go b/tx-submitter/l1checker/blockmonitor.go new file mode 100644 index 00000000..9864bcf8 --- /dev/null +++ b/tx-submitter/l1checker/blockmonitor.go @@ -0,0 +1,48 @@ +package l1checker + +import ( + "context" + "morph-l2/tx-submitter/iface" + "time" + + "github.com/morph-l2/go-ethereum/log" +) + +type IBlockMonitor interface { + BlockNotIncreasedIn(time.Duration) bool +} + +type BlockMonitor struct { + blockGenerateTime time.Duration //12s for Dencun + latestBlockTime time.Time + noGrowthBlockCntTime time.Duration + client iface.L1Client +} + +func NewBlockMonitor(notGrowthInBlocks int64, client iface.L1Client) *BlockMonitor { + return &BlockMonitor{ + blockGenerateTime: time.Second * 12, + latestBlockTime: time.Time{}, + noGrowthBlockCntTime: time.Second * time.Duration(notGrowthInBlocks) * 12, + client: client, + } +} + +func (m *BlockMonitor) StartMonitoring() { + ticker := time.NewTicker(m.blockGenerateTime) + for ; ; <-ticker.C { + header, err := m.client.HeaderByNumber(context.Background(), nil) + if err != nil { + log.Warn("failed to get block in blockmonitor", "error", err) + continue + } + m.latestBlockTime = time.Unix(int64(header.Time), 0) + } +} + +func (m *BlockMonitor) IsGrowth() bool { + if m.latestBlockTime.IsZero() { + return false + } + return time.Since(m.latestBlockTime) > m.noGrowthBlockCntTime +} diff --git a/tx-submitter/l1checker/blockmonitor_test.go b/tx-submitter/l1checker/blockmonitor_test.go new file mode 100644 index 00000000..fb867e89 --- /dev/null +++ b/tx-submitter/l1checker/blockmonitor_test.go @@ -0,0 +1,23 @@ +package l1checker + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestIsGrowth(t *testing.T) { + + blockCnt := int64(2) + monitor := NewBlockMonitor(blockCnt, nil) + monitor.latestBlockTime = time.Time{} + require.Equal(t, false, monitor.IsGrowth()) + + monitor.latestBlockTime = time.Now() + require.Equal(t, false, monitor.IsGrowth()) + + monitor.latestBlockTime = time.Now().Add(-monitor.noGrowthBlockCntTime) + require.Equal(t, true, monitor.IsGrowth()) + +} diff --git a/tx-submitter/services/rollup.go b/tx-submitter/services/rollup.go index 63a65412..bd7aab3d 100644 --- a/tx-submitter/services/rollup.go +++ b/tx-submitter/services/rollup.go @@ -30,6 +30,7 @@ import ( "morph-l2/tx-submitter/db" "morph-l2/tx-submitter/event" "morph-l2/tx-submitter/iface" + "morph-l2/tx-submitter/l1checker" "morph-l2/tx-submitter/localpool" "morph-l2/tx-submitter/metrics" "morph-l2/tx-submitter/utils" @@ -75,6 +76,7 @@ type Rollup struct { collectedL1FeeSum float64 // batchcache batchCache map[uint64]*eth.RPCRollupBatch + bm *l1checker.BlockMonitor } func NewRollup( @@ -93,6 +95,7 @@ func NewRollup( rsaPriv *rsa.PrivateKey, rotator *Rotator, ldb *db.Db, + bm *l1checker.BlockMonitor, ) *Rollup { return &Rollup{ @@ -113,6 +116,7 @@ func NewRollup( externalRsaPriv: rsaPriv, batchCache: make(map[uint64]*eth.RPCRollupBatch), ldb: ldb, + bm: bm, } } @@ -1090,6 +1094,10 @@ func (r *Rollup) SendTx(tx *types.Transaction) error { if tx == nil { return errors.New("nil tx") } + // l1 health check + if !r.bm.IsGrowth() { + return fmt.Errorf("block not growth in %d blocks time", r.cfg.BlockNotIncreasedThreshold) + } err := sendTx(r.L1Client, r.cfg.TxFeeLimit, tx) if err != nil { diff --git a/tx-submitter/utils/config.go b/tx-submitter/utils/config.go index 3d63fa04..30decb20 100644 --- a/tx-submitter/utils/config.go +++ b/tx-submitter/utils/config.go @@ -105,7 +105,8 @@ type Config struct { // event indexer index step EventIndexStep uint64 // leveldb path name - LeveldbPathName string + LeveldbPathName string + BlockNotIncreasedThreshold int64 } // NewConfig parses the DriverConfig from the provided flags or environment variables. @@ -175,6 +176,8 @@ func NewConfig(ctx *cli.Context) (Config, error) { EventIndexStep: ctx.GlobalUint64(flags.EventIndexStepFlag.Name), // leveldb path name LeveldbPathName: ctx.GlobalString(flags.LeveldbPathNameFlag.Name), + // BlockNotIncreasedThreshold + BlockNotIncreasedThreshold: ctx.GlobalInt64(flags.BlockNotIncreasedThreshold.Name), } return cfg, nil From c83e12e4e952c7f95155d58d7ca7242f2b1eaa58 Mon Sep 17 00:00:00 2001 From: WorldDogs <33647825+WorldDogs@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:55:37 +0800 Subject: [PATCH 2/6] start monitoring --- tx-submitter/services/rollup.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tx-submitter/services/rollup.go b/tx-submitter/services/rollup.go index bd7aab3d..02dc4587 100644 --- a/tx-submitter/services/rollup.go +++ b/tx-submitter/services/rollup.go @@ -126,6 +126,8 @@ func (r *Rollup) Start() error { if err := r.PreCheck(); err != nil { return err } + // start l1 monitor + go r.bm.StartMonitoring() // journal jn := localpool.New(r.cfg.JournalFilePath) From 74e8c2aba62f15bcb5bc9bd29f45fd7ad4722d66 Mon Sep 17 00:00:00 2001 From: WorldDogs <33647825+WorldDogs@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:08:29 +0800 Subject: [PATCH 3/6] adjust l1monitor service start order --- tx-submitter/services/rollup.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tx-submitter/services/rollup.go b/tx-submitter/services/rollup.go index 02dc4587..d944b5bd 100644 --- a/tx-submitter/services/rollup.go +++ b/tx-submitter/services/rollup.go @@ -126,8 +126,6 @@ func (r *Rollup) Start() error { if err := r.PreCheck(); err != nil { return err } - // start l1 monitor - go r.bm.StartMonitoring() // journal jn := localpool.New(r.cfg.JournalFilePath) @@ -150,6 +148,10 @@ func (r *Rollup) Start() error { return fmt.Errorf("init fee metrics sum failed: %w", err) } + /// start services + // start l1 monitor + go r.bm.StartMonitoring() + // metrics go utils.Loop(r.ctx, 10*time.Second, func() { From 18a51b24b5c91ae6f28498425ec469097d850915 Mon Sep 17 00:00:00 2001 From: WorldDogs <33647825+WorldDogs@users.noreply.github.com> Date: Mon, 11 Nov 2024 16:19:35 +0800 Subject: [PATCH 4/6] fix(tx-submitter): add mutex to protect latestBlockTime in BlockMonitor - Add a mutex to ensure thread-safe access to latestBlockTime - Implement SetLatestBlockTime and GetLatestBlockTime methods - Update StartMonitoring and IsGrowth methods to use new thread-safe accessors --- tx-submitter/l1checker/blockmonitor.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tx-submitter/l1checker/blockmonitor.go b/tx-submitter/l1checker/blockmonitor.go index 9864bcf8..78270c0f 100644 --- a/tx-submitter/l1checker/blockmonitor.go +++ b/tx-submitter/l1checker/blockmonitor.go @@ -2,9 +2,11 @@ package l1checker import ( "context" - "morph-l2/tx-submitter/iface" + "sync" "time" + "morph-l2/tx-submitter/iface" + "github.com/morph-l2/go-ethereum/log" ) @@ -17,6 +19,7 @@ type BlockMonitor struct { latestBlockTime time.Time noGrowthBlockCntTime time.Duration client iface.L1Client + mu sync.Mutex } func NewBlockMonitor(notGrowthInBlocks int64, client iface.L1Client) *BlockMonitor { @@ -36,13 +39,26 @@ func (m *BlockMonitor) StartMonitoring() { log.Warn("failed to get block in blockmonitor", "error", err) continue } - m.latestBlockTime = time.Unix(int64(header.Time), 0) + m.SetLatestBlockTime(time.Unix(int64(header.Time), 0)) } } func (m *BlockMonitor) IsGrowth() bool { - if m.latestBlockTime.IsZero() { + t := m.GetLatestBlockTime() + if t.IsZero() { return false } - return time.Since(m.latestBlockTime) > m.noGrowthBlockCntTime + return time.Since(t) > m.noGrowthBlockCntTime +} + +func (m *BlockMonitor) SetLatestBlockTime(t time.Time) { + m.mu.Lock() + defer m.mu.Unlock() + m.latestBlockTime = t +} + +func (m *BlockMonitor) GetLatestBlockTime() time.Time { + m.mu.Lock() + defer m.mu.Unlock() + return m.latestBlockTime } From 4e401508115ed168a6ad81151ef20b1e15b6dc1b Mon Sep 17 00:00:00 2001 From: WorldDogs <33647825+WorldDogs@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:39:13 +0800 Subject: [PATCH 5/6] refactor(tx-submitter): introduce constant for block time --- tx-submitter/l1checker/blockmonitor.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tx-submitter/l1checker/blockmonitor.go b/tx-submitter/l1checker/blockmonitor.go index 78270c0f..e1b31ccc 100644 --- a/tx-submitter/l1checker/blockmonitor.go +++ b/tx-submitter/l1checker/blockmonitor.go @@ -10,6 +10,8 @@ import ( "github.com/morph-l2/go-ethereum/log" ) +const blockTime = time.Second * 12 + type IBlockMonitor interface { BlockNotIncreasedIn(time.Duration) bool } @@ -24,7 +26,7 @@ type BlockMonitor struct { func NewBlockMonitor(notGrowthInBlocks int64, client iface.L1Client) *BlockMonitor { return &BlockMonitor{ - blockGenerateTime: time.Second * 12, + blockGenerateTime: blockTime, latestBlockTime: time.Time{}, noGrowthBlockCntTime: time.Second * time.Duration(notGrowthInBlocks) * 12, client: client, From fe8860676ca9d18facea0002f64a6d73244a9b5b Mon Sep 17 00:00:00 2001 From: WorldDogs <33647825+WorldDogs@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:31:06 +0800 Subject: [PATCH 6/6] correct block monitoring logic and time calculation --- tx-submitter/l1checker/blockmonitor.go | 5 +++-- tx-submitter/l1checker/blockmonitor_test.go | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tx-submitter/l1checker/blockmonitor.go b/tx-submitter/l1checker/blockmonitor.go index e1b31ccc..9c870809 100644 --- a/tx-submitter/l1checker/blockmonitor.go +++ b/tx-submitter/l1checker/blockmonitor.go @@ -28,7 +28,7 @@ func NewBlockMonitor(notGrowthInBlocks int64, client iface.L1Client) *BlockMonit return &BlockMonitor{ blockGenerateTime: blockTime, latestBlockTime: time.Time{}, - noGrowthBlockCntTime: time.Second * time.Duration(notGrowthInBlocks) * 12, + noGrowthBlockCntTime: time.Duration(notGrowthInBlocks) * blockTime, client: client, } } @@ -48,9 +48,10 @@ func (m *BlockMonitor) StartMonitoring() { func (m *BlockMonitor) IsGrowth() bool { t := m.GetLatestBlockTime() if t.IsZero() { + log.Warn("latest block time is zero") return false } - return time.Since(t) > m.noGrowthBlockCntTime + return time.Since(t) < m.noGrowthBlockCntTime } func (m *BlockMonitor) SetLatestBlockTime(t time.Time) { diff --git a/tx-submitter/l1checker/blockmonitor_test.go b/tx-submitter/l1checker/blockmonitor_test.go index fb867e89..846e814c 100644 --- a/tx-submitter/l1checker/blockmonitor_test.go +++ b/tx-submitter/l1checker/blockmonitor_test.go @@ -15,9 +15,9 @@ func TestIsGrowth(t *testing.T) { require.Equal(t, false, monitor.IsGrowth()) monitor.latestBlockTime = time.Now() - require.Equal(t, false, monitor.IsGrowth()) + require.Equal(t, true, monitor.IsGrowth()) monitor.latestBlockTime = time.Now().Add(-monitor.noGrowthBlockCntTime) - require.Equal(t, true, monitor.IsGrowth()) + require.Equal(t, false, monitor.IsGrowth()) }