mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
* 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>
155 lines
4.6 KiB
Go
155 lines
4.6 KiB
Go
package rollup_sync_service
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/scroll-tech/go-ethereum/common"
|
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
|
"github.com/scroll-tech/go-ethereum/core/types"
|
|
"github.com/scroll-tech/go-ethereum/crypto"
|
|
)
|
|
|
|
// Chunk contains blocks to be encoded
|
|
type Chunk struct {
|
|
Blocks []*WrappedBlock `json:"blocks"`
|
|
}
|
|
|
|
// NumL1Messages returns the number of L1 messages in this chunk.
|
|
// This number is the sum of included and skipped L1 messages.
|
|
func (c *Chunk) NumL1Messages(totalL1MessagePoppedBefore uint64) uint64 {
|
|
var numL1Messages uint64
|
|
for _, block := range c.Blocks {
|
|
numL1MessagesInBlock := block.numL1Messages(totalL1MessagePoppedBefore)
|
|
numL1Messages += numL1MessagesInBlock
|
|
totalL1MessagePoppedBefore += numL1MessagesInBlock
|
|
}
|
|
// TODO: cache results
|
|
return numL1Messages
|
|
}
|
|
|
|
// Encode encodes the Chunk into RollupV2 Chunk Encoding.
|
|
func (c *Chunk) Encode(totalL1MessagePoppedBefore uint64) ([]byte, error) {
|
|
numBlocks := len(c.Blocks)
|
|
|
|
if numBlocks > 255 {
|
|
return nil, errors.New("number of blocks exceeds 1 byte")
|
|
}
|
|
if numBlocks == 0 {
|
|
return nil, errors.New("number of blocks is 0")
|
|
}
|
|
|
|
var chunkBytes []byte
|
|
chunkBytes = append(chunkBytes, byte(numBlocks))
|
|
|
|
var l2TxDataBytes []byte
|
|
|
|
for _, block := range c.Blocks {
|
|
blockBytes, err := block.Encode(totalL1MessagePoppedBefore)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to encode block: %v", err)
|
|
}
|
|
totalL1MessagePoppedBefore += block.numL1Messages(totalL1MessagePoppedBefore)
|
|
|
|
if len(blockBytes) != 60 {
|
|
return nil, fmt.Errorf("block encoding is not 60 bytes long %x", len(blockBytes))
|
|
}
|
|
|
|
chunkBytes = append(chunkBytes, blockBytes...)
|
|
|
|
// Append rlp-encoded l2Txs
|
|
for _, txData := range block.Transactions {
|
|
if txData.Type == types.L1MessageTxType {
|
|
continue
|
|
}
|
|
rlpTxData, err := convertTxDataToRLPEncoding(txData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var txLen [4]byte
|
|
binary.BigEndian.PutUint32(txLen[:], uint32(len(rlpTxData)))
|
|
l2TxDataBytes = append(l2TxDataBytes, txLen[:]...)
|
|
l2TxDataBytes = append(l2TxDataBytes, rlpTxData...)
|
|
}
|
|
}
|
|
|
|
chunkBytes = append(chunkBytes, l2TxDataBytes...)
|
|
|
|
return chunkBytes, nil
|
|
}
|
|
|
|
// Hash hashes the Chunk into RollupV2 Chunk Hash
|
|
func (c *Chunk) Hash(totalL1MessagePoppedBefore uint64) (common.Hash, error) {
|
|
chunkBytes, err := c.Encode(totalL1MessagePoppedBefore)
|
|
if err != nil {
|
|
return common.Hash{}, err
|
|
}
|
|
numBlocks := chunkBytes[0]
|
|
|
|
// concatenate block contexts
|
|
var dataBytes []byte
|
|
for i := 0; i < int(numBlocks); i++ {
|
|
// only the first 58 bytes of each BlockContext are needed for the hashing process
|
|
dataBytes = append(dataBytes, chunkBytes[1+60*i:60*i+59]...)
|
|
}
|
|
|
|
// concatenate l1 and l2 tx hashes
|
|
for _, block := range c.Blocks {
|
|
var l1TxHashes []byte
|
|
var l2TxHashes []byte
|
|
for _, txData := range block.Transactions {
|
|
txHash := strings.TrimPrefix(txData.TxHash, "0x")
|
|
hashBytes, err := hex.DecodeString(txHash)
|
|
if err != nil {
|
|
return common.Hash{}, err
|
|
}
|
|
if txData.Type == types.L1MessageTxType {
|
|
l1TxHashes = append(l1TxHashes, hashBytes...)
|
|
} else {
|
|
l2TxHashes = append(l2TxHashes, hashBytes...)
|
|
}
|
|
}
|
|
dataBytes = append(dataBytes, l1TxHashes...)
|
|
dataBytes = append(dataBytes, l2TxHashes...)
|
|
}
|
|
|
|
hash := crypto.Keccak256Hash(dataBytes)
|
|
return hash, nil
|
|
}
|
|
|
|
// DecodeChunkBlockRanges decodes the provided chunks into a list of block ranges. Each chunk
|
|
// contains information about multiple blocks, which are decoded and their ranges (from the
|
|
// start block to the end block) are returned.
|
|
func DecodeChunkBlockRanges(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])
|
|
if len(chunk) < 1+numBlocks*blockContextByteSize {
|
|
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)
|
|
for i := 0; i < numBlocks; i++ {
|
|
startIdx := 1 + i*blockContextByteSize // add 1 to skip numBlocks byte
|
|
endIdx := startIdx + blockContextByteSize
|
|
blockContext, err := decodeBlockContext(chunk[startIdx:endIdx])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blockContexts[i] = blockContext
|
|
}
|
|
|
|
chunkBlockRanges = append(chunkBlockRanges, &rawdb.ChunkBlockRange{
|
|
StartBlockNumber: blockContexts[0].BlockNumber,
|
|
EndBlockNumber: blockContexts[len(blockContexts)-1].BlockNumber,
|
|
})
|
|
}
|
|
return chunkBlockRanges, nil
|
|
}
|