mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 07:36:44 +00:00
* add l1 config in genesis config (#249) * add l1 config in genesis config * fix lint * Update params/config.go Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> --------- Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> * extend node configuration (#251) * extend node configuration * use block number instead of hash * accept safe, finalized and numbers for L1Confirmations * fix typos --------- Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> Co-authored-by: Péter Garamvölgyi <peter@scroll.io> * Fix/improve node config parsing (#260) * raise error on failed parsing * default value * add l1-message-type, transaction methods (#252) * add l1-message-type, transaction methods * goimports * Update core/types/transaction.go Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> * txpool l1 check, pointer change, marhsal test * draft: start implementing l1message gas behavior * draft: start implementing l1message gas behavior * change to gas usage * error comment typo Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * goimports * update nonce, add hash test (fails), marshal test * goimports * target addr cant be nil * change call msg * comment out test * lint --------- Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Add L1 message database (#255) * add l1-message-type, transaction methods * goimports * Update core/types/transaction.go Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> * add L1 message store to rawdb * remove comments * rename to l1_message * rename variables and add comments * write l1 msgs in a batch * add more comments * update tests * allow batched and non-batched writes * rename to accessors_l1_message * handle error * add range check * fix tests * update comments * nit * support blocks with 0 l1 messages --------- Co-authored-by: Max Wolff <maxcwolff@gmail.com> Co-authored-by: Max Wolff <max@scroll.io> * Fix L1Message Deep Copy, Complete Bridge Tx Hash test (#269) * deep copy value field, add tx hash test comment * typo * Rename nonce to queueindex, increment sender nonce on L1 message execution (#271) * change nonce to queueindex, increment nonce on L1 message * fix db acccessors * Update core/types/transaction_marshalling.go Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> --------- Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> * Fix db inspect command (#276) fix db inspect command * Add l1 sync service (#256) * extend node configuration * add l1-message-type, transaction methods * goimports * Update core/types/transaction.go Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> * use block number instead of hash * accept safe, finalized and numbers for L1Confirmations * add L1 message store to rawdb * remove comments * fix typos * add L1 message sync service * use l1 contract address and chain ID * use L1DeploymentBlock * add confirmation config * move bridge client to separate file * use uint64 block number * fix bigint comparison * rename constants * add more logs * rename to l1_message * rename variables and add comments * write l1 msgs in a batch * add more comments * update tests * allow batched and non-batched writes * rename to accessors_l1_message * handle error * check if config is provided * improve sync service DB batched writes * add range check * fix tests * update comments * nit * fix flush range and improve comments * solve circular dependency * update stress tests * initialize l1 client for geth * start sync service * add more comments * check nil correctly * address comments * fix merge * fix genesis l1config deserialization * add sync progress logs * initial sync * handle leveldb not found error * use errors.Is * address comments * update DefaultPollInterval --------- Co-authored-by: Nazarii Denha <dengaaa2002@gmail.com> Co-authored-by: Max Wolff <maxcwolff@gmail.com> Co-authored-by: Max Wolff <max@scroll.io> * Add L1 message validation (#272) * add L1 message validation * add comments and better error handling * handle leveldb not found error * update incorrect condition for genesis block * typo * change inclusion index logic * disable L1 message check for legacy tests * set NumL1MessagesPerBlock to 0 in tests * update default genesis config * Add L1 msg validation tests (#303) add L1 msg validation tests * Update miner include l1 messages (#265) * add l1-message-type, transaction methods * goimports * Update core/types/transaction.go Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> * add L1 message store to rawdb * add L1 message sync service * remove comments * use l1 contract address and chain ID * extend node configuration * use block number instead of hash * accept safe, finalized and numbers for L1Confirmations * fix typos * use L1DeploymentBlock * add confirmation config * move bridge client to separate file * use uint64 block number * fix bigint comparison * rename constants * add more logs * Fix/improve node config parsing (#260) * raise error on failed parsing * default value * rename to l1_message * rename variables and add comments * write l1 msgs in a batch * add more comments * update tests * allow batched and non-batched writes * rename to accessors_l1_message * handle error * check if config is provided * improve sync service DB batched writes * include l1 messages in blocks: part 1 * add l1-message-type, transaction methods (#252) * add l1-message-type, transaction methods * goimports * Update core/types/transaction.go Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> * txpool l1 check, pointer change, marhsal test * draft: start implementing l1message gas behavior * draft: start implementing l1message gas behavior * change to gas usage * error comment typo Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * goimports * update nonce, add hash test (fails), marshal test * goimports * target addr cant be nil * change call msg * comment out test * lint --------- Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> Co-authored-by: Haichen Shen <shenhaichen@gmail.com> * Add L1 message database (#255) * add l1-message-type, transaction methods * goimports * Update core/types/transaction.go Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> * add L1 message store to rawdb * remove comments * rename to l1_message * rename variables and add comments * write l1 msgs in a batch * add more comments * update tests * allow batched and non-batched writes * rename to accessors_l1_message * handle error * add range check * fix tests * update comments * nit * support blocks with 0 l1 messages --------- Co-authored-by: Max Wolff <maxcwolff@gmail.com> Co-authored-by: Max Wolff <max@scroll.io> * build(docker): auto docker push when pushing git tags (#258) * build(docker): update docker trigger tag prefix (#259) * Fix L1Message Deep Copy, Complete Bridge Tx Hash test (#269) * deep copy value field, add tx hash test comment * typo * commitl1messages * lint * Revert "add L1 message sync service" This reverts commit 5305e8a5de14766ed249e1a7d64042c7a72cf5c2. * Revert "move bridge client to separate file" This reverts commit 0b220bee37de93c3250545e23430db2c401a2f90. * update branch * use commitMessages for l1Txs * little fix * fix config * fix test * comment fixes * fix * fix config check --------- Co-authored-by: Max Wolff <maxcwolff@gmail.com> Co-authored-by: Max Wolff <max@scroll.io> Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> Co-authored-by: Péter Garamvölgyi <peter@scroll.io> Co-authored-by: Haichen Shen <shenhaichen@gmail.com> Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> * Add ErrUnknownAncestor tests (#305) add ErrUnknownAncestor tests * worker test include l1 msgs (#306) * worker test include l1 msgs * move L1 message index update next to block insertion --------- Co-authored-by: Péter Garamvölgyi <peter@scroll.io> * exclude l1 messages from transaction count limit in block (#307) * exclude l1 messages from transaction count limit in block * fix comments * trigger ci * nit --------- Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> Co-authored-by: Péter Garamvölgyi <peter@scroll.io> * Expose queueIndex on Transaction (#316) expose queueIndex on Transaction * test that l1msg doesn't count in maxTxPerBlock limit (#312) * test that l1msg doesn't count in maxTxPerBlock limit * fix, comment * retrigger ci * change order inside test --------- Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Co-authored-by: Péter Garamvölgyi <th307q@gmail.com> * reuse trace nonce field for queueIndex * expose scroll APIs on the geth console * add L1 message query APIs * Trigger new block on new l1 messages (#343) * trigger new block on new l1 messages * typo * initialize l1MsgCh * fix worker l1msg tests (#345) --------- Co-authored-by: Nazarii Denha <dengaaa2002@gmail.com> * test(worker): ensure that l1 messages are included in the correct order (#346) test that l1msgs added in correct order * rename enqueueIndex --> queueIndex * move QueueIndex into transaction * improve l1 db interface * formatting * bump version * print l1config * add API to query latest included message queue index * clean up tx limit logic * add clarifying comments and todos to ValidateL1Messages * improve db comments and logs * clean up L1MessageTx type handling * format * format * improve L1 message block check * fix missing L1 event handling * fix TestL1MessageValidationFailure * simplify sync height resume logic * make l1Config.l1MessageQueueAddress non-pointer * improve command line flags * remove todo * use abigen tools for log filtering * cache block L1 message count * nit: fix variable name case * improve logs * flush pending writes to DB before shutdown --------- Co-authored-by: Nazarii Denha <dengaaa2002@gmail.com> Co-authored-by: Max Wolff <max@scroll.io> Co-authored-by: Haichen Shen <shenhaichen@gmail.com> Co-authored-by: Max Wolff <maxcwolff@gmail.com> Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Co-authored-by: HAOYUatHZ <haoyu@protonmail.com>
227 lines
6.7 KiB
Go
227 lines
6.7 KiB
Go
package sync_service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/scroll-tech/go-ethereum/core"
|
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
|
"github.com/scroll-tech/go-ethereum/ethdb"
|
|
"github.com/scroll-tech/go-ethereum/event"
|
|
"github.com/scroll-tech/go-ethereum/log"
|
|
"github.com/scroll-tech/go-ethereum/node"
|
|
"github.com/scroll-tech/go-ethereum/params"
|
|
)
|
|
|
|
const (
|
|
// DefaultFetchBlockRange is the number of blocks that we collect in a single eth_getLogs query.
|
|
DefaultFetchBlockRange = uint64(100)
|
|
|
|
// DefaultPollInterval is the frequency at which we query for new L1 messages.
|
|
DefaultPollInterval = time.Second * 10
|
|
|
|
// LogProgressInterval is the frequency at which we log progress.
|
|
LogProgressInterval = time.Second * 10
|
|
|
|
// DbWriteThresholdBytes is the size of batched database writes in bytes.
|
|
DbWriteThresholdBytes = 10 * 1024
|
|
|
|
// DbWriteThresholdBlocks is the number of blocks scanned after which we write to the database
|
|
// even if we have not collected DbWriteThresholdBytes bytes of data yet. This way, if there is
|
|
// a long section of L1 blocks with no messages and we stop or crash, we will not need to re-scan
|
|
// this secion.
|
|
DbWriteThresholdBlocks = 1000
|
|
)
|
|
|
|
// SyncService collects all L1 messages and stores them in a local database.
|
|
type SyncService struct {
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
client *BridgeClient
|
|
db ethdb.Database
|
|
msgCountFeed event.Feed
|
|
pollInterval time.Duration
|
|
latestProcessedBlock uint64
|
|
scope event.SubscriptionScope
|
|
}
|
|
|
|
func NewSyncService(ctx context.Context, genesisConfig *params.ChainConfig, nodeConfig *node.Config, db ethdb.Database, l1Client EthClient) (*SyncService, error) {
|
|
// terminate if the caller does not provide an L1 client (e.g. in tests)
|
|
if l1Client == nil || (reflect.ValueOf(l1Client).Kind() == reflect.Ptr && reflect.ValueOf(l1Client).IsNil()) {
|
|
log.Warn("No L1 client provided, L1 sync service will not run")
|
|
return nil, nil
|
|
}
|
|
|
|
if genesisConfig.Scroll.L1Config == nil {
|
|
return nil, fmt.Errorf("missing L1 config in genesis")
|
|
}
|
|
|
|
client, err := newBridgeClient(ctx, l1Client, genesisConfig.Scroll.L1Config.L1ChainId, nodeConfig.L1Confirmations, genesisConfig.Scroll.L1Config.L1MessageQueueAddress)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to initialize bridge client: %w", err)
|
|
}
|
|
|
|
// assume deployment block has 0 messages
|
|
latestProcessedBlock := nodeConfig.L1DeploymentBlock
|
|
block := rawdb.ReadSyncedL1BlockNumber(db)
|
|
if block != nil {
|
|
// restart from latest synced block number
|
|
latestProcessedBlock = *block
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
|
|
service := SyncService{
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
client: client,
|
|
db: db,
|
|
pollInterval: DefaultPollInterval,
|
|
latestProcessedBlock: latestProcessedBlock,
|
|
}
|
|
|
|
return &service, nil
|
|
}
|
|
|
|
func (s *SyncService) Start() {
|
|
if s == nil {
|
|
return
|
|
}
|
|
|
|
// wait for initial sync before starting node
|
|
log.Info("Starting L1 message sync service", "latestProcessedBlock", s.latestProcessedBlock)
|
|
|
|
// block node startup during initial sync and print some helpful logs
|
|
latestConfirmed, err := s.client.getLatestConfirmedBlockNumber(s.ctx)
|
|
if err == nil && latestConfirmed > s.latestProcessedBlock+1000 {
|
|
log.Warn("Running initial sync of L1 messages before starting l2geth, this might take a while...")
|
|
s.fetchMessages()
|
|
log.Info("L1 message initial sync completed", "latestProcessedBlock", s.latestProcessedBlock)
|
|
}
|
|
|
|
go func() {
|
|
t := time.NewTicker(s.pollInterval)
|
|
defer t.Stop()
|
|
|
|
for {
|
|
// don't wait for ticker during startup
|
|
s.fetchMessages()
|
|
|
|
select {
|
|
case <-s.ctx.Done():
|
|
return
|
|
case <-t.C:
|
|
continue
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (s *SyncService) Stop() {
|
|
if s == nil {
|
|
return
|
|
}
|
|
|
|
log.Info("Stopping sync service")
|
|
|
|
// Unsubscribe all subscriptions registered
|
|
s.scope.Close()
|
|
|
|
if s.cancel != nil {
|
|
s.cancel()
|
|
}
|
|
}
|
|
|
|
// SubscribeNewL1MsgsEvent registers a subscription of NewL1MsgsEvent and
|
|
// starts sending event to the given channel.
|
|
func (s *SyncService) SubscribeNewL1MsgsEvent(ch chan<- core.NewL1MsgsEvent) event.Subscription {
|
|
return s.scope.Track(s.msgCountFeed.Subscribe(ch))
|
|
}
|
|
|
|
func (s *SyncService) fetchMessages() {
|
|
latestConfirmed, err := s.client.getLatestConfirmedBlockNumber(s.ctx)
|
|
if err != nil {
|
|
log.Warn("Failed to get latest confirmed block number", "err", err)
|
|
return
|
|
}
|
|
|
|
log.Trace("Sync service fetchMessages", "latestProcessedBlock", s.latestProcessedBlock, "latestConfirmed", latestConfirmed)
|
|
|
|
batchWriter := s.db.NewBatch()
|
|
numBlocksPendingDbWrite := uint64(0)
|
|
numMessagesPendingDbWrite := 0
|
|
|
|
// helper function to flush database writes cached in memory
|
|
flush := func(lastBlock uint64) {
|
|
// update sync progress
|
|
rawdb.WriteSyncedL1BlockNumber(batchWriter, lastBlock)
|
|
|
|
// write batch in a single transaction
|
|
err := batchWriter.Write()
|
|
if err != nil {
|
|
// crash on database error, no risk of inconsistency here
|
|
log.Crit("Failed to write L1 messages to database", "err", err)
|
|
}
|
|
|
|
batchWriter.Reset()
|
|
numBlocksPendingDbWrite = 0
|
|
|
|
if numMessagesPendingDbWrite > 0 {
|
|
s.msgCountFeed.Send(core.NewL1MsgsEvent{Count: numMessagesPendingDbWrite})
|
|
numMessagesPendingDbWrite = 0
|
|
}
|
|
|
|
s.latestProcessedBlock = lastBlock
|
|
}
|
|
|
|
// ticker for logging progress
|
|
t := time.NewTicker(LogProgressInterval)
|
|
numMsgsCollected := 0
|
|
|
|
// query in batches
|
|
for from := s.latestProcessedBlock + 1; from <= latestConfirmed; from += DefaultFetchBlockRange {
|
|
select {
|
|
case <-s.ctx.Done():
|
|
// flush pending writes to database
|
|
if from > 0 {
|
|
flush(from - 1)
|
|
}
|
|
return
|
|
case <-t.C:
|
|
progress := 100 * float64(s.latestProcessedBlock) / float64(latestConfirmed)
|
|
log.Info("Syncing L1 messages", "processed", s.latestProcessedBlock, "confirmed", latestConfirmed, "collected", numMsgsCollected, "progress(%)", progress)
|
|
default:
|
|
}
|
|
|
|
to := from + DefaultFetchBlockRange - 1
|
|
if to > latestConfirmed {
|
|
to = latestConfirmed
|
|
}
|
|
|
|
msgs, err := s.client.fetchMessagesInRange(s.ctx, from, to)
|
|
if err != nil {
|
|
// flush pending writes to database
|
|
if from > 0 {
|
|
flush(from - 1)
|
|
}
|
|
log.Warn("Failed to fetch L1 messages in range", "fromBlock", from, "toBlock", to, "err", err)
|
|
return
|
|
}
|
|
|
|
if len(msgs) > 0 {
|
|
log.Debug("Received new L1 events", "fromBlock", from, "toBlock", to, "count", len(msgs))
|
|
rawdb.WriteL1Messages(batchWriter, msgs) // collect messages in memory
|
|
numMsgsCollected += len(msgs)
|
|
}
|
|
|
|
numBlocksPendingDbWrite += to - from
|
|
numMessagesPendingDbWrite += len(msgs)
|
|
|
|
// flush new messages to database periodically
|
|
if to == latestConfirmed || batchWriter.ValueSize() >= DbWriteThresholdBytes || numBlocksPendingDbWrite >= DbWriteThresholdBlocks {
|
|
flush(to)
|
|
}
|
|
}
|
|
}
|