Skip to content

feat(MessageQueueV2): add reading of MessageQueueV2 and transition logic from V1 to V2 for EuclidV2 #1108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 64 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
ea5a603
port changes from #1013
jonastheis Dec 10, 2024
b30006f
port changes from #1068
jonastheis Dec 10, 2024
de37d47
go.mod tidy
jonastheis Dec 10, 2024
e34fecf
fix compile error
jonastheis Dec 10, 2024
1327771
fix goimports
jonastheis Dec 10, 2024
b05954d
fix log
jonastheis Dec 10, 2024
ce8f785
address review comments
jonastheis Dec 12, 2024
f10c383
upgrade golang.org/x/net to 0.23.0
jonastheis Dec 12, 2024
cb0a90e
Merge branch 'develop' into jt/l1-follower-mode
jonastheis Dec 12, 2024
b918a2b
port changes from #1018
jonastheis Dec 10, 2024
e51182d
fix tests and linter errors
jonastheis Dec 11, 2024
4e6f759
address review comments
jonastheis Dec 12, 2024
fd6bff3
Merge branch 'develop' into jt/l1-follower-mode-l1-reader
jonastheis Dec 26, 2024
ab3e873
refactor rollup sync service / verifier to use CalldataBlobSource to …
jonastheis Dec 26, 2024
4ced6f2
add configuration and initialize blob clients
jonastheis Dec 27, 2024
6aafa74
fix unit tests
jonastheis Dec 27, 2024
da81a2e
remove unused code
jonastheis Dec 27, 2024
8750045
address review comments
jonastheis Jan 2, 2025
2499c69
address more review comments
jonastheis Jan 2, 2025
fb4fe7c
implement first version of new da-codec and to handle multiple batche…
jonastheis Jan 13, 2025
9bf2f25
add CommitBatchDAV7 and handle multiple commit events submitted in a …
jonastheis Jan 22, 2025
d222f58
Merge remote-tracking branch 'origin/develop' into jt/rollup-verifier…
jonastheis Jan 22, 2025
c56be0d
Merge branch 'jt/rollup-verifier-use-code-from-l1-follower' into jt/l…
jonastheis Jan 22, 2025
3950e58
fix bug due to previous batch being empty when processing the first b…
jonastheis Jan 22, 2025
a043d2f
Allow using MPT
omerfirmak Dec 23, 2024
94c0ad5
Merge remote-tracking branch 'origin/omerfirmak/mpt' into jt/rollup-v…
jonastheis Jan 28, 2025
67c1866
Merge branch 'jt/rollup-verifier-use-code-from-l1-follower' into jt/l…
jonastheis Jan 28, 2025
882fb38
implement reading of QueueTransaction from L1MessageQueueV1 and V2
jonastheis Jan 28, 2025
74046dd
implement access to V1 and V2 messages and replace usage so that V1 i…
jonastheis Jan 29, 2025
44c72cd
add tests
jonastheis Jan 29, 2025
43d54cb
update to latest da-codec
jonastheis Feb 4, 2025
4290d16
add field to CommittedBatchMeta to store LastL1MessageQueueHash for C…
jonastheis Feb 4, 2025
574dd53
adjust rollup verifier to support CodecV7 batches
jonastheis Feb 4, 2025
782c890
address review comments
jonastheis Feb 4, 2025
c96b01e
consume all L1 messages before EuclidV2 fork
jonastheis Feb 4, 2025
62617ef
address review comments
jonastheis Feb 5, 2025
53b6ebf
address review comments
jonastheis Feb 5, 2025
ab3bedf
Merge remote-tracking branch 'origin/develop' into jt/rollup-verifier…
jonastheis Feb 6, 2025
3335654
fix issues after merge
jonastheis Feb 6, 2025
3e18f7f
Merge remote-tracking branch 'origin/jt/rollup-verifier-use-code-from…
jonastheis Feb 6, 2025
6c34782
Merge remote-tracking branch 'origin/jt/l1-follower-mode-update-da-co…
jonastheis Feb 6, 2025
2342335
Merge remote-tracking branch 'origin/develop' into jt/rollup-verifier…
jonastheis Feb 6, 2025
7c14639
Merge remote-tracking branch 'origin/jt/rollup-verifier-use-code-from…
jonastheis Feb 6, 2025
a5f26af
Merge remote-tracking branch 'origin/jt/l1-follower-mode-update-da-co…
jonastheis Feb 6, 2025
634d1f1
go mod tidy
jonastheis Feb 6, 2025
0fa3743
fix unit tests
jonastheis Feb 7, 2025
aab6216
Merge remote-tracking branch 'origin/jt/l1-follower-mode-update-da-co…
jonastheis Feb 7, 2025
e048f53
Merge remote-tracking branch 'origin/develop' into jt/l1-follower-mod…
jonastheis Feb 10, 2025
a2a68e8
update da-codec
jonastheis Feb 10, 2025
ca6649e
add test TestValidateBatchCodecV7
jonastheis Feb 10, 2025
80976ad
go mod tidy
jonastheis Feb 10, 2025
4022989
do not log error on shutdown
jonastheis Feb 11, 2025
90d701c
add flag to explicitly disable L1MessageQueueV2
jonastheis Feb 11, 2025
3ceee06
add TestEuclidV2HardForkMessageQueue to scroll worker
jonastheis Feb 12, 2025
d4cc897
add sanity check for version to deserialization of committedBatchMetaV7
jonastheis Feb 12, 2025
4ffca39
Merge remote-tracking branch 'origin/jt/l1-follower-mode-update-da-co…
jonastheis Feb 12, 2025
8532627
add mechanism for nodes to retrieve V2 messages even if they upgrade …
jonastheis Feb 12, 2025
6d5af23
chore: auto version bump [bot]
Thegaram Feb 12, 2025
3c21f4e
address review comments
jonastheis Feb 13, 2025
2aea708
Merge branch 'jt/l1-follower-mode-update-da-codec' into jt/message-qu…
jonastheis Feb 13, 2025
97955e4
Update core/rawdb/accessors_l1_message.go
jonastheis Feb 13, 2025
23e7a25
Merge branch 'develop' into jt/message-queue-v2
Thegaram Feb 13, 2025
c72cbfc
address review comments
jonastheis Feb 14, 2025
07b4c18
Merge branch 'develop' into jt/message-queue-v2
Thegaram Feb 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ var (
utils.L1EndpointFlag,
utils.L1ConfirmationsFlag,
utils.L1DeploymentBlockFlag,
utils.L1DisableMessageQueueV2Flag,
utils.CircuitCapacityCheckEnabledFlag,
utils.CircuitCapacityCheckWorkersFlag,
utils.RollupVerifyEnabledFlag,
Expand Down
7 changes: 7 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,10 @@ var (
Name: "l1.sync.startblock",
Usage: "L1 block height to start syncing from. Should be set to the L1 message queue deployment block number.",
}
L1DisableMessageQueueV2Flag = &cli.BoolFlag{
Name: "l1.disablemqv2",
Usage: "Disable L1 message queue v2",
}

// Circuit capacity check settings
CircuitCapacityCheckEnabledFlag = cli.BoolFlag{
Expand Down Expand Up @@ -1408,6 +1412,9 @@ func setL1(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalIsSet(L1DeploymentBlockFlag.Name) {
cfg.L1DeploymentBlock = ctx.GlobalUint64(L1DeploymentBlockFlag.Name)
}
if ctx.GlobalIsSet(L1DisableMessageQueueV2Flag.Name) {
cfg.L1DisableMessageQueueV2 = ctx.GlobalBool(L1DisableMessageQueueV2Flag.Name)
}
}

func setSmartCard(ctx *cli.Context, cfg *node.Config) {
Expand Down
46 changes: 45 additions & 1 deletion core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,52 @@ func (v *BlockValidator) ValidateL1Messages(block *types.Block) error {
queueIndex := *nextQueueIndex

L1SectionOver := false
it := rawdb.IterateL1MessagesFrom(v.bc.db, queueIndex)

// From EuclidV2 onwards there can't be any skipped L1 messages, and we use a different L1MessageQueueV2.
if v.config.IsEuclidV2(block.Time()) {
it := rawdb.IterateL1MessagesV2From(v.bc.db, queueIndex)
for _, tx := range block.Transactions() {
if !tx.IsL1MessageTx() {
L1SectionOver = true
continue // we do not verify L2 transactions here
}

// check that L1 messages are before L2 transactions
if L1SectionOver {
return consensus.ErrInvalidL1MessageOrder
}

// queue index must be equal to the expected value
txQueueIndex := tx.AsL1MessageTx().QueueIndex
if txQueueIndex != queueIndex {
return consensus.ErrInvalidL1MessageOrder
}

if exists := it.Next(); !exists {
if err := it.Error(); err != nil {
log.Error("Unexpected DB error in ValidateL1Messages", "err", err, "queueIndex", txQueueIndex)
}
// the message in this block is not available in our local db.
// we'll reprocess this block at a later time.
return consensus.ErrMissingL1MessageData
}

// check that the L1 message in the block is the same that we collected from L1
msg := it.L1Message()
expectedHash := types.NewTx(&msg).Hash()

if tx.Hash() != expectedHash {
return consensus.ErrUnknownL1Message
}

// we expect L1 messages to be in order and contiguous
queueIndex++
}

return nil
}

it := rawdb.IterateL1MessagesV1From(v.bc.db, queueIndex)
for _, tx := range block.Transactions() {
if !tx.IsL1MessageTx() {
L1SectionOver = true
Expand Down
12 changes: 12 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1853,6 +1853,18 @@ func (bc *BlockChain) BuildAndWriteBlock(parentBlock *types.Block, header *types
l.BlockHash = blockHash
}

// Make sure the block body is valid e.g. ordering of L1 messages is correct and continuous.
if err = bc.validator.ValidateBody(fullBlock); err != nil {
bc.reportBlock(fullBlock, receipts, err)
return NonStatTy, fmt.Errorf("error validating block body %d: %w", fullBlock.Number().Uint64(), err)
}

// Double check: even though we just built the block, make sure it is valid.
if err = bc.validator.ValidateState(fullBlock, statedb, receipts, gasUsed); err != nil {
bc.reportBlock(fullBlock, receipts, err)
return NonStatTy, fmt.Errorf("error validating block %d: %w", fullBlock.Number().Uint64(), err)
}

return bc.writeBlockWithState(fullBlock, receipts, logs, statedb, false)
}

Expand Down
179 changes: 173 additions & 6 deletions core/rawdb/accessors_l1_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,9 @@ type L1MessageIterator struct {
maxQueueIndex uint64
}

// IterateL1MessagesFrom creates an L1MessageIterator that iterates over
// iterateL1MessagesFrom creates an L1MessageIterator that iterates over
// all L1 message in the database starting at the provided enqueue index.
func IterateL1MessagesFrom(db ethdb.Database, fromQueueIndex uint64) L1MessageIterator {
func iterateL1MessagesFrom(db ethdb.Database, fromQueueIndex uint64) L1MessageIterator {
start := encodeBigEndian(fromQueueIndex)
it := db.NewIterator(l1MessagePrefix, start)
keyLength := len(l1MessagePrefix) + 8
Expand Down Expand Up @@ -208,10 +208,72 @@ func (it *L1MessageIterator) Error() error {
return it.inner.Error()
}

// ReadL1MessagesFrom retrieves up to `maxCount` L1 messages starting at `startIndex`.
func ReadL1MessagesFrom(db ethdb.Database, startIndex, maxCount uint64) []types.L1MessageTx {
// L1MessageV1Iterator is a wrapper around L1MessageIterator that allows us to iterate over L1 messages V1.
type L1MessageV1Iterator struct {
db ethdb.Database
v2StartIndex *uint64
L1MessageIterator
}

// IterateL1MessagesV1From yields a L1MessageV1Iterator with following behavior:
// - If fromQueueIndex >= L1MessageV2StartIndex: yield 0 messages.
// - Otherwise, simply yield all messages (guaranteed to be V1) starting from `fromQueueIndex` until `L1MessageV2StartIndex`.
func IterateL1MessagesV1From(db ethdb.Database, fromQueueIndex uint64) L1MessageV1Iterator {
return L1MessageV1Iterator{
db: db,
v2StartIndex: ReadL1MessageV2StartIndex(db),
L1MessageIterator: iterateL1MessagesFrom(db, fromQueueIndex),
}
}

func (it *L1MessageV1Iterator) Next() bool {
for it.L1MessageIterator.Next() {
// L1MessageV2StartIndex is the first queue index of L1 messages that are from L1MessageQueueV2.
// Therefore, we stop reading L1 messages V1 when we reach this index.
// We need to check in every iteration if not yet set as the start index can be set in the meantime when we are reading L1 messages.
if it.v2StartIndex == nil {
it.v2StartIndex = ReadL1MessageV2StartIndex(it.db)
}

if it.v2StartIndex != nil && it.QueueIndex() >= *it.v2StartIndex {
return false
}
return true
}
return false
}

// L1MessageV2Iterator is a wrapper around L1MessageIterator that allows us to iterate over L1 messages V2.
type L1MessageV2Iterator struct {
v2StartIndex *uint64
L1MessageIterator
}

// IterateL1MessagesV2From yields a L1MessageV2Iterator with following behavior:
// - If fromQueueIndex < v2StartIndex: yield 0 messages.
// - Otherwise, simply yield all messages (guaranteed to be v2) starting from `fromQueueIndex`.
func IterateL1MessagesV2From(db ethdb.Database, fromQueueIndex uint64) L1MessageV2Iterator {
v2StartIndex := ReadL1MessageV2StartIndex(db)

return L1MessageV2Iterator{
v2StartIndex: v2StartIndex,
L1MessageIterator: iterateL1MessagesFrom(db, fromQueueIndex),
}
}

func (it *L1MessageV2Iterator) Next() bool {
if it.v2StartIndex == nil {
return false
}

return it.L1MessageIterator.Next() && it.QueueIndex() >= *it.v2StartIndex
}

// ReadL1MessagesV1From retrieves up to `maxCount` L1 messages V1 starting at `startIndex`.
// If startIndex is >= L1MessageV2StartIndex, this function returns an empty slice.
func ReadL1MessagesV1From(db ethdb.Database, startIndex, maxCount uint64) []types.L1MessageTx {
msgs := make([]types.L1MessageTx, 0, maxCount)
it := IterateL1MessagesFrom(db, startIndex)
it := IterateL1MessagesV1From(db, startIndex)
defer it.Release()

index := startIndex
Expand All @@ -223,7 +285,50 @@ func ReadL1MessagesFrom(db ethdb.Database, startIndex, maxCount uint64) []types.
// sanity check
if msg.QueueIndex != index {
log.Crit(
"Unexpected QueueIndex in ReadL1MessagesFrom",
"Unexpected QueueIndex in ReadL1MessagesV1From",
"expected", index,
"got", msg.QueueIndex,
"startIndex", startIndex,
"maxCount", maxCount,
)
}

msgs = append(msgs, msg)
index += 1
count -= 1

iteratorL1MessageSizeGauge.Update(int64(unsafe.Sizeof(msg) + uintptr(cap(msg.Data))))

if msg.QueueIndex == it.maxQueueIndex {
break
}
}

if err := it.Error(); err != nil {
log.Crit("Failed to read L1 messages", "err", err)
}

return msgs
}

// ReadL1MessagesV2From retrieves up to `maxCount` L1 messages V2 starting at `startIndex`.
// If startIndex is smaller than L1MessageV2StartIndex, this function returns an empty slice.
func ReadL1MessagesV2From(db ethdb.Database, startIndex, maxCount uint64) []types.L1MessageTx {
msgs := make([]types.L1MessageTx, 0, maxCount)

it := IterateL1MessagesV2From(db, startIndex)
defer it.Release()

index := startIndex
count := maxCount

for count > 0 && it.Next() {
msg := it.L1Message()

// sanity check
if msg.QueueIndex != index {
log.Crit(
"Unexpected QueueIndex in ReadL1MessagesV2From",
"expected", index,
"got", msg.QueueIndex,
"startIndex", startIndex,
Expand Down Expand Up @@ -275,3 +380,65 @@ func ReadFirstQueueIndexNotInL2Block(db ethdb.Reader, l2BlockHash common.Hash) *
queueIndex := binary.BigEndian.Uint64(data)
return &queueIndex
}

// WriteL1MessageV2StartIndex writes the start index of L1 messages that are from L1MessageQueueV2.
func WriteL1MessageV2StartIndex(db ethdb.KeyValueWriter, queueIndex uint64) {
value := big.NewInt(0).SetUint64(queueIndex).Bytes()

if err := db.Put(l1MessageV2StartIndexKey, value); err != nil {
log.Crit("Failed to update L1MessageV2 start index", "err", err)
}
}

// ReadL1MessageV2StartIndex retrieves the start index of L1 messages that are from L1MessageQueueV2.
func ReadL1MessageV2StartIndex(db ethdb.Reader) *uint64 {
data, err := db.Get(l1MessageV2StartIndexKey)
if err != nil && isNotFoundErr(err) {
return nil
}
if err != nil {
log.Crit("Failed to read L1MessageV2 start index from database", "err", err)
}
if len(data) == 0 {
return nil
}

number := new(big.Int).SetBytes(data)
if !number.IsUint64() {
log.Crit("Unexpected number for L1MessageV2 start index", "number", number)
}

res := number.Uint64()
return &res
}

// WriteL1MessageV2FirstL1BlockNumber writes the first synced L1 block number for L1MessageV2.
func WriteL1MessageV2FirstL1BlockNumber(db ethdb.KeyValueWriter, l1BlockNumber uint64) {
value := big.NewInt(0).SetUint64(l1BlockNumber).Bytes()

if err := db.Put(l1MessageV2FirstL1BlockNumberKey, value); err != nil {
log.Crit("Failed to update L1MessageV2 start index", "err", err)
}
}

// ReadL1MessageV2FirstL1BlockNumber retrieves the first synced L1 block number for L1MessageV2.
func ReadL1MessageV2FirstL1BlockNumber(db ethdb.Reader) *uint64 {
data, err := db.Get(l1MessageV2FirstL1BlockNumberKey)
if err != nil && isNotFoundErr(err) {
return nil
}
if err != nil {
log.Crit("Failed to read L1MessageV2 first L1 block number from database", "err", err)
}
if len(data) == 0 {
return nil
}

number := new(big.Int).SetBytes(data)
if !number.IsUint64() {
log.Crit("Unexpected number for L1MessageV2 first L1 block number", "number", number)
}

res := number.Uint64()
return &res
}
Loading
Loading