mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
merge bitcoin#24832: Verify the block filter hash when reading the filter from disk
This commit is contained in:
parent
e507a51323
commit
3bd584c845
@ -5,38 +5,84 @@
|
|||||||
#include <bench/bench.h>
|
#include <bench/bench.h>
|
||||||
#include <blockfilter.h>
|
#include <blockfilter.h>
|
||||||
|
|
||||||
static void ConstructGCSFilter(benchmark::Bench& bench)
|
static const GCSFilter::ElementSet GenerateGCSTestElements()
|
||||||
{
|
{
|
||||||
GCSFilter::ElementSet elements;
|
GCSFilter::ElementSet elements;
|
||||||
for (int i = 0; i < 10000; ++i) {
|
|
||||||
|
// Testing the benchmarks with different number of elements show that a filter
|
||||||
|
// with at least 100,000 elements results in benchmarks that have the same
|
||||||
|
// ns/op. This makes it easy to reason about how long (in nanoseconds) a single
|
||||||
|
// filter element takes to process.
|
||||||
|
for (int i = 0; i < 100000; ++i) {
|
||||||
GCSFilter::Element element(32);
|
GCSFilter::Element element(32);
|
||||||
element[0] = static_cast<unsigned char>(i);
|
element[0] = static_cast<unsigned char>(i);
|
||||||
element[1] = static_cast<unsigned char>(i >> 8);
|
element[1] = static_cast<unsigned char>(i >> 8);
|
||||||
elements.insert(std::move(element));
|
elements.insert(std::move(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GCSBlockFilterGetHash(benchmark::Bench& bench)
|
||||||
|
{
|
||||||
|
auto elements = GenerateGCSTestElements();
|
||||||
|
|
||||||
|
GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements);
|
||||||
|
BlockFilter block_filter(BlockFilterType::BASIC_FILTER, {}, filter.GetEncoded(), /*skip_decode_check=*/false);
|
||||||
|
|
||||||
|
bench.run([&] {
|
||||||
|
block_filter.GetHash();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GCSFilterConstruct(benchmark::Bench& bench)
|
||||||
|
{
|
||||||
|
auto elements = GenerateGCSTestElements();
|
||||||
|
|
||||||
uint64_t siphash_k0 = 0;
|
uint64_t siphash_k0 = 0;
|
||||||
bench.batch(elements.size()).unit("elem").run([&] {
|
bench.run([&]{
|
||||||
GCSFilter filter({siphash_k0, 0, 20, 1 << 20}, elements);
|
GCSFilter filter({siphash_k0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements);
|
||||||
|
|
||||||
siphash_k0++;
|
siphash_k0++;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MatchGCSFilter(benchmark::Bench& bench)
|
static void GCSFilterDecode(benchmark::Bench& bench)
|
||||||
{
|
{
|
||||||
GCSFilter::ElementSet elements;
|
auto elements = GenerateGCSTestElements();
|
||||||
for (int i = 0; i < 10000; ++i) {
|
|
||||||
GCSFilter::Element element(32);
|
|
||||||
element[0] = static_cast<unsigned char>(i);
|
|
||||||
element[1] = static_cast<unsigned char>(i >> 8);
|
|
||||||
elements.insert(std::move(element));
|
|
||||||
}
|
|
||||||
GCSFilter filter({0, 0, 20, 1 << 20}, elements);
|
|
||||||
|
|
||||||
bench.unit("elem").run([&] {
|
GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements);
|
||||||
filter.Match(GCSFilter::Element());
|
auto encoded = filter.GetEncoded();
|
||||||
|
|
||||||
|
bench.run([&] {
|
||||||
|
GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, encoded, /*skip_decode_check=*/false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
BENCHMARK(ConstructGCSFilter);
|
static void GCSFilterDecodeSkipCheck(benchmark::Bench& bench)
|
||||||
BENCHMARK(MatchGCSFilter);
|
{
|
||||||
|
auto elements = GenerateGCSTestElements();
|
||||||
|
|
||||||
|
GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements);
|
||||||
|
auto encoded = filter.GetEncoded();
|
||||||
|
|
||||||
|
bench.run([&] {
|
||||||
|
GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, encoded, /*skip_decode_check=*/true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GCSFilterMatch(benchmark::Bench& bench)
|
||||||
|
{
|
||||||
|
auto elements = GenerateGCSTestElements();
|
||||||
|
|
||||||
|
GCSFilter filter({0, 0, BASIC_FILTER_P, BASIC_FILTER_M}, elements);
|
||||||
|
|
||||||
|
bench.run([&] {
|
||||||
|
filter.Match(GCSFilter::Element());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
BENCHMARK(GCSBlockFilterGetHash);
|
||||||
|
BENCHMARK(GCSFilterConstruct);
|
||||||
|
BENCHMARK(GCSFilterDecode);
|
||||||
|
BENCHMARK(GCSFilterDecodeSkipCheck);
|
||||||
|
BENCHMARK(GCSFilterMatch);
|
||||||
|
@ -47,7 +47,7 @@ GCSFilter::GCSFilter(const Params& params)
|
|||||||
: m_params(params), m_N(0), m_F(0), m_encoded{0}
|
: m_params(params), m_N(0), m_F(0), m_encoded{0}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter)
|
GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter, bool skip_decode_check)
|
||||||
: m_params(params), m_encoded(std::move(encoded_filter))
|
: m_params(params), m_encoded(std::move(encoded_filter))
|
||||||
{
|
{
|
||||||
SpanReader stream{GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0};
|
SpanReader stream{GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0};
|
||||||
@ -59,6 +59,8 @@ GCSFilter::GCSFilter(const Params& params, std::vector<unsigned char> encoded_fi
|
|||||||
}
|
}
|
||||||
m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M);
|
m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_params.m_M);
|
||||||
|
|
||||||
|
if (skip_decode_check) return;
|
||||||
|
|
||||||
// Verify that the encoded filter contains exactly N elements. If it has too much or too little
|
// Verify that the encoded filter contains exactly N elements. If it has too much or too little
|
||||||
// data, a std::ios_base::failure exception will be raised.
|
// data, a std::ios_base::failure exception will be raised.
|
||||||
BitStreamReader<SpanReader> bitreader{stream};
|
BitStreamReader<SpanReader> bitreader{stream};
|
||||||
@ -219,14 +221,14 @@ static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
|
|||||||
}
|
}
|
||||||
|
|
||||||
BlockFilter::BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
|
BlockFilter::BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
|
||||||
std::vector<unsigned char> filter)
|
std::vector<unsigned char> filter, bool skip_decode_check)
|
||||||
: m_filter_type(filter_type), m_block_hash(block_hash)
|
: m_filter_type(filter_type), m_block_hash(block_hash)
|
||||||
{
|
{
|
||||||
GCSFilter::Params params;
|
GCSFilter::Params params;
|
||||||
if (!BuildParams(params)) {
|
if (!BuildParams(params)) {
|
||||||
throw std::invalid_argument("unknown filter_type");
|
throw std::invalid_argument("unknown filter_type");
|
||||||
}
|
}
|
||||||
m_filter = GCSFilter(params, std::move(filter));
|
m_filter = GCSFilter(params, std::move(filter), skip_decode_check);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo)
|
BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo)
|
||||||
|
@ -60,7 +60,7 @@ public:
|
|||||||
explicit GCSFilter(const Params& params = Params());
|
explicit GCSFilter(const Params& params = Params());
|
||||||
|
|
||||||
/** Reconstructs an already-created filter from an encoding. */
|
/** Reconstructs an already-created filter from an encoding. */
|
||||||
GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter);
|
GCSFilter(const Params& params, std::vector<unsigned char> encoded_filter, bool skip_decode_check);
|
||||||
|
|
||||||
/** Builds a new filter from the params and set of elements. */
|
/** Builds a new filter from the params and set of elements. */
|
||||||
GCSFilter(const Params& params, const ElementSet& elements);
|
GCSFilter(const Params& params, const ElementSet& elements);
|
||||||
@ -123,7 +123,7 @@ public:
|
|||||||
|
|
||||||
//! Reconstruct a BlockFilter from parts.
|
//! Reconstruct a BlockFilter from parts.
|
||||||
BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
|
BlockFilter(BlockFilterType filter_type, const uint256& block_hash,
|
||||||
std::vector<unsigned char> filter);
|
std::vector<unsigned char> filter, bool skip_decode_check);
|
||||||
|
|
||||||
//! Construct a new BlockFilter of the specified type from a block.
|
//! Construct a new BlockFilter of the specified type from a block.
|
||||||
BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo);
|
BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo);
|
||||||
@ -165,7 +165,7 @@ public:
|
|||||||
if (!BuildParams(params)) {
|
if (!BuildParams(params)) {
|
||||||
throw std::ios_base::failure("unknown filter_type");
|
throw std::ios_base::failure("unknown filter_type");
|
||||||
}
|
}
|
||||||
m_filter = GCSFilter(params, std::move(encoded_filter));
|
m_filter = GCSFilter(params, std::move(encoded_filter), /*skip_decode_check=*/false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <dbwrapper.h>
|
#include <dbwrapper.h>
|
||||||
|
#include <hash.h>
|
||||||
#include <index/blockfilterindex.h>
|
#include <index/blockfilterindex.h>
|
||||||
#include <node/blockstorage.h>
|
#include <node/blockstorage.h>
|
||||||
#include <serialize.h>
|
#include <serialize.h>
|
||||||
@ -146,18 +147,22 @@ bool BlockFilterIndex::CommitInternal(CDBBatch& batch)
|
|||||||
return BaseIndex::CommitInternal(batch);
|
return BaseIndex::CommitInternal(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockFilterIndex::ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const
|
bool BlockFilterIndex::ReadFilterFromDisk(const FlatFilePos& pos, const uint256& hash, BlockFilter& filter) const
|
||||||
{
|
{
|
||||||
CAutoFile filein(m_filter_fileseq->Open(pos, true), SER_DISK, CLIENT_VERSION);
|
CAutoFile filein(m_filter_fileseq->Open(pos, true), SER_DISK, CLIENT_VERSION);
|
||||||
if (filein.IsNull()) {
|
if (filein.IsNull()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that the hash of the encoded_filter matches the one stored in the db.
|
||||||
uint256 block_hash;
|
uint256 block_hash;
|
||||||
std::vector<uint8_t> encoded_filter;
|
std::vector<uint8_t> encoded_filter;
|
||||||
try {
|
try {
|
||||||
filein >> block_hash >> encoded_filter;
|
filein >> block_hash >> encoded_filter;
|
||||||
filter = BlockFilter(GetFilterType(), block_hash, std::move(encoded_filter));
|
uint256 result;
|
||||||
|
CHash256().Write(encoded_filter).Finalize(result);
|
||||||
|
if (result != hash) return error("Checksum mismatch in filter decode.");
|
||||||
|
filter = BlockFilter(GetFilterType(), block_hash, std::move(encoded_filter), /*skip_decode_check=*/true);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
return error("%s: Failed to deserialize block filter from disk: %s", __func__, e.what());
|
return error("%s: Failed to deserialize block filter from disk: %s", __func__, e.what());
|
||||||
@ -384,7 +389,7 @@ bool BlockFilterIndex::LookupFilter(const CBlockIndex* block_index, BlockFilter&
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ReadFilterFromDisk(entry.pos, filter_out);
|
return ReadFilterFromDisk(entry.pos, entry.hash, filter_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out)
|
bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out)
|
||||||
@ -428,7 +433,7 @@ bool BlockFilterIndex::LookupFilterRange(int start_height, const CBlockIndex* st
|
|||||||
filters_out.resize(entries.size());
|
filters_out.resize(entries.size());
|
||||||
auto filter_pos_it = filters_out.begin();
|
auto filter_pos_it = filters_out.begin();
|
||||||
for (const auto& entry : entries) {
|
for (const auto& entry : entries) {
|
||||||
if (!ReadFilterFromDisk(entry.pos, *filter_pos_it)) {
|
if (!ReadFilterFromDisk(entry.pos, entry.hash, *filter_pos_it)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
++filter_pos_it;
|
++filter_pos_it;
|
||||||
|
@ -32,7 +32,7 @@ private:
|
|||||||
FlatFilePos m_next_filter_pos;
|
FlatFilePos m_next_filter_pos;
|
||||||
std::unique_ptr<FlatFileSeq> m_filter_fileseq;
|
std::unique_ptr<FlatFileSeq> m_filter_fileseq;
|
||||||
|
|
||||||
bool ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const;
|
bool ReadFilterFromDisk(const FlatFilePos& pos, const uint256& hash, BlockFilter& filter) const;
|
||||||
size_t WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter);
|
size_t WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter);
|
||||||
|
|
||||||
Mutex m_cs_headers_cache;
|
Mutex m_cs_headers_cache;
|
||||||
|
Loading…
Reference in New Issue
Block a user