go-ethereum/rollup/da_syncer/da/commitV7.go
Jonas Theis 40ebbd6491
feat(L1 follower): adjust to recent CodecV7 and contract changes (#1120)
* port changes from #1013

* port changes from #1068

* go.mod tidy

* fix compile error

* fix goimports

* fix log

* address review comments

* upgrade golang.org/x/net to 0.23.0

* port changes from #1018

* fix tests and linter errors

* address review comments

* refactor rollup sync service / verifier to use CalldataBlobSource to retrieve data from L1

* add configuration and initialize blob clients

* fix unit tests

* remove unused code

* address review comments

* address more review comments

* implement first version of new da-codec and to handle multiple batches submitted in one transaction

* add CommitBatchDAV7 and handle multiple commit events submitted in a single transactions

* fix bug due to previous batch being empty when processing the first batch within a set of batches

* Allow using MPT

* update to latest da-codec

* add field to CommittedBatchMeta to store LastL1MessageQueueHash for CodecV7 batches

* adjust rollup verifier to support CodecV7 batches

* address review comments

* fix issues after merge

* go mod tidy

* fix unit tests

* update da-codec

* add test TestValidateBatchCodecV7

* go mod tidy

* do not log error on shutdown

* add sanity check for version to deserialization of committedBatchMetaV7

* port changes from #1073

* chore: auto version bump [bot]

* address review comments

* add more logs

* disable ENRUpdater if DA sync mode is enabled

* exit pipeline if context is cancelled

* correctly handle override by setting the head of the chain to the parent's height so that created blocks will always become part of canonical chain

* fix error with genesis event being nil

* chore: auto version bump [bot]

* chore: auto version bump [bot]

* adjust to renaming in CodecV7

* implement carrying forward of L1 MessageQueue index

* fix issue after upgrading from old storage to new format where batchIndex was 0 and all batches would be skipped

* add new RevertBatch event

* add commitBatches to be able to read calldata of CodecV7/EuclidV2 committed batches

* implement finding of L1 message queue height for initial batch in recovery mode

* add sanity checks for computed batches from events and batch hashes given via calldata from commit transaction

* update ScrollChain ABI

* chore: auto version bump [bot]

* remove initial batch form DAQueue

* go mod tidy

* fix underflow bug when l1DeploymentBlock==0

* fix bug with wrong parentBatchHash of first batch of batches submitted in a single tx

* update to latest da-codec

* address review comments

* address review comments

* fix bug when l1MessageV2StartIndex==0 serialized to [] (empty slice) which would always be decoded to non-existing instead of 0

* chore: auto version bump [bot]

* cache go dependencies in Dockerfile.mockccc

* chore: auto version bump [bot]

* add INFO log when reverting rollup events in L1 follower mode

* change LastProcessedMessageQueueIndex of finalize event to  totalL1MessagesPoppedOverall

* cleanup

* chore: auto version bump [bot]

---------

Co-authored-by: Ömer Faruk Irmak <omerfirmak@gmail.com>
Co-authored-by: Thegaram <Thegaram@users.noreply.github.com>
Co-authored-by: jonastheis <jonastheis@users.noreply.github.com>
2025-03-03 13:14:32 +01:00

217 lines
6.5 KiB
Go

package da
import (
"context"
"crypto/sha256"
"fmt"
"github.com/scroll-tech/da-codec/encoding"
"github.com/scroll-tech/go-ethereum/core/rawdb"
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/blob_client"
"github.com/scroll-tech/go-ethereum/rollup/da_syncer/serrors"
"github.com/scroll-tech/go-ethereum/rollup/l1"
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/crypto/kzg4844"
"github.com/scroll-tech/go-ethereum/ethdb"
)
type CommitBatchDAV7 struct {
db ethdb.Database
version encoding.CodecVersion
batchIndex uint64
versionedHashes []common.Hash
blobPayload encoding.DABlobPayload
parentTotalL1MessagePopped uint64
l1MessagesPopped uint64
event *l1.CommitBatchEvent
}
func NewCommitBatchDAV7(ctx context.Context, db ethdb.Database,
blobClient blob_client.BlobClient,
codec encoding.Codec,
commitEvent *l1.CommitBatchEvent,
blobHash common.Hash,
parentBatchHash common.Hash,
l1BlockTime uint64,
) (*CommitBatchDAV7, error) {
calculatedBatch, err := codec.NewDABatchFromParams(commitEvent.BatchIndex().Uint64(), blobHash, parentBatchHash)
if err != nil {
return nil, fmt.Errorf("failed to create new DA batch from params, batch index: %d, err: %w", commitEvent.BatchIndex().Uint64(), err)
}
if calculatedBatch.Hash() != commitEvent.BatchHash() {
return nil, fmt.Errorf("calculated batch hash is not equal to the one from commit event: %s, calculated hash: %s", commitEvent.BatchHash().Hex(), calculatedBatch.Hash().Hex())
}
blob, err := blobClient.GetBlobByVersionedHashAndBlockTime(ctx, blobHash, l1BlockTime)
if err != nil {
return nil, fmt.Errorf("failed to fetch blob from blob client, err: %w", err)
}
if blob == nil {
return nil, fmt.Errorf("unexpected, blob == nil and err != nil, batch index: %d, versionedHash: %s, blobClient: %T", commitEvent.BatchIndex().Uint64(), blobHash.Hex(), blobClient)
}
// compute blob versioned hash and compare with one from tx
c, err := kzg4844.BlobToCommitment(blob)
if err != nil {
return nil, fmt.Errorf("failed to create blob commitment: %w", err)
}
blobVersionedHash := common.Hash(kzg4844.CalcBlobHashV1(sha256.New(), &c))
if blobVersionedHash != blobHash {
return nil, fmt.Errorf("blobVersionedHash from blob source is not equal to versionedHash from tx, correct versioned hash: %s, fetched blob hash: %s", blobHash.Hex(), blobVersionedHash.Hex())
}
blobPayload, err := codec.DecodeBlob(blob)
if err != nil {
return nil, fmt.Errorf("failed to decode blob: %w", err)
}
return &CommitBatchDAV7{
db: db,
version: codec.Version(),
batchIndex: commitEvent.BatchIndex().Uint64(),
versionedHashes: []common.Hash{blobVersionedHash},
blobPayload: blobPayload,
l1MessagesPopped: getL1MessagesPoppedFromBlocks(blobPayload.Blocks()),
event: commitEvent,
}, nil
}
func (c *CommitBatchDAV7) Type() Type {
return CommitBatchWithBlobType
}
func (c *CommitBatchDAV7) BlobVersionedHashes() []common.Hash {
return c.versionedHashes
}
func (c *CommitBatchDAV7) BatchIndex() uint64 {
return c.batchIndex
}
func (c *CommitBatchDAV7) L1BlockNumber() uint64 {
return c.event.BlockNumber()
}
func (c *CommitBatchDAV7) CompareTo(other Entry) int {
if c.BatchIndex() < other.BatchIndex() {
return -1
} else if c.BatchIndex() > other.BatchIndex() {
return 1
}
return 0
}
func (c *CommitBatchDAV7) Event() l1.RollupEvent {
return c.event
}
func (c *CommitBatchDAV7) Blocks() ([]*PartialBlock, error) {
initialL1MessageIndex := c.parentTotalL1MessagePopped
l1Txs, err := getL1MessagesV7(c.db, c.blobPayload.Blocks(), initialL1MessageIndex)
if err != nil {
return nil, fmt.Errorf("failed to get L1 messages for v7 batch %d: %w", c.event.BatchIndex().Uint64(), err)
}
var blocks []*PartialBlock
for i, daBlock := range c.blobPayload.Blocks() {
// create txs
txs := make(types.Transactions, 0, daBlock.NumTransactions())
// insert L1 messages
txs = append(txs, l1Txs[i]...)
// TODO: sanity check L1 messages with prev and post hashes
// insert L2 txs
txs = append(txs, c.blobPayload.Transactions()[i]...)
block := NewPartialBlock(
&PartialHeader{
Number: daBlock.Number(),
Time: daBlock.Timestamp(),
BaseFee: daBlock.BaseFee(),
GasLimit: daBlock.GasLimit(),
Difficulty: 1, // difficulty is enforced to be 1
ExtraData: []byte{}, // extra data is enforced to be empty or at least excluded from the block hash
},
txs)
blocks = append(blocks, block)
}
return blocks, nil
}
func (c *CommitBatchDAV7) SetParentTotalL1MessagePopped(totalL1MessagePopped uint64) {
c.parentTotalL1MessagePopped = totalL1MessagePopped
}
func (c *CommitBatchDAV7) TotalL1MessagesPopped() uint64 {
return c.parentTotalL1MessagePopped + c.l1MessagesPopped
}
func (c *CommitBatchDAV7) L1MessagesPoppedInBatch() uint64 {
return c.l1MessagesPopped
}
func (c *CommitBatchDAV7) Version() encoding.CodecVersion {
return c.version
}
func (c *CommitBatchDAV7) Chunks() []*encoding.DAChunkRawTx {
return []*encoding.DAChunkRawTx{
{
Blocks: c.blobPayload.Blocks(),
Transactions: c.blobPayload.Transactions(),
},
}
}
func getL1MessagesV7(db ethdb.Database, blocks []encoding.DABlock, initialL1MessageIndex uint64) ([]types.Transactions, error) {
allTxs := make([]types.Transactions, 0, len(blocks))
messageIndex := initialL1MessageIndex
totalL1Messages := 0
for _, block := range blocks {
var txsPerBlock types.Transactions
for i := messageIndex; i < messageIndex+uint64(block.NumL1Messages()); i++ {
l1Tx := rawdb.ReadL1Message(db, i)
if l1Tx == nil {
log.Info("L1 message not yet available", "index", i)
// message not yet available
// we return serrors.EOFError as this will be handled in the syncing pipeline with a backoff and retry
return nil, serrors.EOFError
}
txsPerBlock = append(txsPerBlock, types.NewTx(l1Tx))
}
totalL1Messages += int(block.NumL1Messages())
messageIndex += uint64(block.NumL1Messages())
allTxs = append(allTxs, txsPerBlock)
}
if messageIndex != initialL1MessageIndex+uint64(totalL1Messages) {
return nil, fmt.Errorf("unexpected message index: %d, expected: %d", messageIndex, initialL1MessageIndex+uint64(totalL1Messages))
}
return allTxs, nil
}
func getL1MessagesPoppedFromBlocks(blocks []encoding.DABlock) uint64 {
var totalL1MessagePopped uint64
for _, block := range blocks {
totalL1MessagePopped += uint64(block.NumL1Messages())
}
return totalL1MessagePopped
}