10 #include <validation.h>
54 DBHeightKey() : height(0) {}
55 explicit DBHeightKey(
int height_in) : height(height_in) {}
57 template<
typename Stream>
64 template<
typename Stream>
69 throw std::ios_base::failure(
"Invalid format for block filter index DB height key");
78 explicit DBHashKey(
const uint256& hash_in) : hash(hash_in) {}
84 throw std::ios_base::failure(
"Invalid format for block filter index DB hash key");
96 size_t n_cache_size,
bool f_memory,
bool f_wipe)
97 : m_filter_type(filter_type)
100 if (filter_name.empty())
throw std::invalid_argument(
"unknown filter_type");
102 fs::path path =
GetDataDir() /
"indexes" /
"blockfilter" / filter_name;
103 fs::create_directories(path);
105 m_name = filter_name +
" block filter index";
106 m_db = MakeUnique<BaseIndex::DB>(path /
"db", n_cache_size, f_memory, f_wipe);
117 return error(
"%s: Cannot read current %s state; index may be corrupted",
135 return error(
"%s: Failed to open filter file %d", __func__, pos.
nFile);
138 return error(
"%s: Failed to commit filter file %d", __func__, pos.
nFile);
153 std::vector<unsigned char> encoded_filter;
155 filein >> block_hash >> encoded_filter;
158 catch (
const std::exception& e) {
159 return error(
"%s: Failed to deserialize block filter from disk: %s", __func__, e.what());
177 LogPrintf(
"%s: Failed to open filter file %d\n", __func__, pos.
nFile);
181 LogPrintf(
"%s: Failed to truncate filter file %d\n", __func__, pos.
nFile);
185 LogPrintf(
"%s: Failed to commit filter file %d\n", __func__, pos.
nFile);
197 LogPrintf(
"%s: out of disk space\n", __func__);
203 LogPrintf(
"%s: Failed to open filter file %d\n", __func__, pos.
nFile);
221 std::pair<uint256, DBVal> read_out;
222 if (!
m_db->Read(DBHeightKey(pindex->
nHeight - 1), read_out)) {
227 if (read_out.first != expected_block_hash) {
228 return error(
"%s: previous block header belongs to unexpected block %s; expected %s",
229 __func__, read_out.first.ToString(), expected_block_hash.
ToString());
232 prev_header = read_out.second.header;
238 if (bytes_written == 0)
return false;
240 std::pair<uint256, DBVal> value;
242 value.second.hash = filter.
GetHash();
246 if (!
m_db->Write(DBHeightKey(pindex->
nHeight), value)) {
255 const std::string& index_name,
256 int start_height,
int stop_height)
258 DBHeightKey key(start_height);
261 for (
int height = start_height; height <= stop_height; ++height) {
262 if (!db_it.
GetKey(key) || key.height != height) {
263 return error(
"%s: unexpected key in %s: expected (%c, %d)",
267 std::pair<uint256, DBVal> value;
269 return error(
"%s: unable to read value in %s at key (%c, %d)",
273 batch.
Write(DBHashKey(value.first), std::move(value.second));
285 std::unique_ptr<CDBIterator> db_it(
m_db->NewIterator());
298 if (!
m_db->WriteBatch(batch))
return false;
307 std::pair<uint256, DBVal> read_out;
308 if (!db.
Read(DBHeightKey(block_index->
nHeight), read_out)) {
312 result = std::move(read_out.second);
322 const CBlockIndex* stop_index, std::vector<DBVal>& results)
324 if (start_height < 0) {
325 return error(
"%s: start height (%d) is negative", __func__, start_height);
327 if (start_height > stop_index->
nHeight) {
328 return error(
"%s: start height (%d) is greater than stop height (%d)",
329 __func__, start_height, stop_index->
nHeight);
332 size_t results_size =
static_cast<size_t>(stop_index->
nHeight - start_height + 1);
333 std::vector<std::pair<uint256, DBVal>> values(results_size);
335 DBHeightKey key(start_height);
336 std::unique_ptr<CDBIterator> db_it(db.
NewIterator());
337 db_it->Seek(DBHeightKey(start_height));
338 for (
int height = start_height; height <= stop_index->
nHeight; ++height) {
339 if (!db_it->Valid() || !db_it->GetKey(key) || key.height != height) {
343 size_t i =
static_cast<size_t>(height - start_height);
344 if (!db_it->GetValue(values[i])) {
345 return error(
"%s: unable to read value in %s at key (%c, %d)",
352 results.resize(results_size);
357 block_index && block_index->
nHeight >= start_height;
358 block_index = block_index->pprev) {
359 uint256 block_hash = block_index->GetBlockHash();
361 size_t i =
static_cast<size_t>(block_index->nHeight - start_height);
362 if (block_hash == values[i].first) {
363 results[i] = std::move(values[i].second);
367 if (!db.
Read(DBHashKey(block_hash), results[i])) {
368 return error(
"%s: unable to read value in %s at key (%c, %s)",
394 auto header = m_headers_cache.find(block_index->
GetBlockHash());
395 if (header != m_headers_cache.end()) {
396 header_out = header->second;
409 m_headers_cache.emplace(block_index->
GetBlockHash(), entry.header);
412 header_out = entry.header;
417 std::vector<BlockFilter>& filters_out)
const
419 std::vector<DBVal> entries;
424 filters_out.resize(entries.size());
425 auto filter_pos_it = filters_out.begin();
426 for (
const auto& entry : entries) {
437 std::vector<uint256>& hashes_out)
const
440 std::vector<DBVal> entries;
446 hashes_out.reserve(entries.size());
447 for (
const auto& entry : entries) {
448 hashes_out.push_back(entry.hash);
465 size_t n_cache_size,
bool f_memory,
bool f_wipe)
468 std::forward_as_tuple(filter_type),
469 std::forward_as_tuple(filter_type,
470 n_cache_size, f_memory, f_wipe));
471 return result.second;
const std::string & BlockFilterTypeName(BlockFilterType filter_type)
Get the human-readable name for a filter type.
constexpr char DB_BLOCK_HASH
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.
constexpr char DB_BLOCK_HEIGHT
static bool CopyHeightIndexToHashIndex(CDBIterator &db_it, CDBBatch &batch, const std::string &index_name, int start_height, int stop_height)
static bool LookupOne(const CDBWrapper &db, const CBlockIndex *block_index, DBVal &result)
BlockFilterIndex * GetBlockFilterIndex(BlockFilterType filter_type)
Get a block filter index by type.
constexpr unsigned int MAX_FLTR_FILE_SIZE
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...
static bool LookupRange(CDBWrapper &db, const std::string &index_name, int start_height, const CBlockIndex *stop_index, std::vector< DBVal > &results)
constexpr char DB_FILTER_POS
bool InitBlockFilterIndex(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 std::map< BlockFilterType, BlockFilterIndex > g_filter_indexes
static constexpr int CFCHECKPT_INTERVAL
Interval between compact filter checkpoints.
virtual bool Init()
Initialize internal state from the database and block index.
virtual bool CommitInternal(CDBBatch &batch)
Virtual method called internally by Commit that can be overridden to atomically commit more index sta...
virtual bool Rewind(const CBlockIndex *current_tip, const CBlockIndex *new_tip)
Rewind index to an earlier chain tip during a chain reorg.
Complete block filter struct as defined in BIP 157.
const uint256 & GetBlockHash() const
const std::vector< unsigned char > & GetEncodedFilter() const
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 CommitInternal(CDBBatch &batch) override
Virtual method called internally by Commit that can be overridden to atomically commit more index sta...
bool Rewind(const CBlockIndex *current_tip, const CBlockIndex *new_tip) override
Rewind index to an earlier chain tip during a chain reorg.
bool ReadFilterFromDisk(const FlatFilePos &pos, BlockFilter &filter) const
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 LookupFilterHeader(const CBlockIndex *block_index, uint256 &header_out)
Get a single filter header by block.
BlockFilterType m_filter_type
bool Init() override
Initialize internal state from the database and block index.
bool WriteBlock(const CBlock &block, const CBlockIndex *pindex) override
Write update index entries for a newly connected block.
BlockFilterIndex(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 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.
const char * GetName() const override
Get the name of the index for display in logs.
size_t WriteFilterToDisk(FlatFilePos &pos, const BlockFilter &filter)
FlatFilePos m_next_filter_pos
Non-refcounted RAII wrapper for FILE*.
FILE * Get() const
Get wrapped FILE* without transfer of ownership.
bool IsNull() const
Return true if the wrapped FILE* is nullptr, false otherwise.
The block chain is a tree shaped structure starting with the genesis block at the root,...
CBlockIndex * pprev
pointer to the index of the predecessor of this block
uint256 GetBlockHash() const
CBlockIndex * GetAncestor(int height)
Efficiently find an ancestor of this block.
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)
CDBIterator * NewIterator()
bool Read(const K &key, V &value) const
std::string ToString() const
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
static void LogPrintf(const char *fmt, const Args &... args)
std::deque< CInv >::iterator it
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 Serialize(Stream &s, char a)
void Unserialize(Stream &s, char &a)
void ser_writedata8(Stream &s, uint8_t obj)
size_t GetSerializeSize(const T &t, int nVersion=0)
uint32_t ser_readdata32be(Stream &s)
const fs::path & GetDataDir(bool fNetSpecific)
bool TruncateFile(FILE *file, unsigned int length)
bool FileCommit(FILE *file)
bool error(const char *fmt, const Args &... args)
bool UndoReadFromDisk(CBlockUndo &blockundo, const CBlockIndex *pindex)