github.com/ethereum/go-ethereum@v1.16.1/core/rawdb/accessors_indexes.go (about)

     1  // Copyright 2018 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 rawdb
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"errors"
    23  	"fmt"
    24  	"math/big"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/core/types"
    28  	"github.com/ethereum/go-ethereum/crypto"
    29  	"github.com/ethereum/go-ethereum/ethdb"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-ethereum/params"
    32  	"github.com/ethereum/go-ethereum/rlp"
    33  )
    34  
    35  // DecodeTxLookupEntry decodes the supplied tx lookup data.
    36  func DecodeTxLookupEntry(data []byte, db ethdb.Reader) *uint64 {
    37  	// Database v6 tx lookup just stores the block number
    38  	if len(data) < common.HashLength {
    39  		number := new(big.Int).SetBytes(data).Uint64()
    40  		return &number
    41  	}
    42  	// Database v4-v5 tx lookup format just stores the hash
    43  	if len(data) == common.HashLength {
    44  		return ReadHeaderNumber(db, common.BytesToHash(data))
    45  	}
    46  	// Finally try database v3 tx lookup format
    47  	var entry LegacyTxLookupEntry
    48  	if err := rlp.DecodeBytes(data, &entry); err != nil {
    49  		log.Error("Invalid transaction lookup entry RLP", "blob", data, "err", err)
    50  		return nil
    51  	}
    52  	return &entry.BlockIndex
    53  }
    54  
    55  // ReadTxLookupEntry retrieves the positional metadata associated with a transaction
    56  // hash to allow retrieving the transaction or receipt by hash.
    57  func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) *uint64 {
    58  	data, _ := db.Get(txLookupKey(hash))
    59  	if len(data) == 0 {
    60  		return nil
    61  	}
    62  	return DecodeTxLookupEntry(data, db)
    63  }
    64  
    65  // writeTxLookupEntry stores a positional metadata for a transaction,
    66  // enabling hash based transaction and receipt lookups.
    67  func writeTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash, numberBytes []byte) {
    68  	if err := db.Put(txLookupKey(hash), numberBytes); err != nil {
    69  		log.Crit("Failed to store transaction lookup entry", "err", err)
    70  	}
    71  }
    72  
    73  // WriteTxLookupEntries is identical to WriteTxLookupEntry, but it works on
    74  // a list of hashes
    75  func WriteTxLookupEntries(db ethdb.KeyValueWriter, number uint64, hashes []common.Hash) {
    76  	numberBytes := new(big.Int).SetUint64(number).Bytes()
    77  	for _, hash := range hashes {
    78  		writeTxLookupEntry(db, hash, numberBytes)
    79  	}
    80  }
    81  
    82  // WriteTxLookupEntriesByBlock stores a positional metadata for every transaction from
    83  // a block, enabling hash based transaction and receipt lookups.
    84  func WriteTxLookupEntriesByBlock(db ethdb.KeyValueWriter, block *types.Block) {
    85  	numberBytes := block.Number().Bytes()
    86  	for _, tx := range block.Transactions() {
    87  		writeTxLookupEntry(db, tx.Hash(), numberBytes)
    88  	}
    89  }
    90  
    91  // DeleteTxLookupEntry removes all transaction data associated with a hash.
    92  func DeleteTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash) {
    93  	if err := db.Delete(txLookupKey(hash)); err != nil {
    94  		log.Crit("Failed to delete transaction lookup entry", "err", err)
    95  	}
    96  }
    97  
    98  // DeleteTxLookupEntries removes all transaction lookups for a given block.
    99  func DeleteTxLookupEntries(db ethdb.KeyValueWriter, hashes []common.Hash) {
   100  	for _, hash := range hashes {
   101  		DeleteTxLookupEntry(db, hash)
   102  	}
   103  }
   104  
   105  // DeleteAllTxLookupEntries purges all the transaction indexes in the database.
   106  // If condition is specified, only the entry with condition as True will be
   107  // removed; If condition is not specified, the entry is deleted.
   108  func DeleteAllTxLookupEntries(db ethdb.KeyValueStore, condition func(common.Hash, []byte) bool) {
   109  	iter := NewKeyLengthIterator(db.NewIterator(txLookupPrefix, nil), common.HashLength+len(txLookupPrefix))
   110  	defer iter.Release()
   111  
   112  	batch := db.NewBatch()
   113  	for iter.Next() {
   114  		txhash := common.Hash(iter.Key()[1:])
   115  		if condition == nil || condition(txhash, iter.Value()) {
   116  			batch.Delete(iter.Key())
   117  		}
   118  		if batch.ValueSize() >= ethdb.IdealBatchSize {
   119  			if err := batch.Write(); err != nil {
   120  				log.Crit("Failed to delete transaction lookup entries", "err", err)
   121  			}
   122  			batch.Reset()
   123  		}
   124  	}
   125  	if batch.ValueSize() > 0 {
   126  		if err := batch.Write(); err != nil {
   127  			log.Crit("Failed to delete transaction lookup entries", "err", err)
   128  		}
   129  		batch.Reset()
   130  	}
   131  }
   132  
   133  // findTxInBlockBody traverses the given RLP-encoded block body, searching for
   134  // the transaction specified by its hash.
   135  func findTxInBlockBody(blockbody rlp.RawValue, target common.Hash) (*types.Transaction, uint64, error) {
   136  	txnListRLP, _, err := rlp.SplitList(blockbody)
   137  	if err != nil {
   138  		return nil, 0, err
   139  	}
   140  	iter, err := rlp.NewListIterator(txnListRLP)
   141  	if err != nil {
   142  		return nil, 0, err
   143  	}
   144  	txIndex := uint64(0)
   145  	for iter.Next() {
   146  		if iter.Err() != nil {
   147  			return nil, 0, err
   148  		}
   149  		// The preimage for the hash calculation of legacy transactions
   150  		// is just their RLP encoding. For typed (EIP-2718) transactions,
   151  		// which are encoded as byte arrays, the preimage is the content of
   152  		// the byte array, so trim their prefix here.
   153  		txRLP := iter.Value()
   154  		kind, txHashPayload, _, err := rlp.Split(txRLP)
   155  		if err != nil {
   156  			return nil, 0, err
   157  		}
   158  		if kind == rlp.List { // Legacy transaction
   159  			txHashPayload = txRLP
   160  		}
   161  		if crypto.Keccak256Hash(txHashPayload) == target {
   162  			var tx types.Transaction
   163  			if err := rlp.DecodeBytes(txRLP, &tx); err != nil {
   164  				return nil, 0, err
   165  			}
   166  			return &tx, txIndex, nil
   167  		}
   168  		txIndex++
   169  	}
   170  	return nil, 0, errors.New("transaction not found")
   171  }
   172  
   173  // ReadCanonicalTransaction retrieves a specific transaction from the database, along
   174  // with its added positional metadata. Notably, only the transaction in the canonical
   175  // chain is visible.
   176  func ReadCanonicalTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
   177  	blockNumber := ReadTxLookupEntry(db, hash)
   178  	if blockNumber == nil {
   179  		return nil, common.Hash{}, 0, 0
   180  	}
   181  	blockHash := ReadCanonicalHash(db, *blockNumber)
   182  	if blockHash == (common.Hash{}) {
   183  		return nil, common.Hash{}, 0, 0
   184  	}
   185  	bodyRLP := ReadCanonicalBodyRLP(db, *blockNumber, &blockHash)
   186  	if bodyRLP == nil {
   187  		log.Error("Transaction referenced missing", "number", *blockNumber, "hash", blockHash)
   188  		return nil, common.Hash{}, 0, 0
   189  	}
   190  	tx, txIndex, err := findTxInBlockBody(bodyRLP, hash)
   191  	if err != nil {
   192  		log.Error("Transaction not found", "number", *blockNumber, "hash", blockHash, "txhash", hash, "err", err)
   193  		return nil, common.Hash{}, 0, 0
   194  	}
   195  	return tx, blockHash, *blockNumber, txIndex
   196  }
   197  
   198  // ReadCanonicalReceipt retrieves a specific transaction receipt from the database,
   199  // along with its added positional metadata. Notably, only the receipt in the canonical
   200  // chain is visible.
   201  func ReadCanonicalReceipt(db ethdb.Reader, hash common.Hash, config *params.ChainConfig) (*types.Receipt, common.Hash, uint64, uint64) {
   202  	// Retrieve the context of the receipt based on the transaction hash
   203  	blockNumber := ReadTxLookupEntry(db, hash)
   204  	if blockNumber == nil {
   205  		return nil, common.Hash{}, 0, 0
   206  	}
   207  	blockHash := ReadCanonicalHash(db, *blockNumber)
   208  	if blockHash == (common.Hash{}) {
   209  		return nil, common.Hash{}, 0, 0
   210  	}
   211  	blockHeader := ReadHeader(db, blockHash, *blockNumber)
   212  	if blockHeader == nil {
   213  		return nil, common.Hash{}, 0, 0
   214  	}
   215  	// Read all the receipts from the block and return the one with the matching hash
   216  	receipts := ReadReceipts(db, blockHash, *blockNumber, blockHeader.Time, config)
   217  	for receiptIndex, receipt := range receipts {
   218  		if receipt.TxHash == hash {
   219  			return receipt, blockHash, *blockNumber, uint64(receiptIndex)
   220  		}
   221  	}
   222  	log.Error("Receipt not found", "number", *blockNumber, "hash", blockHash, "txhash", hash)
   223  	return nil, common.Hash{}, 0, 0
   224  }
   225  
   226  // extractReceiptFields takes a raw RLP-encoded receipt blob and extracts
   227  // specific fields from it.
   228  func extractReceiptFields(receiptRLP rlp.RawValue) (uint64, uint, error) {
   229  	receiptList, _, err := rlp.SplitList(receiptRLP)
   230  	if err != nil {
   231  		return 0, 0, err
   232  	}
   233  	// Decode the field: receipt status
   234  	// for receipt before the byzantium fork:
   235  	// - bytes: post state root
   236  	// for receipt after the byzantium fork:
   237  	// - bytes: receipt status flag
   238  	_, _, rest, err := rlp.Split(receiptList)
   239  	if err != nil {
   240  		return 0, 0, err
   241  	}
   242  	// Decode the field: cumulative gas used (type: uint64)
   243  	gasUsed, rest, err := rlp.SplitUint64(rest)
   244  	if err != nil {
   245  		return 0, 0, err
   246  	}
   247  	// Decode the field: logs (type: rlp list)
   248  	logList, _, err := rlp.SplitList(rest)
   249  	if err != nil {
   250  		return 0, 0, err
   251  	}
   252  	logCount, err := rlp.CountValues(logList)
   253  	if err != nil {
   254  		return 0, 0, err
   255  	}
   256  	return gasUsed, uint(logCount), nil
   257  }
   258  
   259  // RawReceiptContext carries the contextual information that is needed to derive
   260  // a complete receipt from a raw one.
   261  type RawReceiptContext struct {
   262  	GasUsed  uint64 // Amount of gas used by the associated transaction
   263  	LogIndex uint   // Starting index of the logs within the block
   264  }
   265  
   266  // ReadCanonicalRawReceipt reads a raw receipt at the specified position. It also
   267  // returns the gas used by the associated transaction and the starting index of
   268  // the logs within the block. The main difference with ReadCanonicalReceipt is
   269  // that the additional positional fields are not directly included in the receipt.
   270  // Notably, only receipts from the canonical chain are visible.
   271  func ReadCanonicalRawReceipt(db ethdb.Reader, blockHash common.Hash, blockNumber, txIndex uint64) (*types.Receipt, RawReceiptContext, error) {
   272  	receiptIt, err := rlp.NewListIterator(ReadCanonicalReceiptsRLP(db, blockNumber, &blockHash))
   273  	if err != nil {
   274  		return nil, RawReceiptContext{}, err
   275  	}
   276  	var (
   277  		cumulativeGasUsed uint64
   278  		logIndex          uint
   279  	)
   280  	for i := uint64(0); i <= txIndex; i++ {
   281  		// Unexpected iteration error
   282  		if receiptIt.Err() != nil {
   283  			return nil, RawReceiptContext{}, receiptIt.Err()
   284  		}
   285  		// Unexpected end of iteration
   286  		if !receiptIt.Next() {
   287  			return nil, RawReceiptContext{}, fmt.Errorf("receipt not found, %d, %x, %d", blockNumber, blockHash, txIndex)
   288  		}
   289  		if i == txIndex {
   290  			var stored types.ReceiptForStorage
   291  			if err := rlp.DecodeBytes(receiptIt.Value(), &stored); err != nil {
   292  				return nil, RawReceiptContext{}, err
   293  			}
   294  			return (*types.Receipt)(&stored), RawReceiptContext{
   295  				GasUsed:  stored.CumulativeGasUsed - cumulativeGasUsed,
   296  				LogIndex: logIndex,
   297  			}, nil
   298  		} else {
   299  			gas, logs, err := extractReceiptFields(receiptIt.Value())
   300  			if err != nil {
   301  				return nil, RawReceiptContext{}, err
   302  			}
   303  			cumulativeGasUsed = gas
   304  			logIndex += logs
   305  		}
   306  	}
   307  	return nil, RawReceiptContext{}, fmt.Errorf("receipt not found, %d, %x, %d", blockNumber, blockHash, txIndex)
   308  }
   309  
   310  // ReadFilterMapExtRow retrieves a filter map row at the given mapRowIndex
   311  // (see filtermaps.mapRowIndex for the storage index encoding).
   312  // Note that zero length rows are not stored in the database and therefore all
   313  // non-existent entries are interpreted as empty rows and return no error.
   314  // Also note that the mapRowIndex indexing scheme is the same as the one
   315  // proposed in EIP-7745 for tree-hashing the filter map structure and for the
   316  // same data proximity reasons it is also suitable for database representation.
   317  // See also:
   318  // https://eips.ethereum.org/EIPS/eip-7745#hash-tree-structure
   319  func ReadFilterMapExtRow(db ethdb.KeyValueReader, mapRowIndex uint64, bitLength uint) ([]uint32, error) {
   320  	byteLength := int(bitLength) / 8
   321  	if int(bitLength) != byteLength*8 {
   322  		panic("invalid bit length")
   323  	}
   324  	key := filterMapRowKey(mapRowIndex, false)
   325  	has, err := db.Has(key)
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	if !has {
   330  		return nil, nil
   331  	}
   332  	encRow, err := db.Get(key)
   333  	if err != nil {
   334  		return nil, err
   335  	}
   336  	if len(encRow)%byteLength != 0 {
   337  		return nil, errors.New("invalid encoded extended filter row length")
   338  	}
   339  	row := make([]uint32, len(encRow)/byteLength)
   340  	var b [4]byte
   341  	for i := range row {
   342  		copy(b[:byteLength], encRow[i*byteLength:(i+1)*byteLength])
   343  		row[i] = binary.LittleEndian.Uint32(b[:])
   344  	}
   345  	return row, nil
   346  }
   347  
   348  func ReadFilterMapBaseRows(db ethdb.KeyValueReader, mapRowIndex uint64, rowCount uint32, bitLength uint) ([][]uint32, error) {
   349  	byteLength := int(bitLength) / 8
   350  	if int(bitLength) != byteLength*8 {
   351  		panic("invalid bit length")
   352  	}
   353  	key := filterMapRowKey(mapRowIndex, true)
   354  	has, err := db.Has(key)
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  	rows := make([][]uint32, rowCount)
   359  	if !has {
   360  		return rows, nil
   361  	}
   362  	encRows, err := db.Get(key)
   363  	if err != nil {
   364  		return nil, err
   365  	}
   366  	encLen := len(encRows)
   367  	var (
   368  		entryCount, entriesInRow, rowIndex, headerLen, headerBits int
   369  		headerByte                                                byte
   370  	)
   371  	for headerLen+byteLength*entryCount < encLen {
   372  		if headerBits == 0 {
   373  			headerByte = encRows[headerLen]
   374  			headerLen++
   375  			headerBits = 8
   376  		}
   377  		if headerByte&1 > 0 {
   378  			entriesInRow++
   379  			entryCount++
   380  		} else {
   381  			if entriesInRow > 0 {
   382  				rows[rowIndex] = make([]uint32, entriesInRow)
   383  				entriesInRow = 0
   384  			}
   385  			rowIndex++
   386  		}
   387  		headerByte >>= 1
   388  		headerBits--
   389  	}
   390  	if headerLen+byteLength*entryCount > encLen {
   391  		return nil, errors.New("invalid encoded base filter rows length")
   392  	}
   393  	if entriesInRow > 0 {
   394  		rows[rowIndex] = make([]uint32, entriesInRow)
   395  	}
   396  	nextEntry := headerLen
   397  	for _, row := range rows {
   398  		for i := range row {
   399  			var b [4]byte
   400  			copy(b[:byteLength], encRows[nextEntry:nextEntry+byteLength])
   401  			row[i] = binary.LittleEndian.Uint32(b[:])
   402  			nextEntry += byteLength
   403  		}
   404  	}
   405  	return rows, nil
   406  }
   407  
   408  // WriteFilterMapExtRow stores an extended filter map row at the given mapRowIndex
   409  // or deletes any existing entry if the row is empty.
   410  func WriteFilterMapExtRow(db ethdb.KeyValueWriter, mapRowIndex uint64, row []uint32, bitLength uint) {
   411  	byteLength := int(bitLength) / 8
   412  	if int(bitLength) != byteLength*8 {
   413  		panic("invalid bit length")
   414  	}
   415  	var err error
   416  	if len(row) > 0 {
   417  		encRow := make([]byte, len(row)*byteLength)
   418  		for i, c := range row {
   419  			var b [4]byte
   420  			binary.LittleEndian.PutUint32(b[:], c)
   421  			copy(encRow[i*byteLength:(i+1)*byteLength], b[:byteLength])
   422  		}
   423  		err = db.Put(filterMapRowKey(mapRowIndex, false), encRow)
   424  	} else {
   425  		err = db.Delete(filterMapRowKey(mapRowIndex, false))
   426  	}
   427  	if err != nil {
   428  		log.Crit("Failed to store extended filter map row", "err", err)
   429  	}
   430  }
   431  
   432  func WriteFilterMapBaseRows(db ethdb.KeyValueWriter, mapRowIndex uint64, rows [][]uint32, bitLength uint) {
   433  	byteLength := int(bitLength) / 8
   434  	if int(bitLength) != byteLength*8 {
   435  		panic("invalid bit length")
   436  	}
   437  	var entryCount, zeroBits int
   438  	for i, row := range rows {
   439  		if len(row) > 0 {
   440  			entryCount += len(row)
   441  			zeroBits = i
   442  		}
   443  	}
   444  	var err error
   445  	if entryCount > 0 {
   446  		headerLen := (zeroBits + entryCount + 7) / 8
   447  		encRows := make([]byte, headerLen+entryCount*byteLength)
   448  		nextEntry := headerLen
   449  
   450  		headerPtr, headerByte := 0, byte(1)
   451  		addHeaderBit := func(bit bool) {
   452  			if bit {
   453  				encRows[headerPtr] += headerByte
   454  			}
   455  			if headerByte += headerByte; headerByte == 0 {
   456  				headerPtr++
   457  				headerByte = 1
   458  			}
   459  		}
   460  
   461  		for _, row := range rows {
   462  			for _, entry := range row {
   463  				var b [4]byte
   464  				binary.LittleEndian.PutUint32(b[:], entry)
   465  				copy(encRows[nextEntry:nextEntry+byteLength], b[:byteLength])
   466  				nextEntry += byteLength
   467  				addHeaderBit(true)
   468  			}
   469  			if zeroBits == 0 {
   470  				break
   471  			}
   472  			addHeaderBit(false)
   473  			zeroBits--
   474  		}
   475  		err = db.Put(filterMapRowKey(mapRowIndex, true), encRows)
   476  	} else {
   477  		err = db.Delete(filterMapRowKey(mapRowIndex, true))
   478  	}
   479  	if err != nil {
   480  		log.Crit("Failed to store base filter map rows", "err", err)
   481  	}
   482  }
   483  
   484  func DeleteFilterMapRows(db ethdb.KeyValueStore, mapRows common.Range[uint64], hashScheme bool, stopCallback func(bool) bool) error {
   485  	return SafeDeleteRange(db, filterMapRowKey(mapRows.First(), false), filterMapRowKey(mapRows.AfterLast(), false), hashScheme, stopCallback)
   486  }
   487  
   488  // ReadFilterMapLastBlock retrieves the number of the block that generated the
   489  // last log value entry of the given map.
   490  func ReadFilterMapLastBlock(db ethdb.KeyValueReader, mapIndex uint32) (uint64, common.Hash, error) {
   491  	enc, err := db.Get(filterMapLastBlockKey(mapIndex))
   492  	if err != nil {
   493  		return 0, common.Hash{}, err
   494  	}
   495  	if len(enc) != 40 {
   496  		return 0, common.Hash{}, errors.New("invalid block number and id encoding")
   497  	}
   498  	var id common.Hash
   499  	copy(id[:], enc[8:])
   500  	return binary.BigEndian.Uint64(enc[:8]), id, nil
   501  }
   502  
   503  // WriteFilterMapLastBlock stores the number of the block that generated the
   504  // last log value entry of the given map.
   505  func WriteFilterMapLastBlock(db ethdb.KeyValueWriter, mapIndex uint32, blockNumber uint64, id common.Hash) {
   506  	var enc [40]byte
   507  	binary.BigEndian.PutUint64(enc[:8], blockNumber)
   508  	copy(enc[8:], id[:])
   509  	if err := db.Put(filterMapLastBlockKey(mapIndex), enc[:]); err != nil {
   510  		log.Crit("Failed to store filter map last block pointer", "err", err)
   511  	}
   512  }
   513  
   514  // DeleteFilterMapLastBlock deletes the number of the block that generated the
   515  // last log value entry of the given map.
   516  func DeleteFilterMapLastBlock(db ethdb.KeyValueWriter, mapIndex uint32) {
   517  	if err := db.Delete(filterMapLastBlockKey(mapIndex)); err != nil {
   518  		log.Crit("Failed to delete filter map last block pointer", "err", err)
   519  	}
   520  }
   521  
   522  func DeleteFilterMapLastBlocks(db ethdb.KeyValueStore, maps common.Range[uint32], hashScheme bool, stopCallback func(bool) bool) error {
   523  	return SafeDeleteRange(db, filterMapLastBlockKey(maps.First()), filterMapLastBlockKey(maps.AfterLast()), hashScheme, stopCallback)
   524  }
   525  
   526  // ReadBlockLvPointer retrieves the starting log value index where the log values
   527  // generated by the given block are located.
   528  func ReadBlockLvPointer(db ethdb.KeyValueReader, blockNumber uint64) (uint64, error) {
   529  	encPtr, err := db.Get(filterMapBlockLVKey(blockNumber))
   530  	if err != nil {
   531  		return 0, err
   532  	}
   533  	if len(encPtr) != 8 {
   534  		return 0, errors.New("invalid log value pointer encoding")
   535  	}
   536  	return binary.BigEndian.Uint64(encPtr), nil
   537  }
   538  
   539  // WriteBlockLvPointer stores the starting log value index where the log values
   540  // generated by the given block are located.
   541  func WriteBlockLvPointer(db ethdb.KeyValueWriter, blockNumber, lvPointer uint64) {
   542  	var encPtr [8]byte
   543  	binary.BigEndian.PutUint64(encPtr[:], lvPointer)
   544  	if err := db.Put(filterMapBlockLVKey(blockNumber), encPtr[:]); err != nil {
   545  		log.Crit("Failed to store block log value pointer", "err", err)
   546  	}
   547  }
   548  
   549  // DeleteBlockLvPointer deletes the starting log value index where the log values
   550  // generated by the given block are located.
   551  func DeleteBlockLvPointer(db ethdb.KeyValueWriter, blockNumber uint64) {
   552  	if err := db.Delete(filterMapBlockLVKey(blockNumber)); err != nil {
   553  		log.Crit("Failed to delete block log value pointer", "err", err)
   554  	}
   555  }
   556  
   557  func DeleteBlockLvPointers(db ethdb.KeyValueStore, blocks common.Range[uint64], hashScheme bool, stopCallback func(bool) bool) error {
   558  	return SafeDeleteRange(db, filterMapBlockLVKey(blocks.First()), filterMapBlockLVKey(blocks.AfterLast()), hashScheme, stopCallback)
   559  }
   560  
   561  // FilterMapsRange is a storage representation of the block range covered by the
   562  // filter maps structure and the corresponting log value index range.
   563  type FilterMapsRange struct {
   564  	Version                      uint32
   565  	HeadIndexed                  bool
   566  	HeadDelimiter                uint64
   567  	BlocksFirst, BlocksAfterLast uint64
   568  	MapsFirst, MapsAfterLast     uint32
   569  	TailPartialEpoch             uint32
   570  }
   571  
   572  // ReadFilterMapsRange retrieves the filter maps range data. Note that if the
   573  // database entry is not present, that is interpreted as a valid non-initialized
   574  // state and returns a blank range structure and no error.
   575  func ReadFilterMapsRange(db ethdb.KeyValueReader) (FilterMapsRange, bool, error) {
   576  	if has, err := db.Has(filterMapsRangeKey); err != nil || !has {
   577  		return FilterMapsRange{}, false, err
   578  	}
   579  	encRange, err := db.Get(filterMapsRangeKey)
   580  	if err != nil {
   581  		return FilterMapsRange{}, false, err
   582  	}
   583  	var fmRange FilterMapsRange
   584  	if err := rlp.DecodeBytes(encRange, &fmRange); err != nil {
   585  		return FilterMapsRange{}, false, err
   586  	}
   587  
   588  	return fmRange, true, nil
   589  }
   590  
   591  // WriteFilterMapsRange stores the filter maps range data.
   592  func WriteFilterMapsRange(db ethdb.KeyValueWriter, fmRange FilterMapsRange) {
   593  	encRange, err := rlp.EncodeToBytes(&fmRange)
   594  	if err != nil {
   595  		log.Crit("Failed to encode filter maps range", "err", err)
   596  	}
   597  	if err := db.Put(filterMapsRangeKey, encRange); err != nil {
   598  		log.Crit("Failed to store filter maps range", "err", err)
   599  	}
   600  }
   601  
   602  // DeleteFilterMapsRange deletes the filter maps range data which is interpreted
   603  // as reverting to the un-initialized state.
   604  func DeleteFilterMapsRange(db ethdb.KeyValueWriter) {
   605  	if err := db.Delete(filterMapsRangeKey); err != nil {
   606  		log.Crit("Failed to delete filter maps range", "err", err)
   607  	}
   608  }
   609  
   610  // deletePrefixRange deletes everything with the given prefix from the database.
   611  func deletePrefixRange(db ethdb.KeyValueStore, prefix []byte, hashScheme bool, stopCallback func(bool) bool) error {
   612  	end := bytes.Clone(prefix)
   613  	end[len(end)-1]++
   614  	return SafeDeleteRange(db, prefix, end, hashScheme, stopCallback)
   615  }
   616  
   617  // DeleteFilterMapsDb removes the entire filter maps database
   618  func DeleteFilterMapsDb(db ethdb.KeyValueStore, hashScheme bool, stopCallback func(bool) bool) error {
   619  	return deletePrefixRange(db, []byte(filterMapsPrefix), hashScheme, stopCallback)
   620  }
   621  
   622  // DeleteBloomBitsDb removes the old bloombits database and the associated
   623  // chain indexer database.
   624  func DeleteBloomBitsDb(db ethdb.KeyValueStore, hashScheme bool, stopCallback func(bool) bool) error {
   625  	if err := deletePrefixRange(db, bloomBitsPrefix, hashScheme, stopCallback); err != nil {
   626  		return err
   627  	}
   628  	return deletePrefixRange(db, bloomBitsMetaPrefix, hashScheme, stopCallback)
   629  }