github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/rollup/rollup_sync_service/block.go (about)

     1  package rollup_sync_service
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"math/big"
     9  
    10  	"github.com/scroll-tech/go-ethereum/common"
    11  	"github.com/scroll-tech/go-ethereum/common/hexutil"
    12  	"github.com/scroll-tech/go-ethereum/core/types"
    13  )
    14  
    15  const blockContextByteSize = 60
    16  
    17  // WrappedBlock contains the block's Header, Transactions and WithdrawTrieRoot hash.
    18  type WrappedBlock struct {
    19  	Header *types.Header `json:"header"`
    20  	// Transactions is only used for recover types.Transactions, the from of types.TransactionData field is missing.
    21  	Transactions []*types.TransactionData `json:"transactions"`
    22  	WithdrawRoot common.Hash              `json:"withdraw_trie_root,omitempty"`
    23  }
    24  
    25  // BlockContext represents the essential data of a block in the ScrollChain.
    26  // It provides an overview of block attributes including hash values, block numbers, gas details, and transaction counts.
    27  type BlockContext struct {
    28  	BlockHash       common.Hash
    29  	ParentHash      common.Hash
    30  	BlockNumber     uint64
    31  	Timestamp       uint64
    32  	BaseFee         *big.Int
    33  	GasLimit        uint64
    34  	NumTransactions uint16
    35  	NumL1Messages   uint16
    36  }
    37  
    38  // numL1Messages returns the number of L1 messages in this block.
    39  // This number is the sum of included and skipped L1 messages.
    40  func (w *WrappedBlock) numL1Messages(totalL1MessagePoppedBefore uint64) uint64 {
    41  	var lastQueueIndex *uint64
    42  	for _, txData := range w.Transactions {
    43  		if txData.Type == types.L1MessageTxType {
    44  			lastQueueIndex = &txData.Nonce
    45  		}
    46  	}
    47  	if lastQueueIndex == nil {
    48  		return 0
    49  	}
    50  	// note: last queue index included before this block is totalL1MessagePoppedBefore - 1
    51  	// TODO: cache results
    52  	return *lastQueueIndex - totalL1MessagePoppedBefore + 1
    53  }
    54  
    55  // Encode encodes the WrappedBlock into RollupV2 BlockContext Encoding.
    56  func (w *WrappedBlock) Encode(totalL1MessagePoppedBefore uint64) ([]byte, error) {
    57  	bytes := make([]byte, 60)
    58  
    59  	if !w.Header.Number.IsUint64() {
    60  		return nil, errors.New("block number is not uint64")
    61  	}
    62  
    63  	// note: numL1Messages includes skipped messages
    64  	numL1Messages := w.numL1Messages(totalL1MessagePoppedBefore)
    65  	if numL1Messages > math.MaxUint16 {
    66  		return nil, errors.New("number of L1 messages exceeds max uint16")
    67  	}
    68  
    69  	// note: numTransactions includes skipped messages
    70  	numL2Transactions := w.numL2Transactions()
    71  	numTransactions := numL1Messages + numL2Transactions
    72  	if numTransactions > math.MaxUint16 {
    73  		return nil, errors.New("number of transactions exceeds max uint16")
    74  	}
    75  
    76  	binary.BigEndian.PutUint64(bytes[0:], w.Header.Number.Uint64())
    77  	binary.BigEndian.PutUint64(bytes[8:], w.Header.Time)
    78  	// TODO: [16:47] Currently, baseFee is 0, because we disable EIP-1559.
    79  	binary.BigEndian.PutUint64(bytes[48:], w.Header.GasLimit)
    80  	binary.BigEndian.PutUint16(bytes[56:], uint16(numTransactions))
    81  	binary.BigEndian.PutUint16(bytes[58:], uint16(numL1Messages))
    82  
    83  	return bytes, nil
    84  }
    85  
    86  func txsToTxsData(txs types.Transactions) []*types.TransactionData {
    87  	txsData := make([]*types.TransactionData, len(txs))
    88  	for i, tx := range txs {
    89  		v, r, s := tx.RawSignatureValues()
    90  
    91  		nonce := tx.Nonce()
    92  
    93  		// We need QueueIndex in `NewBatchHeader`. However, `TransactionData`
    94  		// does not have this field. Since `L1MessageTx` do not have a nonce,
    95  		// we reuse this field for storing the queue index.
    96  		if msg := tx.AsL1MessageTx(); msg != nil {
    97  			nonce = msg.QueueIndex
    98  		}
    99  
   100  		txsData[i] = &types.TransactionData{
   101  			Type:     tx.Type(),
   102  			TxHash:   tx.Hash().String(),
   103  			Nonce:    nonce,
   104  			ChainId:  (*hexutil.Big)(tx.ChainId()),
   105  			Gas:      tx.Gas(),
   106  			GasPrice: (*hexutil.Big)(tx.GasPrice()),
   107  			To:       tx.To(),
   108  			Value:    (*hexutil.Big)(tx.Value()),
   109  			Data:     hexutil.Encode(tx.Data()),
   110  			IsCreate: tx.To() == nil,
   111  			V:        (*hexutil.Big)(v),
   112  			R:        (*hexutil.Big)(r),
   113  			S:        (*hexutil.Big)(s),
   114  		}
   115  	}
   116  	return txsData
   117  }
   118  
   119  func convertTxDataToRLPEncoding(txData *types.TransactionData) ([]byte, error) {
   120  	data, err := hexutil.Decode(txData.Data)
   121  	if err != nil {
   122  		return nil, fmt.Errorf("failed to decode txData.Data: %s, err: %w", txData.Data, err)
   123  	}
   124  
   125  	tx := types.NewTx(&types.LegacyTx{
   126  		Nonce:    txData.Nonce,
   127  		To:       txData.To,
   128  		Value:    txData.Value.ToInt(),
   129  		Gas:      txData.Gas,
   130  		GasPrice: txData.GasPrice.ToInt(),
   131  		Data:     data,
   132  		V:        txData.V.ToInt(),
   133  		R:        txData.R.ToInt(),
   134  		S:        txData.S.ToInt(),
   135  	})
   136  
   137  	rlpTxData, err := tx.MarshalBinary()
   138  	if err != nil {
   139  		return nil, fmt.Errorf("failed to marshal binary of the tx: %+v, err: %w", tx, err)
   140  	}
   141  
   142  	return rlpTxData, nil
   143  }
   144  
   145  func (w *WrappedBlock) numL2Transactions() uint64 {
   146  	var count uint64
   147  	for _, txData := range w.Transactions {
   148  		if txData.Type != types.L1MessageTxType {
   149  			count++
   150  		}
   151  	}
   152  	return count
   153  }
   154  
   155  func decodeBlockContext(encodedBlockContext []byte) (*BlockContext, error) {
   156  	if len(encodedBlockContext) != blockContextByteSize {
   157  		return nil, errors.New("block encoding is not 60 bytes long")
   158  	}
   159  
   160  	return &BlockContext{
   161  		BlockNumber:     binary.BigEndian.Uint64(encodedBlockContext[0:8]),
   162  		Timestamp:       binary.BigEndian.Uint64(encodedBlockContext[8:16]),
   163  		GasLimit:        binary.BigEndian.Uint64(encodedBlockContext[48:56]),
   164  		NumTransactions: binary.BigEndian.Uint16(encodedBlockContext[56:58]),
   165  		NumL1Messages:   binary.BigEndian.Uint16(encodedBlockContext[58:60]),
   166  	}, nil
   167  }