mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-20 07:09:15 +01:00
index: deduplicate Hash / Height handling
The code was largely duplicated between coinstatsindex and blockfilterindex. Deduplicate it by moving it to a shared file. slight change in behavior: the index name is no longer part of the error msg in case of (un)serialization errors.
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#include <flatfile.h>
|
||||
#include <hash.h>
|
||||
#include <index/base.h>
|
||||
#include <index/db_key.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <interfaces/types.h>
|
||||
#include <logging.h>
|
||||
@@ -25,7 +26,6 @@
|
||||
|
||||
#include <cerrno>
|
||||
#include <exception>
|
||||
#include <ios>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
@@ -46,12 +46,8 @@
|
||||
* disk location of the next block filter to be written (represented as a FlatFilePos) is stored
|
||||
* under the DB_FILTER_POS key.
|
||||
*
|
||||
* Keys for the height index have the type [DB_BLOCK_HEIGHT, uint32 (BE)]. The height is represented
|
||||
* as big-endian so that sequential reads of filters by height are fast.
|
||||
* Keys for the hash index have the type [DB_BLOCK_HASH, uint256].
|
||||
* The logic for keys is shared with other indexes, see index/db_key.h.
|
||||
*/
|
||||
constexpr uint8_t DB_BLOCK_HASH{'s'};
|
||||
constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
|
||||
constexpr uint8_t DB_FILTER_POS{'P'};
|
||||
|
||||
constexpr unsigned int MAX_FLTR_FILE_SIZE = 0x1000000; // 16 MiB
|
||||
@@ -74,45 +70,6 @@ struct DBVal {
|
||||
SERIALIZE_METHODS(DBVal, obj) { READWRITE(obj.hash, obj.header, obj.pos); }
|
||||
};
|
||||
|
||||
struct DBHeightKey {
|
||||
int height;
|
||||
|
||||
explicit DBHeightKey(int height_in) : height(height_in) {}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s) const
|
||||
{
|
||||
ser_writedata8(s, DB_BLOCK_HEIGHT);
|
||||
ser_writedata32be(s, height);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s)
|
||||
{
|
||||
const uint8_t prefix{ser_readdata8(s)};
|
||||
if (prefix != DB_BLOCK_HEIGHT) {
|
||||
throw std::ios_base::failure("Invalid format for block filter index DB height key");
|
||||
}
|
||||
height = ser_readdata32be(s);
|
||||
}
|
||||
};
|
||||
|
||||
struct DBHashKey {
|
||||
uint256 hash;
|
||||
|
||||
explicit DBHashKey(const uint256& hash_in) : hash(hash_in) {}
|
||||
|
||||
SERIALIZE_METHODS(DBHashKey, obj) {
|
||||
uint8_t prefix{DB_BLOCK_HASH};
|
||||
READWRITE(prefix);
|
||||
if (prefix != DB_BLOCK_HASH) {
|
||||
throw std::ios_base::failure("Invalid format for block filter index DB hash key");
|
||||
}
|
||||
|
||||
READWRITE(obj.hash);
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace
|
||||
|
||||
static std::map<BlockFilterType, BlockFilterIndex> g_filter_indexes;
|
||||
@@ -317,29 +274,6 @@ bool BlockFilterIndex::Write(const BlockFilter& filter, uint32_t block_height, c
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
|
||||
const std::string& index_name, int height)
|
||||
{
|
||||
DBHeightKey key(height);
|
||||
db_it.Seek(key);
|
||||
|
||||
if (!db_it.GetKey(key) || key.height != height) {
|
||||
LogError("unexpected key in %s: expected (%c, %d)",
|
||||
index_name, DB_BLOCK_HEIGHT, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<uint256, DBVal> value;
|
||||
if (!db_it.GetValue(value)) {
|
||||
LogError("unable to read value in %s at key (%c, %d)",
|
||||
index_name, DB_BLOCK_HEIGHT, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
batch.Write(DBHashKey(value.first), std::move(value.second));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockFilterIndex::CustomRemove(const interfaces::BlockInfo& block)
|
||||
{
|
||||
CDBBatch batch(*m_db);
|
||||
@@ -348,7 +282,7 @@ bool BlockFilterIndex::CustomRemove(const interfaces::BlockInfo& block)
|
||||
// During a reorg, we need to copy block filter that is getting disconnected from the
|
||||
// height index to the hash index so we can still find it when the height index entry
|
||||
// is overwritten.
|
||||
if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, block.height)) {
|
||||
if (!CopyHeightIndexToHashIndex<DBVal>(*db_it, batch, m_name, block.height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <crypto/muhash.h>
|
||||
#include <dbwrapper.h>
|
||||
#include <index/base.h>
|
||||
#include <index/db_key.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <interfaces/types.h>
|
||||
#include <kernel/coinstats.h>
|
||||
@@ -28,7 +29,6 @@
|
||||
#include <validation.h>
|
||||
|
||||
#include <compare>
|
||||
#include <ios>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
#include <string>
|
||||
@@ -40,8 +40,6 @@ using kernel::CCoinsStats;
|
||||
using kernel::GetBogoSize;
|
||||
using kernel::RemoveCoinHash;
|
||||
|
||||
static constexpr uint8_t DB_BLOCK_HASH{'s'};
|
||||
static constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
|
||||
static constexpr uint8_t DB_MUHASH{'M'};
|
||||
|
||||
namespace {
|
||||
@@ -85,47 +83,6 @@ struct DBVal {
|
||||
SER_READ(obj, obj.total_coinbase_amount = UintToArith256(coinbase));
|
||||
}
|
||||
};
|
||||
|
||||
struct DBHeightKey {
|
||||
int height;
|
||||
|
||||
explicit DBHeightKey(int height_in) : height(height_in) {}
|
||||
|
||||
template <typename Stream>
|
||||
void Serialize(Stream& s) const
|
||||
{
|
||||
ser_writedata8(s, DB_BLOCK_HEIGHT);
|
||||
ser_writedata32be(s, height);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
void Unserialize(Stream& s)
|
||||
{
|
||||
const uint8_t prefix{ser_readdata8(s)};
|
||||
if (prefix != DB_BLOCK_HEIGHT) {
|
||||
throw std::ios_base::failure("Invalid format for coinstatsindex DB height key");
|
||||
}
|
||||
height = ser_readdata32be(s);
|
||||
}
|
||||
};
|
||||
|
||||
struct DBHashKey {
|
||||
uint256 block_hash;
|
||||
|
||||
explicit DBHashKey(const uint256& hash_in) : block_hash(hash_in) {}
|
||||
|
||||
SERIALIZE_METHODS(DBHashKey, obj)
|
||||
{
|
||||
uint8_t prefix{DB_BLOCK_HASH};
|
||||
READWRITE(prefix);
|
||||
if (prefix != DB_BLOCK_HASH) {
|
||||
throw std::ios_base::failure("Invalid format for coinstatsindex DB hash key");
|
||||
}
|
||||
|
||||
READWRITE(obj.block_hash);
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace
|
||||
|
||||
std::unique_ptr<CoinStatsIndex> g_coin_stats_index;
|
||||
@@ -257,29 +214,6 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
|
||||
const std::string& index_name, int height)
|
||||
{
|
||||
DBHeightKey key{height};
|
||||
db_it.Seek(key);
|
||||
|
||||
if (!db_it.GetKey(key) || key.height != height) {
|
||||
LogError("unexpected key in %s: expected (%c, %d)",
|
||||
index_name, DB_BLOCK_HEIGHT, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<uint256, DBVal> value;
|
||||
if (!db_it.GetValue(value)) {
|
||||
LogError("unable to read value in %s at key (%c, %d)",
|
||||
index_name, DB_BLOCK_HEIGHT, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
batch.Write(DBHashKey(value.first), value.second);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoinStatsIndex::CustomRemove(const interfaces::BlockInfo& block)
|
||||
{
|
||||
CDBBatch batch(*m_db);
|
||||
@@ -287,7 +221,7 @@ bool CoinStatsIndex::CustomRemove(const interfaces::BlockInfo& block)
|
||||
|
||||
// During a reorg, copy the block's hash digest from the height index to the hash index,
|
||||
// ensuring it's still accessible after the height index entry is overwritten.
|
||||
if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, block.height)) {
|
||||
if (!CopyHeightIndexToHashIndex<DBVal>(*db_it, batch, m_name, block.height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
94
src/index/db_key.h
Normal file
94
src/index/db_key.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2025-present The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_INDEX_DB_KEY_H
|
||||
#define BITCOIN_INDEX_DB_KEY_H
|
||||
|
||||
#include <dbwrapper.h>
|
||||
#include <interfaces/types.h>
|
||||
#include <logging.h>
|
||||
#include <serialize.h>
|
||||
#include <uint256.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <ios>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
/*
|
||||
* This file includes the logic for the db keys used by blockfilterindex and coinstatsindex.
|
||||
* Index data is usually indexed by height, but in case of a reorg, entries of blocks no
|
||||
* longer in the main chain will be copied to a hash index by which they can still be queried.
|
||||
* Keys for the height index have the type [DB_BLOCK_HEIGHT, uint32 (BE)]. The height is represented
|
||||
* as big-endian so that sequential reads of filters by height are fast.
|
||||
* Keys for the hash index have the type [DB_BLOCK_HASH, uint256].
|
||||
*/
|
||||
|
||||
static constexpr uint8_t DB_BLOCK_HASH{'s'};
|
||||
static constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
|
||||
|
||||
struct DBHeightKey {
|
||||
int height;
|
||||
|
||||
explicit DBHeightKey(int height_in) : height(height_in) {}
|
||||
|
||||
template<typename Stream>
|
||||
void Serialize(Stream& s) const
|
||||
{
|
||||
ser_writedata8(s, DB_BLOCK_HEIGHT);
|
||||
ser_writedata32be(s, height);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s)
|
||||
{
|
||||
const uint8_t prefix{ser_readdata8(s)};
|
||||
if (prefix != DB_BLOCK_HEIGHT) {
|
||||
throw std::ios_base::failure("Invalid format for index DB height key");
|
||||
}
|
||||
height = ser_readdata32be(s);
|
||||
}
|
||||
};
|
||||
|
||||
struct DBHashKey {
|
||||
uint256 hash;
|
||||
|
||||
explicit DBHashKey(const uint256& hash_in) : hash(hash_in) {}
|
||||
|
||||
SERIALIZE_METHODS(DBHashKey, obj) {
|
||||
uint8_t prefix{DB_BLOCK_HASH};
|
||||
READWRITE(prefix);
|
||||
if (prefix != DB_BLOCK_HASH) {
|
||||
throw std::ios_base::failure("Invalid format for index DB hash key");
|
||||
}
|
||||
|
||||
READWRITE(obj.hash);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DBVal>
|
||||
[[nodiscard]] static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
|
||||
const std::string& index_name, int height)
|
||||
{
|
||||
DBHeightKey key(height);
|
||||
db_it.Seek(key);
|
||||
|
||||
if (!db_it.GetKey(key) || key.height != height) {
|
||||
LogError("unexpected key in %s: expected (%c, %d)",
|
||||
index_name, DB_BLOCK_HEIGHT, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<uint256, DBVal> value;
|
||||
if (!db_it.GetValue(value)) {
|
||||
LogError("unable to read value in %s at key (%c, %d)",
|
||||
index_name, DB_BLOCK_HEIGHT, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
batch.Write(DBHashKey(value.first), value.second);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // BITCOIN_INDEX_DB_KEY_H
|
||||
Reference in New Issue
Block a user