mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-03-17 00:20:39 +00:00
The iterator loop in findTxInBlockBody returned the outer-scoped err when iter.Err() was non-nil, which could incorrectly propagate a nil or stale error and hide actual RLP decoding issues. This patch returns iter.Err() as intended by the rlp list iterator API, matching established patterns elsewhere in the codebase and improving diagnostics when encountering malformed transaction entries.
633 lines
22 KiB
Go
633 lines
22 KiB
Go
// Copyright 2018 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package rawdb
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
"github.com/ethereum/go-ethereum/log"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
)
|
|
|
|
// DecodeTxLookupEntry decodes the supplied tx lookup data.
|
|
func DecodeTxLookupEntry(data []byte, db ethdb.Reader) *uint64 {
|
|
// Database v6 tx lookup just stores the block number
|
|
if len(data) < common.HashLength {
|
|
number := new(big.Int).SetBytes(data).Uint64()
|
|
return &number
|
|
}
|
|
// Database v4-v5 tx lookup format just stores the hash
|
|
if len(data) == common.HashLength {
|
|
number, ok := ReadHeaderNumber(db, common.BytesToHash(data))
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return &number
|
|
}
|
|
// Finally try database v3 tx lookup format
|
|
var entry LegacyTxLookupEntry
|
|
if err := rlp.DecodeBytes(data, &entry); err != nil {
|
|
log.Error("Invalid transaction lookup entry RLP", "blob", data, "err", err)
|
|
return nil
|
|
}
|
|
return &entry.BlockIndex
|
|
}
|
|
|
|
// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
|
|
// hash to allow retrieving the transaction or receipt by hash.
|
|
func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) *uint64 {
|
|
data, _ := db.Get(txLookupKey(hash))
|
|
if len(data) == 0 {
|
|
return nil
|
|
}
|
|
return DecodeTxLookupEntry(data, db)
|
|
}
|
|
|
|
// writeTxLookupEntry stores a positional metadata for a transaction,
|
|
// enabling hash based transaction and receipt lookups.
|
|
func writeTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash, numberBytes []byte) {
|
|
if err := db.Put(txLookupKey(hash), numberBytes); err != nil {
|
|
log.Crit("Failed to store transaction lookup entry", "err", err)
|
|
}
|
|
}
|
|
|
|
// WriteTxLookupEntries is identical to WriteTxLookupEntry, but it works on
|
|
// a list of hashes
|
|
func WriteTxLookupEntries(db ethdb.KeyValueWriter, number uint64, hashes []common.Hash) {
|
|
numberBytes := new(big.Int).SetUint64(number).Bytes()
|
|
for _, hash := range hashes {
|
|
writeTxLookupEntry(db, hash, numberBytes)
|
|
}
|
|
}
|
|
|
|
// WriteTxLookupEntriesByBlock stores a positional metadata for every transaction from
|
|
// a block, enabling hash based transaction and receipt lookups.
|
|
func WriteTxLookupEntriesByBlock(db ethdb.KeyValueWriter, block *types.Block) {
|
|
numberBytes := block.Number().Bytes()
|
|
for _, tx := range block.Transactions() {
|
|
writeTxLookupEntry(db, tx.Hash(), numberBytes)
|
|
}
|
|
}
|
|
|
|
// DeleteTxLookupEntry removes all transaction data associated with a hash.
|
|
func DeleteTxLookupEntry(db ethdb.KeyValueWriter, hash common.Hash) {
|
|
if err := db.Delete(txLookupKey(hash)); err != nil {
|
|
log.Crit("Failed to delete transaction lookup entry", "err", err)
|
|
}
|
|
}
|
|
|
|
// DeleteTxLookupEntries removes all transaction lookups for a given block.
|
|
func DeleteTxLookupEntries(db ethdb.KeyValueWriter, hashes []common.Hash) {
|
|
for _, hash := range hashes {
|
|
DeleteTxLookupEntry(db, hash)
|
|
}
|
|
}
|
|
|
|
// DeleteAllTxLookupEntries purges all the transaction indexes in the database.
|
|
// If condition is specified, only the entry with condition as True will be
|
|
// removed; If condition is not specified, the entry is deleted.
|
|
func DeleteAllTxLookupEntries(db ethdb.KeyValueStore, condition func(common.Hash, []byte) bool) {
|
|
iter := NewKeyLengthIterator(db.NewIterator(txLookupPrefix, nil), common.HashLength+len(txLookupPrefix))
|
|
defer iter.Release()
|
|
|
|
batch := db.NewBatch()
|
|
for iter.Next() {
|
|
txhash := common.Hash(iter.Key()[1:])
|
|
if condition == nil || condition(txhash, iter.Value()) {
|
|
batch.Delete(iter.Key())
|
|
}
|
|
if batch.ValueSize() >= ethdb.IdealBatchSize {
|
|
if err := batch.Write(); err != nil {
|
|
log.Crit("Failed to delete transaction lookup entries", "err", err)
|
|
}
|
|
batch.Reset()
|
|
}
|
|
}
|
|
if batch.ValueSize() > 0 {
|
|
if err := batch.Write(); err != nil {
|
|
log.Crit("Failed to delete transaction lookup entries", "err", err)
|
|
}
|
|
batch.Reset()
|
|
}
|
|
}
|
|
|
|
// findTxInBlockBody traverses the given RLP-encoded block body, searching for
|
|
// the transaction specified by its hash.
|
|
func findTxInBlockBody(blockbody rlp.RawValue, target common.Hash) (*types.Transaction, uint64, error) {
|
|
txnListRLP, _, err := rlp.SplitList(blockbody)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
iter, err := rlp.NewListIterator(txnListRLP)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
txIndex := uint64(0)
|
|
for iter.Next() {
|
|
if iter.Err() != nil {
|
|
return nil, 0, iter.Err()
|
|
}
|
|
// The preimage for the hash calculation of legacy transactions
|
|
// is just their RLP encoding. For typed (EIP-2718) transactions,
|
|
// which are encoded as byte arrays, the preimage is the content of
|
|
// the byte array, so trim their prefix here.
|
|
txRLP := iter.Value()
|
|
kind, txHashPayload, _, err := rlp.Split(txRLP)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if kind == rlp.List { // Legacy transaction
|
|
txHashPayload = txRLP
|
|
}
|
|
if crypto.Keccak256Hash(txHashPayload) == target {
|
|
var tx types.Transaction
|
|
if err := rlp.DecodeBytes(txRLP, &tx); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
return &tx, txIndex, nil
|
|
}
|
|
txIndex++
|
|
}
|
|
return nil, 0, errors.New("transaction not found")
|
|
}
|
|
|
|
// ReadCanonicalTransaction retrieves a specific transaction from the database, along
|
|
// with its added positional metadata. Notably, only the transaction in the canonical
|
|
// chain is visible.
|
|
func ReadCanonicalTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
|
|
blockNumber := ReadTxLookupEntry(db, hash)
|
|
if blockNumber == nil {
|
|
return nil, common.Hash{}, 0, 0
|
|
}
|
|
blockHash := ReadCanonicalHash(db, *blockNumber)
|
|
if blockHash == (common.Hash{}) {
|
|
return nil, common.Hash{}, 0, 0
|
|
}
|
|
bodyRLP := ReadCanonicalBodyRLP(db, *blockNumber, &blockHash)
|
|
if bodyRLP == nil {
|
|
log.Error("Transaction referenced missing", "number", *blockNumber, "hash", blockHash)
|
|
return nil, common.Hash{}, 0, 0
|
|
}
|
|
tx, txIndex, err := findTxInBlockBody(bodyRLP, hash)
|
|
if err != nil {
|
|
log.Error("Transaction not found", "number", *blockNumber, "hash", blockHash, "txhash", hash, "err", err)
|
|
return nil, common.Hash{}, 0, 0
|
|
}
|
|
return tx, blockHash, *blockNumber, txIndex
|
|
}
|
|
|
|
// ReadCanonicalReceipt retrieves a specific transaction receipt from the database,
|
|
// along with its added positional metadata. Notably, only the receipt in the canonical
|
|
// chain is visible.
|
|
func ReadCanonicalReceipt(db ethdb.Reader, hash common.Hash, config *params.ChainConfig) (*types.Receipt, common.Hash, uint64, uint64) {
|
|
// Retrieve the context of the receipt based on the transaction hash
|
|
blockNumber := ReadTxLookupEntry(db, hash)
|
|
if blockNumber == nil {
|
|
return nil, common.Hash{}, 0, 0
|
|
}
|
|
blockHash := ReadCanonicalHash(db, *blockNumber)
|
|
if blockHash == (common.Hash{}) {
|
|
return nil, common.Hash{}, 0, 0
|
|
}
|
|
blockHeader := ReadHeader(db, blockHash, *blockNumber)
|
|
if blockHeader == nil {
|
|
return nil, common.Hash{}, 0, 0
|
|
}
|
|
// Read all the receipts from the block and return the one with the matching hash
|
|
receipts := ReadReceipts(db, blockHash, *blockNumber, blockHeader.Time, config)
|
|
for receiptIndex, receipt := range receipts {
|
|
if receipt.TxHash == hash {
|
|
return receipt, blockHash, *blockNumber, uint64(receiptIndex)
|
|
}
|
|
}
|
|
log.Error("Receipt not found", "number", *blockNumber, "hash", blockHash, "txhash", hash)
|
|
return nil, common.Hash{}, 0, 0
|
|
}
|
|
|
|
// extractReceiptFields takes a raw RLP-encoded receipt blob and extracts
|
|
// specific fields from it.
|
|
func extractReceiptFields(receiptRLP rlp.RawValue) (uint64, uint, error) {
|
|
receiptList, _, err := rlp.SplitList(receiptRLP)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
// Decode the field: receipt status
|
|
// for receipt before the byzantium fork:
|
|
// - bytes: post state root
|
|
// for receipt after the byzantium fork:
|
|
// - bytes: receipt status flag
|
|
_, _, rest, err := rlp.Split(receiptList)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
// Decode the field: cumulative gas used (type: uint64)
|
|
gasUsed, rest, err := rlp.SplitUint64(rest)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
// Decode the field: logs (type: rlp list)
|
|
logList, _, err := rlp.SplitList(rest)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
logCount, err := rlp.CountValues(logList)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return gasUsed, uint(logCount), nil
|
|
}
|
|
|
|
// RawReceiptContext carries the contextual information that is needed to derive
|
|
// a complete receipt from a raw one.
|
|
type RawReceiptContext struct {
|
|
GasUsed uint64 // Amount of gas used by the associated transaction
|
|
LogIndex uint // Starting index of the logs within the block
|
|
}
|
|
|
|
// ReadCanonicalRawReceipt reads a raw receipt at the specified position. It also
|
|
// returns the gas used by the associated transaction and the starting index of
|
|
// the logs within the block. The main difference with ReadCanonicalReceipt is
|
|
// that the additional positional fields are not directly included in the receipt.
|
|
// Notably, only receipts from the canonical chain are visible.
|
|
func ReadCanonicalRawReceipt(db ethdb.Reader, blockHash common.Hash, blockNumber, txIndex uint64) (*types.Receipt, RawReceiptContext, error) {
|
|
receiptIt, err := rlp.NewListIterator(ReadCanonicalReceiptsRLP(db, blockNumber, &blockHash))
|
|
if err != nil {
|
|
return nil, RawReceiptContext{}, err
|
|
}
|
|
var (
|
|
cumulativeGasUsed uint64
|
|
logIndex uint
|
|
)
|
|
for i := uint64(0); i <= txIndex; i++ {
|
|
// Unexpected iteration error
|
|
if receiptIt.Err() != nil {
|
|
return nil, RawReceiptContext{}, receiptIt.Err()
|
|
}
|
|
// Unexpected end of iteration
|
|
if !receiptIt.Next() {
|
|
return nil, RawReceiptContext{}, fmt.Errorf("receipt not found, %d, %x, %d", blockNumber, blockHash, txIndex)
|
|
}
|
|
if i == txIndex {
|
|
var stored types.ReceiptForStorage
|
|
if err := rlp.DecodeBytes(receiptIt.Value(), &stored); err != nil {
|
|
return nil, RawReceiptContext{}, err
|
|
}
|
|
return (*types.Receipt)(&stored), RawReceiptContext{
|
|
GasUsed: stored.CumulativeGasUsed - cumulativeGasUsed,
|
|
LogIndex: logIndex,
|
|
}, nil
|
|
} else {
|
|
gas, logs, err := extractReceiptFields(receiptIt.Value())
|
|
if err != nil {
|
|
return nil, RawReceiptContext{}, err
|
|
}
|
|
cumulativeGasUsed = gas
|
|
logIndex += logs
|
|
}
|
|
}
|
|
return nil, RawReceiptContext{}, fmt.Errorf("receipt not found, %d, %x, %d", blockNumber, blockHash, txIndex)
|
|
}
|
|
|
|
// ReadFilterMapExtRow retrieves a filter map row at the given mapRowIndex
|
|
// (see filtermaps.mapRowIndex for the storage index encoding).
|
|
// Note that zero length rows are not stored in the database and therefore all
|
|
// non-existent entries are interpreted as empty rows and return no error.
|
|
// Also note that the mapRowIndex indexing scheme is the same as the one
|
|
// proposed in EIP-7745 for tree-hashing the filter map structure and for the
|
|
// same data proximity reasons it is also suitable for database representation.
|
|
// See also:
|
|
// https://eips.ethereum.org/EIPS/eip-7745#hash-tree-structure
|
|
func ReadFilterMapExtRow(db ethdb.KeyValueReader, mapRowIndex uint64, bitLength uint) ([]uint32, error) {
|
|
byteLength := int(bitLength) / 8
|
|
if int(bitLength) != byteLength*8 {
|
|
panic("invalid bit length")
|
|
}
|
|
key := filterMapRowKey(mapRowIndex, false)
|
|
has, err := db.Has(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !has {
|
|
return nil, nil
|
|
}
|
|
encRow, err := db.Get(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(encRow)%byteLength != 0 {
|
|
return nil, errors.New("invalid encoded extended filter row length")
|
|
}
|
|
row := make([]uint32, len(encRow)/byteLength)
|
|
var b [4]byte
|
|
for i := range row {
|
|
copy(b[:byteLength], encRow[i*byteLength:(i+1)*byteLength])
|
|
row[i] = binary.LittleEndian.Uint32(b[:])
|
|
}
|
|
return row, nil
|
|
}
|
|
|
|
func ReadFilterMapBaseRows(db ethdb.KeyValueReader, mapRowIndex uint64, rowCount uint32, bitLength uint) ([][]uint32, error) {
|
|
byteLength := int(bitLength) / 8
|
|
if int(bitLength) != byteLength*8 {
|
|
panic("invalid bit length")
|
|
}
|
|
key := filterMapRowKey(mapRowIndex, true)
|
|
has, err := db.Has(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rows := make([][]uint32, rowCount)
|
|
if !has {
|
|
return rows, nil
|
|
}
|
|
encRows, err := db.Get(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
encLen := len(encRows)
|
|
var (
|
|
entryCount, entriesInRow, rowIndex, headerLen, headerBits int
|
|
headerByte byte
|
|
)
|
|
for headerLen+byteLength*entryCount < encLen {
|
|
if headerBits == 0 {
|
|
headerByte = encRows[headerLen]
|
|
headerLen++
|
|
headerBits = 8
|
|
}
|
|
if headerByte&1 > 0 {
|
|
entriesInRow++
|
|
entryCount++
|
|
} else {
|
|
if entriesInRow > 0 {
|
|
rows[rowIndex] = make([]uint32, entriesInRow)
|
|
entriesInRow = 0
|
|
}
|
|
rowIndex++
|
|
}
|
|
headerByte >>= 1
|
|
headerBits--
|
|
}
|
|
if headerLen+byteLength*entryCount > encLen {
|
|
return nil, errors.New("invalid encoded base filter rows length")
|
|
}
|
|
if entriesInRow > 0 {
|
|
rows[rowIndex] = make([]uint32, entriesInRow)
|
|
}
|
|
nextEntry := headerLen
|
|
for _, row := range rows {
|
|
for i := range row {
|
|
var b [4]byte
|
|
copy(b[:byteLength], encRows[nextEntry:nextEntry+byteLength])
|
|
row[i] = binary.LittleEndian.Uint32(b[:])
|
|
nextEntry += byteLength
|
|
}
|
|
}
|
|
return rows, nil
|
|
}
|
|
|
|
// WriteFilterMapExtRow stores an extended filter map row at the given mapRowIndex
|
|
// or deletes any existing entry if the row is empty.
|
|
func WriteFilterMapExtRow(db ethdb.KeyValueWriter, mapRowIndex uint64, row []uint32, bitLength uint) {
|
|
byteLength := int(bitLength) / 8
|
|
if int(bitLength) != byteLength*8 {
|
|
panic("invalid bit length")
|
|
}
|
|
var err error
|
|
if len(row) > 0 {
|
|
encRow := make([]byte, len(row)*byteLength)
|
|
for i, c := range row {
|
|
var b [4]byte
|
|
binary.LittleEndian.PutUint32(b[:], c)
|
|
copy(encRow[i*byteLength:(i+1)*byteLength], b[:byteLength])
|
|
}
|
|
err = db.Put(filterMapRowKey(mapRowIndex, false), encRow)
|
|
} else {
|
|
err = db.Delete(filterMapRowKey(mapRowIndex, false))
|
|
}
|
|
if err != nil {
|
|
log.Crit("Failed to store extended filter map row", "err", err)
|
|
}
|
|
}
|
|
|
|
func WriteFilterMapBaseRows(db ethdb.KeyValueWriter, mapRowIndex uint64, rows [][]uint32, bitLength uint) {
|
|
byteLength := int(bitLength) / 8
|
|
if int(bitLength) != byteLength*8 {
|
|
panic("invalid bit length")
|
|
}
|
|
var entryCount, zeroBits int
|
|
for i, row := range rows {
|
|
if len(row) > 0 {
|
|
entryCount += len(row)
|
|
zeroBits = i
|
|
}
|
|
}
|
|
var err error
|
|
if entryCount > 0 {
|
|
headerLen := (zeroBits + entryCount + 7) / 8
|
|
encRows := make([]byte, headerLen+entryCount*byteLength)
|
|
nextEntry := headerLen
|
|
|
|
headerPtr, headerByte := 0, byte(1)
|
|
addHeaderBit := func(bit bool) {
|
|
if bit {
|
|
encRows[headerPtr] += headerByte
|
|
}
|
|
if headerByte += headerByte; headerByte == 0 {
|
|
headerPtr++
|
|
headerByte = 1
|
|
}
|
|
}
|
|
|
|
for _, row := range rows {
|
|
for _, entry := range row {
|
|
var b [4]byte
|
|
binary.LittleEndian.PutUint32(b[:], entry)
|
|
copy(encRows[nextEntry:nextEntry+byteLength], b[:byteLength])
|
|
nextEntry += byteLength
|
|
addHeaderBit(true)
|
|
}
|
|
if zeroBits == 0 {
|
|
break
|
|
}
|
|
addHeaderBit(false)
|
|
zeroBits--
|
|
}
|
|
err = db.Put(filterMapRowKey(mapRowIndex, true), encRows)
|
|
} else {
|
|
err = db.Delete(filterMapRowKey(mapRowIndex, true))
|
|
}
|
|
if err != nil {
|
|
log.Crit("Failed to store base filter map rows", "err", err)
|
|
}
|
|
}
|
|
|
|
func DeleteFilterMapRows(db ethdb.KeyValueStore, mapRows common.Range[uint64], hashScheme bool, stopCallback func(bool) bool) error {
|
|
return SafeDeleteRange(db, filterMapRowKey(mapRows.First(), false), filterMapRowKey(mapRows.AfterLast(), false), hashScheme, stopCallback)
|
|
}
|
|
|
|
// ReadFilterMapLastBlock retrieves the number of the block that generated the
|
|
// last log value entry of the given map.
|
|
func ReadFilterMapLastBlock(db ethdb.KeyValueReader, mapIndex uint32) (uint64, common.Hash, error) {
|
|
enc, err := db.Get(filterMapLastBlockKey(mapIndex))
|
|
if err != nil {
|
|
return 0, common.Hash{}, err
|
|
}
|
|
if len(enc) != 40 {
|
|
return 0, common.Hash{}, errors.New("invalid block number and id encoding")
|
|
}
|
|
var id common.Hash
|
|
copy(id[:], enc[8:])
|
|
return binary.BigEndian.Uint64(enc[:8]), id, nil
|
|
}
|
|
|
|
// WriteFilterMapLastBlock stores the number of the block that generated the
|
|
// last log value entry of the given map.
|
|
func WriteFilterMapLastBlock(db ethdb.KeyValueWriter, mapIndex uint32, blockNumber uint64, id common.Hash) {
|
|
var enc [40]byte
|
|
binary.BigEndian.PutUint64(enc[:8], blockNumber)
|
|
copy(enc[8:], id[:])
|
|
if err := db.Put(filterMapLastBlockKey(mapIndex), enc[:]); err != nil {
|
|
log.Crit("Failed to store filter map last block pointer", "err", err)
|
|
}
|
|
}
|
|
|
|
// DeleteFilterMapLastBlock deletes the number of the block that generated the
|
|
// last log value entry of the given map.
|
|
func DeleteFilterMapLastBlock(db ethdb.KeyValueWriter, mapIndex uint32) {
|
|
if err := db.Delete(filterMapLastBlockKey(mapIndex)); err != nil {
|
|
log.Crit("Failed to delete filter map last block pointer", "err", err)
|
|
}
|
|
}
|
|
|
|
func DeleteFilterMapLastBlocks(db ethdb.KeyValueStore, maps common.Range[uint32], hashScheme bool, stopCallback func(bool) bool) error {
|
|
return SafeDeleteRange(db, filterMapLastBlockKey(maps.First()), filterMapLastBlockKey(maps.AfterLast()), hashScheme, stopCallback)
|
|
}
|
|
|
|
// ReadBlockLvPointer retrieves the starting log value index where the log values
|
|
// generated by the given block are located.
|
|
func ReadBlockLvPointer(db ethdb.KeyValueReader, blockNumber uint64) (uint64, error) {
|
|
encPtr, err := db.Get(filterMapBlockLVKey(blockNumber))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if len(encPtr) != 8 {
|
|
return 0, errors.New("invalid log value pointer encoding")
|
|
}
|
|
return binary.BigEndian.Uint64(encPtr), nil
|
|
}
|
|
|
|
// WriteBlockLvPointer stores the starting log value index where the log values
|
|
// generated by the given block are located.
|
|
func WriteBlockLvPointer(db ethdb.KeyValueWriter, blockNumber, lvPointer uint64) {
|
|
var encPtr [8]byte
|
|
binary.BigEndian.PutUint64(encPtr[:], lvPointer)
|
|
if err := db.Put(filterMapBlockLVKey(blockNumber), encPtr[:]); err != nil {
|
|
log.Crit("Failed to store block log value pointer", "err", err)
|
|
}
|
|
}
|
|
|
|
// DeleteBlockLvPointer deletes the starting log value index where the log values
|
|
// generated by the given block are located.
|
|
func DeleteBlockLvPointer(db ethdb.KeyValueWriter, blockNumber uint64) {
|
|
if err := db.Delete(filterMapBlockLVKey(blockNumber)); err != nil {
|
|
log.Crit("Failed to delete block log value pointer", "err", err)
|
|
}
|
|
}
|
|
|
|
func DeleteBlockLvPointers(db ethdb.KeyValueStore, blocks common.Range[uint64], hashScheme bool, stopCallback func(bool) bool) error {
|
|
return SafeDeleteRange(db, filterMapBlockLVKey(blocks.First()), filterMapBlockLVKey(blocks.AfterLast()), hashScheme, stopCallback)
|
|
}
|
|
|
|
// FilterMapsRange is a storage representation of the block range covered by the
|
|
// filter maps structure and the corresponting log value index range.
|
|
type FilterMapsRange struct {
|
|
Version uint32
|
|
HeadIndexed bool
|
|
HeadDelimiter uint64
|
|
BlocksFirst, BlocksAfterLast uint64
|
|
MapsFirst, MapsAfterLast uint32
|
|
TailPartialEpoch uint32
|
|
}
|
|
|
|
// ReadFilterMapsRange retrieves the filter maps range data. Note that if the
|
|
// database entry is not present, that is interpreted as a valid non-initialized
|
|
// state and returns a blank range structure and no error.
|
|
func ReadFilterMapsRange(db ethdb.KeyValueReader) (FilterMapsRange, bool, error) {
|
|
if has, err := db.Has(filterMapsRangeKey); err != nil || !has {
|
|
return FilterMapsRange{}, false, err
|
|
}
|
|
encRange, err := db.Get(filterMapsRangeKey)
|
|
if err != nil {
|
|
return FilterMapsRange{}, false, err
|
|
}
|
|
var fmRange FilterMapsRange
|
|
if err := rlp.DecodeBytes(encRange, &fmRange); err != nil {
|
|
return FilterMapsRange{}, false, err
|
|
}
|
|
|
|
return fmRange, true, nil
|
|
}
|
|
|
|
// WriteFilterMapsRange stores the filter maps range data.
|
|
func WriteFilterMapsRange(db ethdb.KeyValueWriter, fmRange FilterMapsRange) {
|
|
encRange, err := rlp.EncodeToBytes(&fmRange)
|
|
if err != nil {
|
|
log.Crit("Failed to encode filter maps range", "err", err)
|
|
}
|
|
if err := db.Put(filterMapsRangeKey, encRange); err != nil {
|
|
log.Crit("Failed to store filter maps range", "err", err)
|
|
}
|
|
}
|
|
|
|
// DeleteFilterMapsRange deletes the filter maps range data which is interpreted
|
|
// as reverting to the un-initialized state.
|
|
func DeleteFilterMapsRange(db ethdb.KeyValueWriter) {
|
|
if err := db.Delete(filterMapsRangeKey); err != nil {
|
|
log.Crit("Failed to delete filter maps range", "err", err)
|
|
}
|
|
}
|
|
|
|
// deletePrefixRange deletes everything with the given prefix from the database.
|
|
func deletePrefixRange(db ethdb.KeyValueStore, prefix []byte, hashScheme bool, stopCallback func(bool) bool) error {
|
|
end := bytes.Clone(prefix)
|
|
end[len(end)-1]++
|
|
return SafeDeleteRange(db, prefix, end, hashScheme, stopCallback)
|
|
}
|
|
|
|
// DeleteFilterMapsDb removes the entire filter maps database
|
|
func DeleteFilterMapsDb(db ethdb.KeyValueStore, hashScheme bool, stopCallback func(bool) bool) error {
|
|
return deletePrefixRange(db, []byte(filterMapsPrefix), hashScheme, stopCallback)
|
|
}
|
|
|
|
// DeleteBloomBitsDb removes the old bloombits database and the associated
|
|
// chain indexer database.
|
|
func DeleteBloomBitsDb(db ethdb.KeyValueStore, hashScheme bool, stopCallback func(bool) bool) error {
|
|
if err := deletePrefixRange(db, bloomBitsPrefix, hashScheme, stopCallback); err != nil {
|
|
return err
|
|
}
|
|
return deletePrefixRange(db, bloomBitsMetaPrefix, hashScheme, stopCallback)
|
|
}
|