github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/history_index.go (about) 1 // Copyright 2025 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/ 16 17 package pathdb 18 19 import ( 20 "errors" 21 "fmt" 22 "math" 23 "sort" 24 25 "github.com/ethereum/go-ethereum/core/rawdb" 26 "github.com/ethereum/go-ethereum/ethdb" 27 ) 28 29 // parseIndex parses the index data with the supplied byte stream. The index data 30 // is a list of fixed-sized metadata. Empty metadata is regarded as invalid. 31 func parseIndex(blob []byte) ([]*indexBlockDesc, error) { 32 if len(blob) == 0 { 33 return nil, errors.New("empty state history index") 34 } 35 if len(blob)%indexBlockDescSize != 0 { 36 return nil, fmt.Errorf("corrupted state index, len: %d", len(blob)) 37 } 38 var ( 39 lastID uint32 40 descList []*indexBlockDesc 41 ) 42 for i := 0; i < len(blob)/indexBlockDescSize; i++ { 43 var desc indexBlockDesc 44 desc.decode(blob[i*indexBlockDescSize : (i+1)*indexBlockDescSize]) 45 if desc.empty() { 46 return nil, errors.New("empty state history index block") 47 } 48 if lastID != 0 { 49 if lastID+1 != desc.id { 50 return nil, fmt.Errorf("index block id is out of order, last-id: %d, this-id: %d", lastID, desc.id) 51 } 52 // Theoretically, order should be validated between consecutive index blocks, 53 // ensuring that elements within them are strictly ordered. However, since 54 // tracking the minimum element in each block has non-trivial storage overhead, 55 // this check is optimistically omitted. 56 // 57 // TODO(rjl493456442) the minimal element can be resolved from the index block, 58 // evaluate the check cost (mostly IO overhead). 59 60 /* if desc.min <= lastMax { 61 return nil, fmt.Errorf("index block range is out of order, last-max: %d, this-min: %d", lastMax, desc.min) 62 }*/ 63 } 64 lastID = desc.id 65 descList = append(descList, &desc) 66 } 67 return descList, nil 68 } 69 70 // indexReader is the structure to look up the state history index records 71 // associated with the specific state element. 72 type indexReader struct { 73 db ethdb.KeyValueReader 74 descList []*indexBlockDesc 75 readers map[uint32]*blockReader 76 state stateIdent 77 } 78 79 // loadIndexData loads the index data associated with the specified state. 80 func loadIndexData(db ethdb.KeyValueReader, state stateIdent) ([]*indexBlockDesc, error) { 81 var blob []byte 82 if state.account { 83 blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash) 84 } else { 85 blob = rawdb.ReadStorageHistoryIndex(db, state.addressHash, state.storageHash) 86 } 87 if len(blob) == 0 { 88 return nil, nil 89 } 90 return parseIndex(blob) 91 } 92 93 // newIndexReader constructs a index reader for the specified state. Reader with 94 // empty data is allowed. 95 func newIndexReader(db ethdb.KeyValueReader, state stateIdent) (*indexReader, error) { 96 descList, err := loadIndexData(db, state) 97 if err != nil { 98 return nil, err 99 } 100 return &indexReader{ 101 descList: descList, 102 readers: make(map[uint32]*blockReader), 103 db: db, 104 state: state, 105 }, nil 106 } 107 108 // refresh reloads the last section of index data to account for any additional 109 // elements that may have been written to disk. 110 func (r *indexReader) refresh() error { 111 // Release the reader for the last section of index data, as its content 112 // may have been modified by additional elements written to the disk. 113 if len(r.descList) != 0 { 114 last := r.descList[len(r.descList)-1] 115 if !last.full() { 116 delete(r.readers, last.id) 117 } 118 } 119 descList, err := loadIndexData(r.db, r.state) 120 if err != nil { 121 return err 122 } 123 r.descList = descList 124 return nil 125 } 126 127 // readGreaterThan locates the first element that is greater than the specified 128 // id. If no such element is found, MaxUint64 is returned. 129 func (r *indexReader) readGreaterThan(id uint64) (uint64, error) { 130 index := sort.Search(len(r.descList), func(i int) bool { 131 return id < r.descList[i].max 132 }) 133 if index == len(r.descList) { 134 return math.MaxUint64, nil 135 } 136 desc := r.descList[index] 137 138 br, ok := r.readers[desc.id] 139 if !ok { 140 var ( 141 err error 142 blob []byte 143 ) 144 if r.state.account { 145 blob = rawdb.ReadAccountHistoryIndexBlock(r.db, r.state.addressHash, desc.id) 146 } else { 147 blob = rawdb.ReadStorageHistoryIndexBlock(r.db, r.state.addressHash, r.state.storageHash, desc.id) 148 } 149 br, err = newBlockReader(blob) 150 if err != nil { 151 return 0, err 152 } 153 r.readers[desc.id] = br 154 } 155 // The supplied ID is not greater than block.max, ensuring that an element 156 // satisfying the condition can be found. 157 return br.readGreaterThan(id) 158 } 159 160 // indexWriter is responsible for writing index data for a specific state (either 161 // an account or a storage slot). The state index follows a two-layer structure: 162 // the first layer consists of a list of fixed-size metadata, each linked to a 163 // second-layer block. The index data (monotonically increasing list of state 164 // history ids) is stored in these second-layer index blocks, which are size 165 // limited. 166 type indexWriter struct { 167 descList []*indexBlockDesc // The list of index block descriptions 168 bw *blockWriter // The live index block writer 169 frozen []*blockWriter // The finalized index block writers, waiting for flush 170 lastID uint64 // The ID of the latest tracked history 171 state stateIdent 172 db ethdb.KeyValueReader 173 } 174 175 // newIndexWriter constructs the index writer for the specified state. 176 func newIndexWriter(db ethdb.KeyValueReader, state stateIdent) (*indexWriter, error) { 177 var blob []byte 178 if state.account { 179 blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash) 180 } else { 181 blob = rawdb.ReadStorageHistoryIndex(db, state.addressHash, state.storageHash) 182 } 183 if len(blob) == 0 { 184 desc := newIndexBlockDesc(0) 185 bw, _ := newBlockWriter(nil, desc) 186 return &indexWriter{ 187 descList: []*indexBlockDesc{desc}, 188 bw: bw, 189 state: state, 190 db: db, 191 }, nil 192 } 193 descList, err := parseIndex(blob) 194 if err != nil { 195 return nil, err 196 } 197 var ( 198 indexBlock []byte 199 lastDesc = descList[len(descList)-1] 200 ) 201 if state.account { 202 indexBlock = rawdb.ReadAccountHistoryIndexBlock(db, state.addressHash, lastDesc.id) 203 } else { 204 indexBlock = rawdb.ReadStorageHistoryIndexBlock(db, state.addressHash, state.storageHash, lastDesc.id) 205 } 206 bw, err := newBlockWriter(indexBlock, lastDesc) 207 if err != nil { 208 return nil, err 209 } 210 return &indexWriter{ 211 descList: descList, 212 lastID: lastDesc.max, 213 bw: bw, 214 state: state, 215 db: db, 216 }, nil 217 } 218 219 // append adds the new element into the index writer. 220 func (w *indexWriter) append(id uint64) error { 221 if id <= w.lastID { 222 return fmt.Errorf("append element out of order, last: %d, this: %d", w.lastID, id) 223 } 224 if w.bw.full() { 225 if err := w.rotate(); err != nil { 226 return err 227 } 228 } 229 if err := w.bw.append(id); err != nil { 230 return err 231 } 232 w.lastID = id 233 234 return nil 235 } 236 237 // rotate creates a new index block for storing index records from scratch 238 // and caches the current full index block for finalization. 239 func (w *indexWriter) rotate() error { 240 var ( 241 err error 242 desc = newIndexBlockDesc(w.bw.desc.id + 1) 243 ) 244 w.frozen = append(w.frozen, w.bw) 245 w.bw, err = newBlockWriter(nil, desc) 246 if err != nil { 247 return err 248 } 249 w.descList = append(w.descList, desc) 250 return nil 251 } 252 253 // finish finalizes all the frozen index block writers along with the live one 254 // if it's not empty, committing the index block data and the index meta into 255 // the supplied batch. 256 // 257 // This function is safe to be called multiple times. 258 func (w *indexWriter) finish(batch ethdb.Batch) { 259 var ( 260 writers = append(w.frozen, w.bw) 261 descList = w.descList 262 ) 263 // The live index block writer might be empty if the entire index write 264 // is created from scratch, remove it from committing. 265 if w.bw.empty() { 266 writers = writers[:len(writers)-1] 267 descList = descList[:len(descList)-1] 268 } 269 if len(writers) == 0 { 270 return // nothing to commit 271 } 272 for _, bw := range writers { 273 if w.state.account { 274 rawdb.WriteAccountHistoryIndexBlock(batch, w.state.addressHash, bw.desc.id, bw.finish()) 275 } else { 276 rawdb.WriteStorageHistoryIndexBlock(batch, w.state.addressHash, w.state.storageHash, bw.desc.id, bw.finish()) 277 } 278 } 279 w.frozen = nil // release all the frozen writers 280 281 buf := make([]byte, 0, indexBlockDescSize*len(descList)) 282 for _, desc := range descList { 283 buf = append(buf, desc.encode()...) 284 } 285 if w.state.account { 286 rawdb.WriteAccountHistoryIndex(batch, w.state.addressHash, buf) 287 } else { 288 rawdb.WriteStorageHistoryIndex(batch, w.state.addressHash, w.state.storageHash, buf) 289 } 290 } 291 292 // indexDeleter is responsible for deleting index data for a specific state. 293 type indexDeleter struct { 294 descList []*indexBlockDesc // The list of index block descriptions 295 bw *blockWriter // The live index block writer 296 dropped []uint32 // The list of index block id waiting for deleting 297 lastID uint64 // The ID of the latest tracked history 298 state stateIdent 299 db ethdb.KeyValueReader 300 } 301 302 // newIndexDeleter constructs the index deleter for the specified state. 303 func newIndexDeleter(db ethdb.KeyValueReader, state stateIdent) (*indexDeleter, error) { 304 var blob []byte 305 if state.account { 306 blob = rawdb.ReadAccountHistoryIndex(db, state.addressHash) 307 } else { 308 blob = rawdb.ReadStorageHistoryIndex(db, state.addressHash, state.storageHash) 309 } 310 if len(blob) == 0 { 311 // TODO(rjl493456442) we can probably return an error here, 312 // deleter with no data is meaningless. 313 desc := newIndexBlockDesc(0) 314 bw, _ := newBlockWriter(nil, desc) 315 return &indexDeleter{ 316 descList: []*indexBlockDesc{desc}, 317 bw: bw, 318 state: state, 319 db: db, 320 }, nil 321 } 322 descList, err := parseIndex(blob) 323 if err != nil { 324 return nil, err 325 } 326 var ( 327 indexBlock []byte 328 lastDesc = descList[len(descList)-1] 329 ) 330 if state.account { 331 indexBlock = rawdb.ReadAccountHistoryIndexBlock(db, state.addressHash, lastDesc.id) 332 } else { 333 indexBlock = rawdb.ReadStorageHistoryIndexBlock(db, state.addressHash, state.storageHash, lastDesc.id) 334 } 335 bw, err := newBlockWriter(indexBlock, lastDesc) 336 if err != nil { 337 return nil, err 338 } 339 return &indexDeleter{ 340 descList: descList, 341 lastID: lastDesc.max, 342 bw: bw, 343 state: state, 344 db: db, 345 }, nil 346 } 347 348 // empty returns an flag indicating whether the state index is empty. 349 func (d *indexDeleter) empty() bool { 350 return d.bw.empty() && len(d.descList) == 1 351 } 352 353 // pop removes the last written element from the index writer. 354 func (d *indexDeleter) pop(id uint64) error { 355 if id == 0 { 356 return fmt.Errorf("zero history ID is not valid") 357 } 358 if id != d.lastID { 359 return fmt.Errorf("pop element out of order, last: %d, this: %d", d.lastID, id) 360 } 361 if err := d.bw.pop(id); err != nil { 362 return err 363 } 364 if !d.bw.empty() { 365 d.lastID = d.bw.desc.max 366 return nil 367 } 368 // Discarding the last block writer if it becomes empty by popping an element 369 d.dropped = append(d.dropped, d.descList[len(d.descList)-1].id) 370 371 // Reset the entire index writer if it becomes empty after popping an element 372 if d.empty() { 373 d.lastID = 0 374 return nil 375 } 376 d.descList = d.descList[:len(d.descList)-1] 377 378 // Open the previous block writer for deleting 379 var ( 380 indexBlock []byte 381 lastDesc = d.descList[len(d.descList)-1] 382 ) 383 if d.state.account { 384 indexBlock = rawdb.ReadAccountHistoryIndexBlock(d.db, d.state.addressHash, lastDesc.id) 385 } else { 386 indexBlock = rawdb.ReadStorageHistoryIndexBlock(d.db, d.state.addressHash, d.state.storageHash, lastDesc.id) 387 } 388 bw, err := newBlockWriter(indexBlock, lastDesc) 389 if err != nil { 390 return err 391 } 392 d.bw = bw 393 d.lastID = bw.desc.max 394 return nil 395 } 396 397 // finish deletes the empty index blocks and updates the index meta. 398 // 399 // This function is safe to be called multiple times. 400 func (d *indexDeleter) finish(batch ethdb.Batch) { 401 for _, id := range d.dropped { 402 if d.state.account { 403 rawdb.DeleteAccountHistoryIndexBlock(batch, d.state.addressHash, id) 404 } else { 405 rawdb.DeleteStorageHistoryIndexBlock(batch, d.state.addressHash, d.state.storageHash, id) 406 } 407 } 408 d.dropped = nil 409 410 // Flush the content of last block writer, regardless it's dirty or not 411 if !d.bw.empty() { 412 if d.state.account { 413 rawdb.WriteAccountHistoryIndexBlock(batch, d.state.addressHash, d.bw.desc.id, d.bw.finish()) 414 } else { 415 rawdb.WriteStorageHistoryIndexBlock(batch, d.state.addressHash, d.state.storageHash, d.bw.desc.id, d.bw.finish()) 416 } 417 } 418 // Flush the index metadata into the supplied batch 419 if d.empty() { 420 if d.state.account { 421 rawdb.DeleteAccountHistoryIndex(batch, d.state.addressHash) 422 } else { 423 rawdb.DeleteStorageHistoryIndex(batch, d.state.addressHash, d.state.storageHash) 424 } 425 } else { 426 buf := make([]byte, 0, indexBlockDescSize*len(d.descList)) 427 for _, desc := range d.descList { 428 buf = append(buf, desc.encode()...) 429 } 430 if d.state.account { 431 rawdb.WriteAccountHistoryIndex(batch, d.state.addressHash, buf) 432 } else { 433 rawdb.WriteStorageHistoryIndex(batch, d.state.addressHash, d.state.storageHash, buf) 434 } 435 } 436 }