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  }