mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
512 lines
19 KiB
Go
512 lines
19 KiB
Go
package rollup_sync_service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/scroll-tech/go-ethereum/accounts/abi"
|
|
"github.com/scroll-tech/go-ethereum/common"
|
|
"github.com/scroll-tech/go-ethereum/core"
|
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
|
"github.com/scroll-tech/go-ethereum/core/types"
|
|
"github.com/scroll-tech/go-ethereum/ethdb"
|
|
"github.com/scroll-tech/go-ethereum/log"
|
|
"github.com/scroll-tech/go-ethereum/node"
|
|
"github.com/scroll-tech/go-ethereum/params"
|
|
|
|
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
|
|
"github.com/scroll-tech/go-ethereum/rollup/sync_service"
|
|
"github.com/scroll-tech/go-ethereum/rollup/types/encoding"
|
|
"github.com/scroll-tech/go-ethereum/rollup/types/encoding/codecv0"
|
|
"github.com/scroll-tech/go-ethereum/rollup/types/encoding/codecv1"
|
|
"github.com/scroll-tech/go-ethereum/rollup/withdrawtrie"
|
|
)
|
|
|
|
const (
|
|
// defaultFetchBlockRange is the number of blocks that we collect in a single eth_getLogs query.
|
|
defaultFetchBlockRange = uint64(100)
|
|
|
|
// defaultSyncInterval is the frequency at which we query for new rollup event.
|
|
defaultSyncInterval = 60 * time.Second
|
|
|
|
// defaultMaxRetries is the maximum number of retries allowed when the local node is not synced up to the required block height.
|
|
defaultMaxRetries = 20
|
|
|
|
// defaultGetBlockInRangeRetryDelay is the time delay between retries when attempting to get blocks in range.
|
|
// The service will wait for this duration if it detects that the local node has not synced up to the block height
|
|
// of a specific L1 batch finalize event.
|
|
defaultGetBlockInRangeRetryDelay = 60 * time.Second
|
|
|
|
// defaultLogInterval is the frequency at which we print the latestProcessedBlock.
|
|
defaultLogInterval = 5 * time.Minute
|
|
)
|
|
|
|
// RollupSyncService collects ScrollChain batch commit/revert/finalize events and stores metadata into db.
|
|
type RollupSyncService struct {
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
client *L1Client
|
|
db ethdb.Database
|
|
latestProcessedBlock uint64
|
|
scrollChainABI *abi.ABI
|
|
l1CommitBatchEventSignature common.Hash
|
|
l1RevertBatchEventSignature common.Hash
|
|
l1FinalizeBatchEventSignature common.Hash
|
|
bc *core.BlockChain
|
|
stack *node.Node
|
|
}
|
|
|
|
func NewRollupSyncService(ctx context.Context, genesisConfig *params.ChainConfig, db ethdb.Database, l1Client sync_service.EthClient, bc *core.BlockChain, stack *node.Node) (*RollupSyncService, 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 rollup sync service will not run")
|
|
return nil, nil
|
|
}
|
|
|
|
if genesisConfig.Scroll.L1Config == nil {
|
|
return nil, fmt.Errorf("missing L1 config in genesis")
|
|
}
|
|
|
|
scrollChainABI, err := scrollChainMetaData.GetAbi()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get scroll chain abi: %w", err)
|
|
}
|
|
|
|
client, err := newL1Client(ctx, l1Client, genesisConfig.Scroll.L1Config.L1ChainId, genesisConfig.Scroll.L1Config.ScrollChainAddress, scrollChainABI)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to initialize l1 client: %w", err)
|
|
}
|
|
|
|
// Initialize the latestProcessedBlock with the block just before the L1 deployment block.
|
|
// This serves as a default value when there's no L1 rollup events synced in the database.
|
|
var latestProcessedBlock uint64
|
|
if stack.Config().L1DeploymentBlock > 0 {
|
|
latestProcessedBlock = stack.Config().L1DeploymentBlock - 1
|
|
}
|
|
|
|
block := rawdb.ReadRollupEventSyncedL1BlockNumber(db)
|
|
if block != nil {
|
|
// restart from latest synced block number
|
|
latestProcessedBlock = *block
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
|
|
service := RollupSyncService{
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
client: client,
|
|
db: db,
|
|
latestProcessedBlock: latestProcessedBlock,
|
|
scrollChainABI: scrollChainABI,
|
|
l1CommitBatchEventSignature: scrollChainABI.Events["CommitBatch"].ID,
|
|
l1RevertBatchEventSignature: scrollChainABI.Events["RevertBatch"].ID,
|
|
l1FinalizeBatchEventSignature: scrollChainABI.Events["FinalizeBatch"].ID,
|
|
bc: bc,
|
|
stack: stack,
|
|
}
|
|
|
|
return &service, nil
|
|
}
|
|
|
|
func (s *RollupSyncService) Start() {
|
|
if s == nil {
|
|
return
|
|
}
|
|
|
|
log.Info("Starting rollup event sync background service", "latest processed block", s.latestProcessedBlock)
|
|
|
|
go func() {
|
|
syncTicker := time.NewTicker(defaultSyncInterval)
|
|
defer syncTicker.Stop()
|
|
|
|
logTicker := time.NewTicker(defaultLogInterval)
|
|
defer logTicker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-s.ctx.Done():
|
|
return
|
|
case <-syncTicker.C:
|
|
s.fetchRollupEvents()
|
|
case <-logTicker.C:
|
|
log.Info("Sync rollup events progress update", "latestProcessedBlock", s.latestProcessedBlock)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (s *RollupSyncService) Stop() {
|
|
if s == nil {
|
|
return
|
|
}
|
|
|
|
log.Info("Stopping rollup event sync background service")
|
|
|
|
if s.cancel != nil {
|
|
s.cancel()
|
|
}
|
|
}
|
|
|
|
func (s *RollupSyncService) fetchRollupEvents() {
|
|
latestConfirmed, err := s.client.getLatestFinalizedBlockNumber()
|
|
if err != nil {
|
|
log.Warn("failed to get latest confirmed block number", "err", err)
|
|
return
|
|
}
|
|
|
|
log.Trace("Sync service fetch rollup events", "latest processed block", s.latestProcessedBlock, "latest confirmed", latestConfirmed)
|
|
|
|
// query in batches
|
|
for from := s.latestProcessedBlock + 1; from <= latestConfirmed; from += defaultFetchBlockRange {
|
|
if s.ctx.Err() != nil {
|
|
log.Info("Context canceled", "reason", s.ctx.Err())
|
|
return
|
|
}
|
|
|
|
to := from + defaultFetchBlockRange - 1
|
|
if to > latestConfirmed {
|
|
to = latestConfirmed
|
|
}
|
|
|
|
logs, err := s.client.fetchRollupEventsInRange(from, to)
|
|
if err != nil {
|
|
log.Error("failed to fetch rollup events in range", "from block", from, "to block", to, "err", err)
|
|
return
|
|
}
|
|
|
|
if err := s.parseAndUpdateRollupEventLogs(logs, to); err != nil {
|
|
log.Error("failed to parse and update rollup event logs", "err", err)
|
|
return
|
|
}
|
|
|
|
s.latestProcessedBlock = to
|
|
}
|
|
}
|
|
|
|
func (s *RollupSyncService) parseAndUpdateRollupEventLogs(logs []types.Log, endBlockNumber uint64) error {
|
|
for _, vLog := range logs {
|
|
switch vLog.Topics[0] {
|
|
case s.l1CommitBatchEventSignature:
|
|
event := &L1CommitBatchEvent{}
|
|
if err := UnpackLog(s.scrollChainABI, event, "CommitBatch", vLog); err != nil {
|
|
return fmt.Errorf("failed to unpack commit rollup event log, err: %w", err)
|
|
}
|
|
batchIndex := event.BatchIndex.Uint64()
|
|
log.Trace("found new CommitBatch event", "batch index", batchIndex)
|
|
|
|
chunkBlockRanges, err := s.getChunkRanges(batchIndex, &vLog)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get chunk ranges, batch index: %v, err: %w", batchIndex, err)
|
|
}
|
|
rawdb.WriteBatchChunkRanges(s.db, batchIndex, chunkBlockRanges)
|
|
|
|
case s.l1RevertBatchEventSignature:
|
|
event := &L1RevertBatchEvent{}
|
|
if err := UnpackLog(s.scrollChainABI, event, "RevertBatch", vLog); err != nil {
|
|
return fmt.Errorf("failed to unpack revert rollup event log, err: %w", err)
|
|
}
|
|
batchIndex := event.BatchIndex.Uint64()
|
|
log.Trace("found new RevertBatch event", "batch index", batchIndex)
|
|
|
|
rawdb.DeleteBatchChunkRanges(s.db, batchIndex)
|
|
|
|
case s.l1FinalizeBatchEventSignature:
|
|
event := &L1FinalizeBatchEvent{}
|
|
if err := UnpackLog(s.scrollChainABI, event, "FinalizeBatch", vLog); err != nil {
|
|
return fmt.Errorf("failed to unpack finalized rollup event log, err: %w", err)
|
|
}
|
|
batchIndex := event.BatchIndex.Uint64()
|
|
log.Trace("found new FinalizeBatch event", "batch index", batchIndex)
|
|
|
|
parentBatchMeta, chunks, err := s.getLocalInfoForBatch(batchIndex)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get local node info, batch index: %v, err: %w", batchIndex, err)
|
|
}
|
|
|
|
endBlock, finalizedBatchMeta, err := validateBatch(event, parentBatchMeta, chunks, s.bc.Config(), s.stack)
|
|
if err != nil {
|
|
return fmt.Errorf("fatal: validateBatch failed: finalize event: %v, err: %w", event, err)
|
|
}
|
|
|
|
rawdb.WriteFinalizedL2BlockNumber(s.db, endBlock)
|
|
rawdb.WriteFinalizedBatchMeta(s.db, batchIndex, finalizedBatchMeta)
|
|
|
|
if batchIndex%100 == 0 {
|
|
log.Info("finalized batch progress", "batch index", batchIndex, "finalized l2 block height", endBlock)
|
|
}
|
|
|
|
default:
|
|
return fmt.Errorf("unknown event, topic: %v, tx hash: %v", vLog.Topics[0].Hex(), vLog.TxHash.Hex())
|
|
}
|
|
}
|
|
|
|
// note: the batch updates above are idempotent, if we crash
|
|
// before this line and reexecute the previous steps, we will
|
|
// get the same result.
|
|
rawdb.WriteRollupEventSyncedL1BlockNumber(s.db, endBlockNumber)
|
|
return nil
|
|
}
|
|
|
|
func (s *RollupSyncService) getLocalInfoForBatch(batchIndex uint64) (*rawdb.FinalizedBatchMeta, []*encoding.Chunk, error) {
|
|
chunkBlockRanges := rawdb.ReadBatchChunkRanges(s.db, batchIndex)
|
|
if len(chunkBlockRanges) == 0 {
|
|
return nil, nil, fmt.Errorf("failed to get batch chunk ranges, empty chunk block ranges")
|
|
}
|
|
|
|
endBlockNumber := chunkBlockRanges[len(chunkBlockRanges)-1].EndBlockNumber
|
|
for i := 0; i < defaultMaxRetries; i++ {
|
|
if s.ctx.Err() != nil {
|
|
log.Info("Context canceled", "reason", s.ctx.Err())
|
|
return nil, nil, s.ctx.Err()
|
|
}
|
|
|
|
localSyncedBlockHeight := s.bc.CurrentBlock().Number().Uint64()
|
|
if localSyncedBlockHeight >= endBlockNumber {
|
|
break // ready to proceed, exit retry loop
|
|
}
|
|
|
|
log.Debug("local node is not synced up to the required block height, waiting for next retry",
|
|
"retries", i+1, "local synced block height", localSyncedBlockHeight, "required end block number", endBlockNumber)
|
|
time.Sleep(defaultGetBlockInRangeRetryDelay)
|
|
}
|
|
|
|
localSyncedBlockHeight := s.bc.CurrentBlock().Number().Uint64()
|
|
if localSyncedBlockHeight < endBlockNumber {
|
|
return nil, nil, fmt.Errorf("local node is not synced up to the required block height: %v, local synced block height: %v", endBlockNumber, localSyncedBlockHeight)
|
|
}
|
|
|
|
chunks := make([]*encoding.Chunk, len(chunkBlockRanges))
|
|
for i, cr := range chunkBlockRanges {
|
|
chunks[i] = &encoding.Chunk{Blocks: make([]*encoding.Block, cr.EndBlockNumber-cr.StartBlockNumber+1)}
|
|
for j := cr.StartBlockNumber; j <= cr.EndBlockNumber; j++ {
|
|
block := s.bc.GetBlockByNumber(j)
|
|
if block == nil {
|
|
return nil, nil, fmt.Errorf("failed to get block by number: %v", i)
|
|
}
|
|
txData := encoding.TxsToTxsData(block.Transactions())
|
|
state, err := s.bc.StateAt(block.Root())
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to get block state, block: %v, err: %w", block.Hash().Hex(), err)
|
|
}
|
|
withdrawRoot := withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, state)
|
|
chunks[i].Blocks[j-cr.StartBlockNumber] = &encoding.Block{
|
|
Header: block.Header(),
|
|
Transactions: txData,
|
|
WithdrawRoot: withdrawRoot,
|
|
}
|
|
}
|
|
}
|
|
|
|
// get metadata of parent batch: default to genesis batch metadata.
|
|
parentBatchMeta := &rawdb.FinalizedBatchMeta{}
|
|
if batchIndex > 0 {
|
|
parentBatchMeta = rawdb.ReadFinalizedBatchMeta(s.db, batchIndex-1)
|
|
}
|
|
|
|
return parentBatchMeta, chunks, nil
|
|
}
|
|
|
|
func (s *RollupSyncService) getChunkRanges(batchIndex uint64, vLog *types.Log) ([]*rawdb.ChunkBlockRange, error) {
|
|
if batchIndex == 0 {
|
|
return []*rawdb.ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 0}}, nil
|
|
}
|
|
|
|
tx, _, err := s.client.client.TransactionByHash(s.ctx, vLog.TxHash)
|
|
if err != nil {
|
|
log.Debug("failed to get transaction by hash, probably an unindexed transaction, fetching the whole block to get the transaction",
|
|
"tx hash", vLog.TxHash.Hex(), "block number", vLog.BlockNumber, "block hash", vLog.BlockHash.Hex(), "err", err)
|
|
block, err := s.client.client.BlockByHash(s.ctx, vLog.BlockHash)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get block by hash, block number: %v, block hash: %v, err: %w", vLog.BlockNumber, vLog.BlockHash.Hex(), err)
|
|
}
|
|
|
|
found := false
|
|
for _, txInBlock := range block.Transactions() {
|
|
if txInBlock.Hash() == vLog.TxHash {
|
|
tx = txInBlock
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return nil, fmt.Errorf("transaction not found in the block, tx hash: %v, block number: %v, block hash: %v", vLog.TxHash.Hex(), vLog.BlockNumber, vLog.BlockHash.Hex())
|
|
}
|
|
}
|
|
|
|
return s.decodeChunkBlockRanges(tx.Data())
|
|
}
|
|
|
|
// decodeChunkBlockRanges decodes chunks in a batch based on the commit batch transaction's calldata.
|
|
func (s *RollupSyncService) decodeChunkBlockRanges(txData []byte) ([]*rawdb.ChunkBlockRange, error) {
|
|
const methodIDLength = 4
|
|
if len(txData) < methodIDLength {
|
|
return nil, fmt.Errorf("transaction data is too short, length of tx data: %v, minimum length required: %v", len(txData), methodIDLength)
|
|
}
|
|
|
|
method, err := s.scrollChainABI.MethodById(txData[:methodIDLength])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get method by ID, ID: %v, err: %w", txData[:methodIDLength], err)
|
|
}
|
|
|
|
values, err := method.Inputs.Unpack(txData[methodIDLength:])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unpack transaction data using ABI, tx data: %v, err: %w", txData, err)
|
|
}
|
|
|
|
type commitBatchArgs struct {
|
|
Version uint8
|
|
ParentBatchHeader []byte
|
|
Chunks [][]byte
|
|
SkippedL1MessageBitmap []byte
|
|
}
|
|
var args commitBatchArgs
|
|
if err = method.Inputs.Copy(&args, values); err != nil {
|
|
return nil, fmt.Errorf("failed to decode calldata into commitBatch args, values: %+v, err: %w", values, err)
|
|
}
|
|
|
|
return decodeBlockRangesFromEncodedChunks(encoding.CodecVersion(args.Version), args.Chunks)
|
|
}
|
|
|
|
// validateBatch verifies the consistency between the L1 contract and L2 node data.
|
|
// The function will terminate the node and exit if any consistency check fails.
|
|
// It returns the number of the end block, a finalized batch meta data, and an error if any.
|
|
func validateBatch(event *L1FinalizeBatchEvent, parentBatchMeta *rawdb.FinalizedBatchMeta, chunks []*encoding.Chunk, chainCfg *params.ChainConfig, stack *node.Node) (uint64, *rawdb.FinalizedBatchMeta, error) {
|
|
if len(chunks) == 0 {
|
|
return 0, nil, fmt.Errorf("invalid argument: length of chunks is 0, batch index: %v", event.BatchIndex.Uint64())
|
|
}
|
|
|
|
startChunk := chunks[0]
|
|
if len(startChunk.Blocks) == 0 {
|
|
return 0, nil, fmt.Errorf("invalid argument: block count of start chunk is 0, batch index: %v", event.BatchIndex.Uint64())
|
|
}
|
|
startBlock := startChunk.Blocks[0]
|
|
|
|
endChunk := chunks[len(chunks)-1]
|
|
if len(endChunk.Blocks) == 0 {
|
|
return 0, nil, fmt.Errorf("invalid argument: block count of end chunk is 0, batch index: %v", event.BatchIndex.Uint64())
|
|
}
|
|
endBlock := endChunk.Blocks[len(endChunk.Blocks)-1]
|
|
|
|
localStateRoot := endBlock.Header.Root
|
|
if localStateRoot != event.StateRoot {
|
|
log.Error("State root mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentBatchMeta.BatchHash.Hex(), "l1 finalized state root", event.StateRoot.Hex(), "l2 state root", localStateRoot.Hex())
|
|
stack.Close()
|
|
os.Exit(1)
|
|
}
|
|
|
|
localWithdrawRoot := endBlock.WithdrawRoot
|
|
if localWithdrawRoot != event.WithdrawRoot {
|
|
log.Error("Withdraw root mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentBatchMeta.BatchHash.Hex(), "l1 finalized withdraw root", event.WithdrawRoot.Hex(), "l2 withdraw root", localWithdrawRoot.Hex())
|
|
stack.Close()
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Note: All params of batch are calculated locally based on the block data.
|
|
batch := &encoding.Batch{
|
|
Index: event.BatchIndex.Uint64(),
|
|
TotalL1MessagePoppedBefore: parentBatchMeta.TotalL1MessagePopped,
|
|
ParentBatchHash: parentBatchMeta.BatchHash,
|
|
Chunks: chunks,
|
|
}
|
|
|
|
var localBatchHash common.Hash
|
|
if !chainCfg.IsBernoulli(startBlock.Header.Number) {
|
|
daBatch, err := codecv0.NewDABatch(batch)
|
|
if err != nil {
|
|
return 0, nil, fmt.Errorf("failed to create codecv0 DA batch, batch index: %v, err: %w", event.BatchIndex.Uint64(), err)
|
|
}
|
|
localBatchHash = daBatch.Hash()
|
|
} else {
|
|
daBatch, err := codecv1.NewDABatch(batch)
|
|
if err != nil {
|
|
return 0, nil, fmt.Errorf("failed to create codecv1 DA batch, batch index: %v, err: %w", event.BatchIndex.Uint64(), err)
|
|
}
|
|
localBatchHash = daBatch.Hash()
|
|
}
|
|
|
|
// Note: If the batch headers match, this ensures the consistency of blocks and transactions
|
|
// (including skipped transactions) between L1 and L2.
|
|
if localBatchHash != event.BatchHash {
|
|
log.Error("Batch hash mismatch", "batch index", event.BatchIndex.Uint64(), "start block", startBlock.Header.Number.Uint64(), "end block", endBlock.Header.Number.Uint64(), "parent batch hash", parentBatchMeta.BatchHash.Hex(), "parent TotalL1MessagePopped", parentBatchMeta.TotalL1MessagePopped, "l1 finalized batch hash", event.BatchHash.Hex(), "l2 batch hash", localBatchHash.Hex())
|
|
chunksJson, err := json.Marshal(chunks)
|
|
if err != nil {
|
|
log.Error("marshal chunks failed", "err", err)
|
|
}
|
|
log.Error("Chunks", "chunks", string(chunksJson))
|
|
stack.Close()
|
|
os.Exit(1)
|
|
}
|
|
|
|
totalL1MessagePopped := parentBatchMeta.TotalL1MessagePopped
|
|
for _, chunk := range chunks {
|
|
totalL1MessagePopped += chunk.NumL1Messages(totalL1MessagePopped)
|
|
}
|
|
finalizedBatchMeta := &rawdb.FinalizedBatchMeta{
|
|
BatchHash: localBatchHash,
|
|
TotalL1MessagePopped: totalL1MessagePopped,
|
|
StateRoot: localStateRoot,
|
|
WithdrawRoot: localWithdrawRoot,
|
|
}
|
|
return endBlock.Header.Number.Uint64(), finalizedBatchMeta, nil
|
|
}
|
|
|
|
// decodeBlockRangesFromEncodedChunks decodes the provided chunks into a list of block ranges.
|
|
func decodeBlockRangesFromEncodedChunks(codecVersion encoding.CodecVersion, chunks [][]byte) ([]*rawdb.ChunkBlockRange, error) {
|
|
var chunkBlockRanges []*rawdb.ChunkBlockRange
|
|
for _, chunk := range chunks {
|
|
if len(chunk) < 1 {
|
|
return nil, fmt.Errorf("invalid chunk, length is less than 1")
|
|
}
|
|
|
|
numBlocks := int(chunk[0])
|
|
|
|
switch codecVersion {
|
|
case encoding.CodecV0:
|
|
if len(chunk) < 1+numBlocks*60 {
|
|
return nil, fmt.Errorf("invalid chunk byte length, expected: %v, got: %v", 1+numBlocks*60, len(chunk))
|
|
}
|
|
daBlocks := make([]*codecv0.DABlock, numBlocks)
|
|
for i := 0; i < numBlocks; i++ {
|
|
startIdx := 1 + i*60 // add 1 to skip numBlocks byte
|
|
endIdx := startIdx + 60
|
|
daBlock, err := codecv0.DecodeDABlock(chunk[startIdx:endIdx])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
daBlocks[i] = daBlock
|
|
}
|
|
|
|
chunkBlockRanges = append(chunkBlockRanges, &rawdb.ChunkBlockRange{
|
|
StartBlockNumber: daBlocks[0].BlockNumber,
|
|
EndBlockNumber: daBlocks[len(daBlocks)-1].BlockNumber,
|
|
})
|
|
case encoding.CodecV1:
|
|
if len(chunk) != 1+numBlocks*60 {
|
|
return nil, fmt.Errorf("invalid chunk byte length, expected: %v, got: %v", 1+numBlocks*60, len(chunk))
|
|
}
|
|
daBlocks := make([]*codecv1.DABlock, numBlocks)
|
|
for i := 0; i < numBlocks; i++ {
|
|
startIdx := 1 + i*60 // add 1 to skip numBlocks byte
|
|
endIdx := startIdx + 60
|
|
daBlock, err := codecv1.DecodeDABlock(chunk[startIdx:endIdx])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
daBlocks[i] = daBlock
|
|
}
|
|
|
|
chunkBlockRanges = append(chunkBlockRanges, &rawdb.ChunkBlockRange{
|
|
StartBlockNumber: daBlocks[0].BlockNumber,
|
|
EndBlockNumber: daBlocks[len(daBlocks)-1].BlockNumber,
|
|
})
|
|
default:
|
|
return nil, fmt.Errorf("unexpected batch version %v", codecVersion)
|
|
}
|
|
}
|
|
return chunkBlockRanges, nil
|
|
}
|