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 }