Skip to content

Commit

Permalink
feat(rollup): add fallback if TransactionByHash fails in getting comm…
Browse files Browse the repository at this point in the history
…itBatch calldata (ethereum#601)

* feat(rollup): add fallback if TransactionByHash fails in getting commitBatch calldata

* Update rollup/rollup_sync_service/rollup_sync_service.go

Co-authored-by: Péter Garamvölgyi <peter@scroll.io>

* change log.Warn to log.Debug

---------

Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
  • Loading branch information
colinlyguo and Thegaram authored Dec 18, 2023
1 parent 38a3a9c commit b235afb
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 28 deletions.
26 changes: 13 additions & 13 deletions core/rawdb/accessors_rollup_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type FinalizedBatchMeta struct {
func WriteRollupEventSyncedL1BlockNumber(db ethdb.KeyValueWriter, l1BlockNumber uint64) {
value := big.NewInt(0).SetUint64(l1BlockNumber).Bytes()
if err := db.Put(rollupEventSyncedL1BlockNumberKey, value); err != nil {
log.Crit("failed to store rollup event synced L1 block number for rollup event", "err", err)
log.Crit("failed to store rollup event synced L1 block number for rollup event", "L1 block number", l1BlockNumber, "value", value, "err", err)
}
}

Expand All @@ -44,7 +44,7 @@ func ReadRollupEventSyncedL1BlockNumber(db ethdb.Reader) *uint64 {

number := new(big.Int).SetBytes(data)
if !number.IsUint64() {
log.Crit("unexpected rollup event synced L1 block number in database", "number", number)
log.Crit("unexpected rollup event synced L1 block number in database", "data", data, "number", number)
}

rollupEventSyncedL1BlockNumber := number.Uint64()
Expand All @@ -54,12 +54,12 @@ func ReadRollupEventSyncedL1BlockNumber(db ethdb.Reader) *uint64 {
// WriteBatchChunkRanges writes the block ranges for each chunk within a batch to the database.
// It serializes the chunk ranges using RLP and stores them under a key derived from the batch index.
func WriteBatchChunkRanges(db ethdb.KeyValueWriter, batchIndex uint64, chunkBlockRanges []*ChunkBlockRange) {
bytes, err := rlp.EncodeToBytes(chunkBlockRanges)
value, err := rlp.EncodeToBytes(chunkBlockRanges)
if err != nil {
log.Crit("failed to RLP encode batch chunk ranges", "batch index", batchIndex, "err", err)
}
if err := db.Put(batchChunkRangesKey(batchIndex), bytes); err != nil {
log.Crit("failed to store batch chunk ranges", "batch index", batchIndex, "err", err)
if err := db.Put(batchChunkRangesKey(batchIndex), value); err != nil {
log.Crit("failed to store batch chunk ranges", "batch index", batchIndex, "value", value, "err", err)
}
}

Expand Down Expand Up @@ -92,12 +92,12 @@ func ReadBatchChunkRanges(db ethdb.Reader, batchIndex uint64) []*ChunkBlockRange
// WriteFinalizedBatchMeta stores the metadata of a finalized batch in the database.
func WriteFinalizedBatchMeta(db ethdb.KeyValueWriter, batchIndex uint64, finalizedBatchMeta *FinalizedBatchMeta) {
var err error
bytes, err := rlp.EncodeToBytes(finalizedBatchMeta)
value, err := rlp.EncodeToBytes(finalizedBatchMeta)
if err != nil {
log.Crit("failed to RLP encode batch metadata", "batch index", batchIndex, "err", err)
log.Crit("failed to RLP encode batch metadata", "batch index", batchIndex, "finalized batch meta", finalizedBatchMeta, "err", err)
}
if err := db.Put(batchMetaKey(batchIndex), bytes); err != nil {
log.Crit("failed to store batch metadata", "batch index", batchIndex, "err", err)
if err := db.Put(batchMetaKey(batchIndex), value); err != nil {
log.Crit("failed to store batch metadata", "batch index", batchIndex, "value", value, "err", err)
}
}

Expand All @@ -108,7 +108,7 @@ func ReadFinalizedBatchMeta(db ethdb.Reader, batchIndex uint64) *FinalizedBatchM
return nil
}
if err != nil {
log.Crit("failed to read finalized batch metadata from database", "err", err)
log.Crit("failed to read finalized batch metadata from database", "batch index", batchIndex, "err", err)
}

fbm := new(FinalizedBatchMeta)
Expand All @@ -122,7 +122,7 @@ func ReadFinalizedBatchMeta(db ethdb.Reader, batchIndex uint64) *FinalizedBatchM
func WriteFinalizedL2BlockNumber(db ethdb.KeyValueWriter, l2BlockNumber uint64) {
value := big.NewInt(0).SetUint64(l2BlockNumber).Bytes()
if err := db.Put(finalizedL2BlockNumberKey, value); err != nil {
log.Crit("failed to store finalized L2 block number for rollup event", "err", err)
log.Crit("failed to store finalized L2 block number for rollup event", "L2 block number", l2BlockNumber, "value", value, "err", err)
}
}

Expand All @@ -133,12 +133,12 @@ func ReadFinalizedL2BlockNumber(db ethdb.Reader) *uint64 {
return nil
}
if err != nil {
log.Crit("failed to read finalized L2 block number from database", "err", err)
log.Crit("failed to read finalized L2 block number from database", "key", finalizedL2BlockNumberKey, "err", err)
}

number := new(big.Int).SetBytes(data)
if !number.IsUint64() {
log.Crit("unexpected finalized L2 block number in database", "number", number)
log.Crit("unexpected finalized L2 block number in database", "data", data, "number", number)
}

finalizedL2BlockNumber := number.Uint64()
Expand Down
2 changes: 1 addition & 1 deletion params/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
const (
VersionMajor = 5 // Major version component of the current release
VersionMinor = 1 // Minor version component of the current release
VersionPatch = 6 // Patch version component of the current release
VersionPatch = 7 // Patch version component of the current release
VersionMeta = "mainnet" // Version metadata to append to the version string
)

Expand Down
2 changes: 1 addition & 1 deletion rollup/rollup_sync_service/chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func DecodeChunkBlockRanges(chunks [][]byte) ([]*rawdb.ChunkBlockRange, error) {

numBlocks := int(chunk[0])
if len(chunk) < 1+numBlocks*blockContextByteSize {
return nil, fmt.Errorf("chunk size doesn't match with numBlocks")
return nil, fmt.Errorf("chunk size doesn't match with numBlocks, byte length of chunk: %v, expected length: %v", len(chunk), 1+numBlocks*blockContextByteSize)
}

blockContexts := make([]*BlockContext, numBlocks)
Expand Down
4 changes: 4 additions & 0 deletions rollup/rollup_sync_service/l1client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ func (m *mockEthClient) TransactionByHash(ctx context.Context, txHash common.Has
}
return &tx, false, nil
}

func (m *mockEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
return nil, nil
}
46 changes: 33 additions & 13 deletions rollup/rollup_sync_service/rollup_sync_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func (s *RollupSyncService) parseAndUpdateRollupEventLogs(logs []types.Log, endB

chunkBlockRanges, err := s.getChunkRanges(batchIndex, &vLog)
if err != nil {
return fmt.Errorf("failed to get chunk ranges, err: %w", err)
return fmt.Errorf("failed to get chunk ranges, batch index: %v, err: %w", batchIndex, err)
}
rawdb.WriteBatchChunkRanges(s.db, batchIndex, chunkBlockRanges)

Expand Down Expand Up @@ -311,9 +311,26 @@ func (s *RollupSyncService) getChunkRanges(batchIndex uint64, vLog *types.Log) (
return []*rawdb.ChunkBlockRange{{StartBlockNumber: 0, EndBlockNumber: 0}}, nil
}

tx, _, err := s.client.client.TransactionByHash(context.Background(), vLog.TxHash)
tx, _, err := s.client.client.TransactionByHash(s.ctx, vLog.TxHash)
if err != nil {
return nil, fmt.Errorf("failed to get transaction, err: %w", err)
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())
Expand All @@ -323,17 +340,17 @@ func (s *RollupSyncService) getChunkRanges(batchIndex uint64, vLog *types.Log) (
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")
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[:4], err)
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: %v", err)
return nil, fmt.Errorf("failed to unpack transaction data using ABI, tx data: %v, err: %w", txData, err)
}

type commitBatchArgs struct {
Expand All @@ -345,11 +362,11 @@ func (s *RollupSyncService) decodeChunkBlockRanges(txData []byte) ([]*rawdb.Chun
var args commitBatchArgs
err = method.Inputs.Copy(&args, values)
if err != nil {
return nil, fmt.Errorf("failed to decode calldata into commitBatch args, err: %w", err)
return nil, fmt.Errorf("failed to decode calldata into commitBatch args, values: %+v, err: %w", values, err)
}

if args.Version != batchHeaderVersion {
return nil, fmt.Errorf("unexpected batch version, expected: %d, got: %v", batchHeaderVersion, args.Version)
return nil, fmt.Errorf("unexpected batch version, expected: %v, got: %v", batchHeaderVersion, args.Version)
}

return DecodeChunkBlockRanges(args.Chunks)
Expand All @@ -360,18 +377,18 @@ func (s *RollupSyncService) decodeChunkBlockRanges(txData []byte) ([]*rawdb.Chun
// 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 []*Chunk) (uint64, *rawdb.FinalizedBatchMeta, error) {
if len(chunks) == 0 {
return 0, nil, fmt.Errorf("invalid argument: length of chunks is 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")
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")
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]

Expand All @@ -392,15 +409,18 @@ func validateBatch(event *L1FinalizeBatchEvent, parentBatchMeta *rawdb.Finalized
// Note: All params for NewBatchHeader are calculated locally based on the block data.
batchHeader, err := NewBatchHeader(batchHeaderVersion, event.BatchIndex.Uint64(), parentBatchMeta.TotalL1MessagePopped, parentBatchMeta.BatchHash, chunks)
if err != nil {
return 0, nil, fmt.Errorf("failed to construct batch header, err: %w", err)
return 0, nil, fmt.Errorf("failed to construct batch header, batch index: %v, err: %w", event.BatchIndex.Uint64(), err)
}

// Note: If the batch headers match, this ensures the consistency of blocks and transactions
// (including skipped transactions) between L1 and L2.
localBatchHash := batchHeader.Hash()
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, _ := json.Marshal(chunks)
chunksJson, err := json.Marshal(chunks)
if err != nil {
log.Error("marshal chunks failed", "err", err)
}
log.Error("Chunks", "chunks", string(chunksJson))
syscall.Kill(os.Getpid(), syscall.SIGTERM)
return 0, nil, fmt.Errorf("batch hash mismatch")
Expand Down
1 change: 1 addition & 0 deletions rollup/sync_service/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ type EthClient interface {
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error)
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
}

0 comments on commit b235afb

Please sign in to comment.