16#include <validation.h>
60 explicit DBHeightKey(
int height_in) : height(height_in) {}
62 template<
typename Stream>
69 template<
typename Stream>
74 throw std::ios_base::failure(
"Invalid format for block filter index DB height key");
83 explicit DBHashKey(
const uint256& hash_in) : hash(hash_in) {}
89 throw std::ios_base::failure(
"Invalid format for block filter index DB hash key");
101 size_t n_cache_size,
bool f_memory,
bool f_wipe)
103 , m_filter_type(filter_type)
106 if (filter_name.empty())
throw std::invalid_argument(
"unknown filter_type");
111 m_db = std::make_unique<BaseIndex::DB>(path /
"db", n_cache_size, f_memory, f_wipe);
122 LogError(
"%s: Cannot read current %s state; index may be corrupted\n",
134 if (!op_last_header) {
135 LogError(
"Cannot read last block filter header; index may be corrupted\n");
151 LogError(
"%s: Failed to open filter file %d\n", __func__, pos.
nFile);
154 if (!file.Commit()) {
155 LogError(
"%s: Failed to commit filter file %d\n", __func__, pos.
nFile);
166 if (filein.IsNull()) {
172 std::vector<uint8_t> encoded_filter;
174 filein >> block_hash >> encoded_filter;
175 if (
Hash(encoded_filter) != hash) {
176 LogError(
"Checksum mismatch in filter decode.\n");
181 catch (
const std::exception& e) {
182 LogError(
"%s: Failed to deserialize block filter from disk: %s\n", __func__, e.what());
200 if (last_file.IsNull()) {
201 LogPrintf(
"%s: Failed to open filter file %d\n", __func__, pos.
nFile);
204 if (!last_file.Truncate(pos.
nPos)) {
205 LogPrintf(
"%s: Failed to truncate filter file %d\n", __func__, pos.
nFile);
208 if (!last_file.Commit()) {
209 LogPrintf(
"%s: Failed to commit filter file %d\n", __func__, pos.
nFile);
221 LogPrintf(
"%s: out of disk space\n", __func__);
226 if (fileout.IsNull()) {
227 LogPrintf(
"%s: Failed to open filter file %d\n", __func__, pos.
nFile);
237 std::pair<uint256, DBVal> read_out;
238 if (!
m_db->Read(DBHeightKey(height), read_out)) {
242 if (read_out.first != expected_block_hash) {
243 LogError(
"%s: previous block header belongs to unexpected block %s; expected %s\n",
244 __func__, read_out.first.ToString(), expected_block_hash.
ToString());
248 return read_out.second.header;
275 if (bytes_written == 0)
return false;
277 std::pair<uint256, DBVal> value;
279 value.second.hash = filter.
GetHash();
280 value.second.header = filter_header;
283 if (!
m_db->Write(DBHeightKey(block_height), value)) {
292 const std::string& index_name,
293 int start_height,
int stop_height)
295 DBHeightKey key(start_height);
298 for (
int height = start_height; height <= stop_height; ++height) {
299 if (!db_it.
GetKey(key) || key.height != height) {
300 LogError(
"%s: unexpected key in %s: expected (%c, %d)\n",
305 std::pair<uint256, DBVal> value;
307 LogError(
"%s: unable to read value in %s at key (%c, %d)\n",
312 batch.
Write(DBHashKey(value.first), std::move(value.second));
322 std::unique_ptr<CDBIterator> db_it(
m_db->NewIterator());
335 if (!
m_db->WriteBatch(batch))
return false;
346 std::pair<uint256, DBVal> read_out;
347 if (!db.
Read(DBHeightKey(block_index->
nHeight), read_out)) {
351 result = std::move(read_out.second);
361 const CBlockIndex* stop_index, std::vector<DBVal>& results)
363 if (start_height < 0) {
364 LogError(
"%s: start height (%d) is negative\n", __func__, start_height);
367 if (start_height > stop_index->
nHeight) {
368 LogError(
"%s: start height (%d) is greater than stop height (%d)\n",
369 __func__, start_height, stop_index->
nHeight);
373 size_t results_size =
static_cast<size_t>(stop_index->
nHeight - start_height + 1);
374 std::vector<std::pair<uint256, DBVal>>
values(results_size);
376 DBHeightKey key(start_height);
377 std::unique_ptr<CDBIterator> db_it(db.
NewIterator());
378 db_it->Seek(DBHeightKey(start_height));
379 for (
int height = start_height; height <= stop_index->
nHeight; ++height) {
380 if (!db_it->Valid() || !db_it->GetKey(key) || key.height != height) {
384 size_t i =
static_cast<size_t>(height - start_height);
385 if (!db_it->GetValue(
values[i])) {
386 LogError(
"%s: unable to read value in %s at key (%c, %d)\n",
394 results.resize(results_size);
399 block_index && block_index->
nHeight >= start_height;
400 block_index = block_index->pprev) {
401 uint256 block_hash = block_index->GetBlockHash();
403 size_t i =
static_cast<size_t>(block_index->nHeight - start_height);
404 if (block_hash ==
values[i].first) {
405 results[i] = std::move(
values[i].second);
409 if (!db.
Read(DBHashKey(block_hash), results[i])) {
410 LogError(
"%s: unable to read value in %s at key (%c, %s)\n",
437 auto header = m_headers_cache.find(block_index->
GetBlockHash());
438 if (header != m_headers_cache.end()) {
439 header_out = header->second;
452 m_headers_cache.emplace(block_index->
GetBlockHash(), entry.header);
455 header_out = entry.header;
460 std::vector<BlockFilter>& filters_out)
const
462 std::vector<DBVal> entries;
467 filters_out.resize(entries.size());
468 auto filter_pos_it = filters_out.begin();
469 for (
const auto& entry : entries) {
480 std::vector<uint256>& hashes_out)
const
483 std::vector<DBVal> entries;
489 hashes_out.reserve(entries.size());
490 for (
const auto& entry : entries) {
491 hashes_out.push_back(entry.hash);
508 size_t n_cache_size,
bool f_memory,
bool f_wipe)
511 std::forward_as_tuple(filter_type),
512 std::forward_as_tuple(make_chain(), filter_type,
513 n_cache_size, f_memory, f_wipe));
514 return result.second;
const std::string & BlockFilterTypeName(BlockFilterType filter_type)
Get the human-readable name for a filter type.
constexpr unsigned int FLTR_FILE_CHUNK_SIZE
The pre-allocation chunk size for fltr?????.dat files.
bool DestroyBlockFilterIndex(BlockFilterType filter_type)
Destroy the block filter index with the given type.
void DestroyAllBlockFilterIndexes()
Destroy all open block filter indexes.
static bool CopyHeightIndexToHashIndex(CDBIterator &db_it, CDBBatch &batch, const std::string &index_name, int start_height, int stop_height)
BlockFilterIndex * GetBlockFilterIndex(BlockFilterType filter_type)
Get a block filter index by type.
constexpr uint8_t DB_FILTER_POS
static bool LookupOne(const CDBWrapper &db, const CBlockIndex *block_index, DBVal &result)
constexpr unsigned int MAX_FLTR_FILE_SIZE
constexpr uint8_t DB_BLOCK_HASH
void ForEachBlockFilterIndex(std::function< void(BlockFilterIndex &)> fn)
Iterate over all running block filter indexes, invoking fn on each.
constexpr size_t CF_HEADERS_CACHE_MAX_SZ
Maximum size of the cfheaders cache We have a limit to prevent a bug in filling this cache potentiall...
bool InitBlockFilterIndex(std::function< std::unique_ptr< interfaces::Chain >()> make_chain, BlockFilterType filter_type, size_t n_cache_size, bool f_memory, bool f_wipe)
Initialize a block filter index for the given type if one does not already exist.
static bool LookupRange(CDBWrapper &db, const std::string &index_name, int start_height, const CBlockIndex *stop_index, std::vector< DBVal > &results)
constexpr uint8_t DB_BLOCK_HEIGHT
static std::map< BlockFilterType, BlockFilterIndex > g_filter_indexes
static constexpr int CFCHECKPT_INTERVAL
Interval between compact filter checkpoints.
#define Assert(val)
Identity function.
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Non-refcounted RAII wrapper for FILE*.
Base class for indices of blockchain data.
const std::string & GetName() const LIFETIMEBOUND
Get the name of the index for display in logs.
Chainstate * m_chainstate
Complete block filter struct as defined in BIP 157.
const uint256 & GetBlockHash() const LIFETIMEBOUND
const std::vector< unsigned char > & GetEncodedFilter() const LIFETIMEBOUND
uint256 ComputeHeader(const uint256 &prev_header) const
Compute the filter header given the previous one.
BlockFilterType GetFilterType() const
uint256 GetHash() const
Compute the filter hash.
BlockFilterIndex is used to store and retrieve block filters, hashes, and headers for a range of bloc...
std::unique_ptr< BaseIndex::DB > m_db
bool CustomInit(const std::optional< interfaces::BlockRef > &block) override
Initialize internal state from the database and block index.
bool LookupFilterRange(int start_height, const CBlockIndex *stop_index, std::vector< BlockFilter > &filters_out) const
Get a range of filters between two heights on a chain.
BlockFilterType GetFilterType() const
bool CustomCommit(CDBBatch &batch) override
Virtual method called internally by Commit that can be overridden to atomically commit more index sta...
BlockFilterType m_filter_type
BlockFilterIndex(std::unique_ptr< interfaces::Chain > chain, BlockFilterType filter_type, size_t n_cache_size, bool f_memory=false, bool f_wipe=false)
Constructs the index, which becomes available to be queried.
std::unique_ptr< FlatFileSeq > m_filter_fileseq
bool LookupFilter(const CBlockIndex *block_index, BlockFilter &filter_out) const
Get a single filter by block.
bool ReadFilterFromDisk(const FlatFilePos &pos, const uint256 &hash, BlockFilter &filter) const
bool LookupFilterHashRange(int start_height, const CBlockIndex *stop_index, std::vector< uint256 > &hashes_out) const
Get a range of filter hashes between two heights on a chain.
bool CustomAppend(const interfaces::BlockInfo &block) override
Write update index entries for a newly connected block.
size_t WriteFilterToDisk(FlatFilePos &pos, const BlockFilter &filter)
bool LookupFilterHeader(const CBlockIndex *block_index, uint256 &header_out) EXCLUSIVE_LOCKS_REQUIRED(!m_cs_headers_cache)
Get a single filter header by block.
std::optional< uint256 > ReadFilterHeader(int height, const uint256 &expected_block_hash)
bool Write(const BlockFilter &filter, uint32_t block_height, const uint256 &filter_header)
bool CustomRewind(const interfaces::BlockRef ¤t_tip, const interfaces::BlockRef &new_tip) override
Rewind index to an earlier chain tip during a chain reorg.
FlatFilePos m_next_filter_pos
The block chain is a tree shaped structure starting with the genesis block at the root,...
uint256 GetBlockHash() const
int nHeight
height of the entry in the chain. The genesis block has height 0
Undo information for a CBlock.
Batch of changes queued to be written to a CDBWrapper.
void Write(const K &key, const V &value)
bool Read(const K &key, V &value) const
CDBIterator * NewIterator()
node::BlockManager & m_blockman
Reference to a BlockManager instance which itself is shared across all Chainstate instances.
std::string ToString() const
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
CBlockIndex * LookupBlockIndex(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
bool UndoReadFromDisk(CBlockUndo &blockundo, const CBlockIndex &index) const
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
uint256 Hash(const T &in1)
Compute the 256-bit hash of an object.
static path u8path(const std::string &utf8_str)
static bool create_directories(const std::filesystem::path &p)
Create directory (and if necessary its parents), unless the leaf directory already exists or is a sym...
static const int64_t values[]
A selection of numbers that do not trigger int64_t overflow when added/subtracted.
size_t GetSerializeSize(const T &t)
void Serialize(Stream &, V)=delete
uint8_t ser_readdata8(Stream &s)
void ser_writedata32be(Stream &s, uint32_t obj)
#define SERIALIZE_METHODS(cls, obj)
Implement the Serialize and Unserialize methods by delegating to a single templated static method tha...
void Unserialize(Stream &, V)=delete
void ser_writedata8(Stream &s, uint8_t obj)
uint32_t ser_readdata32be(Stream &s)
Block data sent with blockConnected, blockDisconnected notifications.
Hash/height pair to help track and identify blocks.
#define WITH_LOCK(cs, code)
Run code while locking a mutex.