github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/core/rawdb/accessors_l1_message.go (about)

     1  package rawdb
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"math/big"
     7  	"time"
     8  	"unsafe"
     9  
    10  	"github.com/scroll-tech/go-ethereum/common"
    11  	"github.com/scroll-tech/go-ethereum/core/types"
    12  	"github.com/scroll-tech/go-ethereum/ethdb"
    13  	"github.com/scroll-tech/go-ethereum/log"
    14  	"github.com/scroll-tech/go-ethereum/metrics"
    15  	"github.com/scroll-tech/go-ethereum/rlp"
    16  )
    17  
    18  var (
    19  	// L1 message iterator metrics
    20  	iteratorNextCalledCounter      = metrics.NewRegisteredCounter("rawdb/l1_message/iterator/next_called", nil)
    21  	iteratorInnerNextCalledCounter = metrics.NewRegisteredCounter("rawdb/l1_message/iterator/inner_next_called", nil)
    22  	iteratorLengthMismatchCounter  = metrics.NewRegisteredCounter("rawdb/l1_message/iterator/length_mismatch", nil)
    23  	iteratorNextDurationTimer      = metrics.NewRegisteredTimer("rawdb/l1_message/iterator/next_time", nil)
    24  	iteratorL1MessageSizeGauge     = metrics.NewRegisteredGauge("rawdb/l1_message/size", nil)
    25  )
    26  
    27  // WriteSyncedL1BlockNumber writes the highest synced L1 block number to the database.
    28  func WriteSyncedL1BlockNumber(db ethdb.KeyValueWriter, L1BlockNumber uint64) {
    29  	value := big.NewInt(0).SetUint64(L1BlockNumber).Bytes()
    30  
    31  	if err := db.Put(syncedL1BlockNumberKey, value); err != nil {
    32  		log.Crit("Failed to update synced L1 block number", "err", err)
    33  	}
    34  }
    35  
    36  // ReadSyncedL1BlockNumber retrieves the highest synced L1 block number.
    37  func ReadSyncedL1BlockNumber(db ethdb.Reader) *uint64 {
    38  	data, err := db.Get(syncedL1BlockNumberKey)
    39  	if err != nil && isNotFoundErr(err) {
    40  		return nil
    41  	}
    42  	if err != nil {
    43  		log.Crit("Failed to read synced L1 block number from database", "err", err)
    44  	}
    45  	if len(data) == 0 {
    46  		return nil
    47  	}
    48  
    49  	number := new(big.Int).SetBytes(data)
    50  	if !number.IsUint64() {
    51  		log.Crit("Unexpected synced L1 block number in database", "number", number)
    52  	}
    53  
    54  	value := number.Uint64()
    55  	return &value
    56  }
    57  
    58  // WriteHighestSyncedQueueIndex writes the highest synced L1 message queue index to the database.
    59  func WriteHighestSyncedQueueIndex(db ethdb.KeyValueWriter, queueIndex uint64) {
    60  	value := big.NewInt(0).SetUint64(queueIndex).Bytes()
    61  
    62  	if err := db.Put(highestSyncedQueueIndexKey, value); err != nil {
    63  		log.Crit("Failed to update highest synced L1 message queue index", "err", err)
    64  	}
    65  }
    66  
    67  // ReadHighestSyncedQueueIndex retrieves the highest synced L1 message queue index.
    68  func ReadHighestSyncedQueueIndex(db ethdb.Reader) uint64 {
    69  	data, err := db.Get(highestSyncedQueueIndexKey)
    70  	if err != nil && isNotFoundErr(err) {
    71  		return 0
    72  	}
    73  	if err != nil {
    74  		log.Crit("Failed to read highest synced L1 message queue index from database", "err", err)
    75  	}
    76  	if len(data) == 0 {
    77  		return 0
    78  	}
    79  
    80  	number := new(big.Int).SetBytes(data)
    81  	if !number.IsUint64() {
    82  		log.Crit("Unexpected highest synced L1 block number in database", "number", number)
    83  	}
    84  
    85  	return number.Uint64()
    86  }
    87  
    88  // WriteL1Message writes an L1 message to the database.
    89  // We assume that L1 messages are written to DB following their queue index order.
    90  func WriteL1Message(db ethdb.KeyValueWriter, l1Msg types.L1MessageTx) {
    91  	bytes, err := rlp.EncodeToBytes(l1Msg)
    92  	if err != nil {
    93  		log.Crit("Failed to RLP encode L1 message", "err", err)
    94  	}
    95  	if err := db.Put(L1MessageKey(l1Msg.QueueIndex), bytes); err != nil {
    96  		log.Crit("Failed to store L1 message", "err", err)
    97  	}
    98  
    99  	WriteHighestSyncedQueueIndex(db, l1Msg.QueueIndex)
   100  }
   101  
   102  // WriteL1Messages writes an array of L1 messages to the database.
   103  // Note: pass a db of type `ethdb.Batcher` to batch writes in memory.
   104  func WriteL1Messages(db ethdb.KeyValueWriter, l1Msgs []types.L1MessageTx) {
   105  	for _, msg := range l1Msgs {
   106  		WriteL1Message(db, msg)
   107  	}
   108  }
   109  
   110  // ReadL1MessageRLP retrieves an L1 message in its raw RLP database encoding.
   111  func ReadL1MessageRLP(db ethdb.Reader, queueIndex uint64) rlp.RawValue {
   112  	data, err := db.Get(L1MessageKey(queueIndex))
   113  	if err != nil && isNotFoundErr(err) {
   114  		return nil
   115  	}
   116  	if err != nil {
   117  		log.Crit("Failed to load L1 message", "queueIndex", queueIndex, "err", err)
   118  	}
   119  	return data
   120  }
   121  
   122  // ReadL1Message retrieves the L1 message corresponding to the enqueue index.
   123  func ReadL1Message(db ethdb.Reader, queueIndex uint64) *types.L1MessageTx {
   124  	data := ReadL1MessageRLP(db, queueIndex)
   125  	if len(data) == 0 {
   126  		return nil
   127  	}
   128  	l1Msg := new(types.L1MessageTx)
   129  	if err := rlp.Decode(bytes.NewReader(data), l1Msg); err != nil {
   130  		log.Crit("Invalid L1 message RLP", "queueIndex", queueIndex, "data", data, "err", err)
   131  	}
   132  	return l1Msg
   133  }
   134  
   135  // L1MessageIterator is a wrapper around ethdb.Iterator that
   136  // allows us to iterate over L1 messages in the database. It
   137  // implements an interface similar to ethdb.Iterator.
   138  type L1MessageIterator struct {
   139  	inner         ethdb.Iterator
   140  	keyLength     int
   141  	maxQueueIndex uint64
   142  }
   143  
   144  // IterateL1MessagesFrom creates an L1MessageIterator that iterates over
   145  // all L1 message in the database starting at the provided enqueue index.
   146  func IterateL1MessagesFrom(db ethdb.Database, fromQueueIndex uint64) L1MessageIterator {
   147  	start := encodeBigEndian(fromQueueIndex)
   148  	it := db.NewIterator(l1MessagePrefix, start)
   149  	keyLength := len(l1MessagePrefix) + 8
   150  	maxQueueIndex := ReadHighestSyncedQueueIndex(db)
   151  
   152  	return L1MessageIterator{
   153  		inner:         it,
   154  		keyLength:     keyLength,
   155  		maxQueueIndex: maxQueueIndex,
   156  	}
   157  }
   158  
   159  // Next moves the iterator to the next key/value pair.
   160  // It returns false when the iterator is exhausted.
   161  // TODO: Consider reading items in batches.
   162  func (it *L1MessageIterator) Next() bool {
   163  	iteratorNextCalledCounter.Inc(1)
   164  
   165  	defer func(t0 time.Time) {
   166  		iteratorNextDurationTimer.Update(time.Since(t0))
   167  	}(time.Now())
   168  
   169  	for it.inner.Next() {
   170  		iteratorInnerNextCalledCounter.Inc(1)
   171  
   172  		key := it.inner.Key()
   173  		if len(key) == it.keyLength {
   174  			return true
   175  		} else {
   176  			iteratorLengthMismatchCounter.Inc(1)
   177  		}
   178  	}
   179  	return false
   180  }
   181  
   182  // QueueIndex returns the enqueue index of the current L1 message.
   183  func (it *L1MessageIterator) QueueIndex() uint64 {
   184  	key := it.inner.Key()
   185  	raw := key[len(l1MessagePrefix) : len(l1MessagePrefix)+8]
   186  	queueIndex := binary.BigEndian.Uint64(raw)
   187  	return queueIndex
   188  }
   189  
   190  // L1Message returns the current L1 message.
   191  func (it *L1MessageIterator) L1Message() types.L1MessageTx {
   192  	data := it.inner.Value()
   193  	l1Msg := types.L1MessageTx{}
   194  	if err := rlp.DecodeBytes(data, &l1Msg); err != nil {
   195  		log.Crit("Invalid L1 message RLP", "data", data, "err", err)
   196  	}
   197  	return l1Msg
   198  }
   199  
   200  // Release releases the associated resources.
   201  func (it *L1MessageIterator) Release() {
   202  	it.inner.Release()
   203  }
   204  
   205  // ReadL1MessagesFrom retrieves up to `maxCount` L1 messages starting at `startIndex`.
   206  func ReadL1MessagesFrom(db ethdb.Database, startIndex, maxCount uint64) []types.L1MessageTx {
   207  	msgs := make([]types.L1MessageTx, 0, maxCount)
   208  	it := IterateL1MessagesFrom(db, startIndex)
   209  	defer it.Release()
   210  
   211  	index := startIndex
   212  	count := maxCount
   213  
   214  	for count > 0 && it.Next() {
   215  		msg := it.L1Message()
   216  
   217  		// sanity check
   218  		if msg.QueueIndex != index {
   219  			log.Crit(
   220  				"Unexpected QueueIndex in ReadL1MessagesFrom",
   221  				"expected", index,
   222  				"got", msg.QueueIndex,
   223  				"startIndex", startIndex,
   224  				"maxCount", maxCount,
   225  			)
   226  		}
   227  
   228  		msgs = append(msgs, msg)
   229  		index += 1
   230  		count -= 1
   231  
   232  		iteratorL1MessageSizeGauge.Update(int64(unsafe.Sizeof(msg) + uintptr(cap(msg.Data))))
   233  
   234  		if msg.QueueIndex == it.maxQueueIndex {
   235  			break
   236  		}
   237  	}
   238  
   239  	return msgs
   240  }
   241  
   242  // WriteFirstQueueIndexNotInL2Block writes the queue index of the first message
   243  // that is NOT included in the ledger up to and including the provided L2 block.
   244  // The L2 block is identified by its block hash. If the L2 block contains zero
   245  // L1 messages, this value MUST equal its parent's value.
   246  func WriteFirstQueueIndexNotInL2Block(db ethdb.KeyValueWriter, l2BlockHash common.Hash, queueIndex uint64) {
   247  	if err := db.Put(FirstQueueIndexNotInL2BlockKey(l2BlockHash), encodeBigEndian(queueIndex)); err != nil {
   248  		log.Crit("Failed to store first L1 message not in L2 block", "l2BlockHash", l2BlockHash, "err", err)
   249  	}
   250  }
   251  
   252  // ReadFirstQueueIndexNotInL2Block retrieves the queue index of the first message
   253  // that is NOT included in the ledger up to and including the provided L2 block.
   254  func ReadFirstQueueIndexNotInL2Block(db ethdb.Reader, l2BlockHash common.Hash) *uint64 {
   255  	data, err := db.Get(FirstQueueIndexNotInL2BlockKey(l2BlockHash))
   256  	if err != nil && isNotFoundErr(err) {
   257  		return nil
   258  	}
   259  	if err != nil {
   260  		log.Crit("Failed to read first L1 message not in L2 block from database", "l2BlockHash", l2BlockHash, "err", err)
   261  	}
   262  	if len(data) == 0 {
   263  		return nil
   264  	}
   265  	queueIndex := binary.BigEndian.Uint64(data)
   266  	return &queueIndex
   267  }