mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 03:47:29 -03:00
Compare commits
11 commits
99b37e65c9
...
32821f3606
Author | SHA1 | Date | |
---|---|---|---|
|
32821f3606 | ||
|
66aa6a47bd | ||
|
7c123c08dd | ||
|
1d99994da8 | ||
|
d3d3955c99 | ||
|
ed8ba94b86 | ||
|
c509c62db0 | ||
|
85ef8558b0 | ||
|
e76130d635 | ||
|
f259d14cca | ||
|
a20191c567 |
13 changed files with 338 additions and 160 deletions
|
@ -44,7 +44,7 @@ add_executable(bench_bitcoin
|
||||||
pool.cpp
|
pool.cpp
|
||||||
prevector.cpp
|
prevector.cpp
|
||||||
random.cpp
|
random.cpp
|
||||||
readblock.cpp
|
readwriteblock.cpp
|
||||||
rollingbloom.cpp
|
rollingbloom.cpp
|
||||||
rpc_blockchain.cpp
|
rpc_blockchain.cpp
|
||||||
rpc_mempool.cpp
|
rpc_mempool.cpp
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
// Copyright (c) 2023 The Bitcoin Core developers
|
|
||||||
// Distributed under the MIT software license, see the accompanying
|
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
||||||
|
|
||||||
#include <bench/bench.h>
|
|
||||||
#include <bench/data/block413567.raw.h>
|
|
||||||
#include <flatfile.h>
|
|
||||||
#include <node/blockstorage.h>
|
|
||||||
#include <primitives/block.h>
|
|
||||||
#include <primitives/transaction.h>
|
|
||||||
#include <serialize.h>
|
|
||||||
#include <span.h>
|
|
||||||
#include <streams.h>
|
|
||||||
#include <test/util/setup_common.h>
|
|
||||||
#include <validation.h>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
static FlatFilePos WriteBlockToDisk(ChainstateManager& chainman)
|
|
||||||
{
|
|
||||||
DataStream stream{benchmark::data::block413567};
|
|
||||||
CBlock block;
|
|
||||||
stream >> TX_WITH_WITNESS(block);
|
|
||||||
|
|
||||||
return chainman.m_blockman.SaveBlockToDisk(block, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ReadBlockFromDiskTest(benchmark::Bench& bench)
|
|
||||||
{
|
|
||||||
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
|
|
||||||
ChainstateManager& chainman{*testing_setup->m_node.chainman};
|
|
||||||
|
|
||||||
CBlock block;
|
|
||||||
const auto pos{WriteBlockToDisk(chainman)};
|
|
||||||
|
|
||||||
bench.run([&] {
|
|
||||||
const auto success{chainman.m_blockman.ReadBlockFromDisk(block, pos)};
|
|
||||||
assert(success);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ReadRawBlockFromDiskTest(benchmark::Bench& bench)
|
|
||||||
{
|
|
||||||
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
|
|
||||||
ChainstateManager& chainman{*testing_setup->m_node.chainman};
|
|
||||||
|
|
||||||
std::vector<uint8_t> block_data;
|
|
||||||
const auto pos{WriteBlockToDisk(chainman)};
|
|
||||||
|
|
||||||
bench.run([&] {
|
|
||||||
const auto success{chainman.m_blockman.ReadRawBlockFromDisk(block_data, pos)};
|
|
||||||
assert(success);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
BENCHMARK(ReadBlockFromDiskTest, benchmark::PriorityLevel::HIGH);
|
|
||||||
BENCHMARK(ReadRawBlockFromDiskTest, benchmark::PriorityLevel::HIGH);
|
|
79
src/bench/readwriteblock.cpp
Normal file
79
src/bench/readwriteblock.cpp
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright (c) 2023 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <bench/bench.h>
|
||||||
|
#include <bench/data/block413567.raw.h>
|
||||||
|
#include <flatfile.h>
|
||||||
|
#include <node/blockstorage.h>
|
||||||
|
#include <primitives/block.h>
|
||||||
|
#include <primitives/transaction.h>
|
||||||
|
#include <serialize.h>
|
||||||
|
#include <span.h>
|
||||||
|
#include <streams.h>
|
||||||
|
#include <test/util/setup_common.h>
|
||||||
|
#include <validation.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
CBlock CreateTestBlock()
|
||||||
|
{
|
||||||
|
DataStream stream{benchmark::data::block413567};
|
||||||
|
CBlock block;
|
||||||
|
stream >> TX_WITH_WITNESS(block);
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetSerializeSizeBench(benchmark::Bench& bench)
|
||||||
|
{
|
||||||
|
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
|
||||||
|
const CBlock block{CreateTestBlock()};
|
||||||
|
bench.run([&] {
|
||||||
|
const uint32_t block_size{static_cast<uint32_t>(GetSerializeSize(TX_WITH_WITNESS(block)))};
|
||||||
|
assert(block_size == benchmark::data::block413567.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SaveBlockToDiskBench(benchmark::Bench& bench)
|
||||||
|
{
|
||||||
|
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
|
||||||
|
auto& blockman{testing_setup->m_node.chainman->m_blockman};
|
||||||
|
const CBlock block{CreateTestBlock()};
|
||||||
|
bench.run([&] {
|
||||||
|
const auto pos{blockman.SaveBlock(block, 413'567)};
|
||||||
|
assert(!pos.IsNull());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReadBlockFromDiskBench(benchmark::Bench& bench)
|
||||||
|
{
|
||||||
|
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
|
||||||
|
auto& blockman{testing_setup->m_node.chainman->m_blockman};
|
||||||
|
const auto pos{blockman.SaveBlock(CreateTestBlock(), 413'567)};
|
||||||
|
CBlock block;
|
||||||
|
bench.run([&] {
|
||||||
|
const auto success{blockman.ReadBlockFromDisk(block, pos)};
|
||||||
|
assert(success);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReadRawBlockFromDiskBench(benchmark::Bench& bench)
|
||||||
|
{
|
||||||
|
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
|
||||||
|
auto& blockman{testing_setup->m_node.chainman->m_blockman};
|
||||||
|
const auto pos{blockman.SaveBlock(CreateTestBlock(), 413'567)};
|
||||||
|
std::vector<uint8_t> block_data;
|
||||||
|
blockman.ReadRawBlockFromDisk(block_data, pos); // warmup
|
||||||
|
bench.run([&] {
|
||||||
|
const auto success{blockman.ReadRawBlockFromDisk(block_data, pos)};
|
||||||
|
assert(success);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(GetSerializeSizeBench, benchmark::PriorityLevel::HIGH);
|
||||||
|
BENCHMARK(SaveBlockToDiskBench, benchmark::PriorityLevel::HIGH);
|
||||||
|
BENCHMARK(ReadBlockFromDiskBench, benchmark::PriorityLevel::HIGH);
|
||||||
|
BENCHMARK(ReadRawBlockFromDiskBench, benchmark::PriorityLevel::HIGH);
|
|
@ -669,39 +669,12 @@ CBlockFileInfo* BlockManager::GetBlockFileInfo(size_t n)
|
||||||
return &m_blockfile_info.at(n);
|
return &m_blockfile_info.at(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockManager::UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock) const
|
|
||||||
{
|
|
||||||
// Open history file to append
|
|
||||||
AutoFile fileout{OpenUndoFile(pos)};
|
|
||||||
if (fileout.IsNull()) {
|
|
||||||
LogError("%s: OpenUndoFile failed\n", __func__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write index header
|
|
||||||
unsigned int nSize = GetSerializeSize(blockundo);
|
|
||||||
fileout << GetParams().MessageStart() << nSize;
|
|
||||||
|
|
||||||
// Write undo data
|
|
||||||
long fileOutPos = fileout.tell();
|
|
||||||
pos.nPos = (unsigned int)fileOutPos;
|
|
||||||
fileout << blockundo;
|
|
||||||
|
|
||||||
// calculate & write checksum
|
|
||||||
HashWriter hasher{};
|
|
||||||
hasher << hashBlock;
|
|
||||||
hasher << blockundo;
|
|
||||||
fileout << hasher.GetHash();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BlockManager::UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex& index) const
|
bool BlockManager::UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex& index) const
|
||||||
{
|
{
|
||||||
const FlatFilePos pos{WITH_LOCK(::cs_main, return index.GetUndoPos())};
|
const FlatFilePos pos{WITH_LOCK(::cs_main, return index.GetUndoPos())};
|
||||||
|
|
||||||
// Open history file to read
|
// Open history file to read
|
||||||
AutoFile filein{OpenUndoFile(pos, true)};
|
BufferedReadOnlyFile filein{m_undo_file_seq, pos, m_xor_key};
|
||||||
if (filein.IsNull()) {
|
if (filein.IsNull()) {
|
||||||
LogError("%s: OpenUndoFile failed for %s\n", __func__, pos.ToString());
|
LogError("%s: OpenUndoFile failed for %s\n", __func__, pos.ToString());
|
||||||
return false;
|
return false;
|
||||||
|
@ -963,62 +936,55 @@ bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFileP
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockManager::WriteBlockToDisk(const CBlock& block, FlatFilePos& pos) const
|
bool BlockManager::SaveBlockUndo(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
|
||||||
{
|
|
||||||
// Open history file to append
|
|
||||||
AutoFile fileout{OpenBlockFile(pos)};
|
|
||||||
if (fileout.IsNull()) {
|
|
||||||
LogError("%s: OpenBlockFile failed\n", __func__);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write index header
|
|
||||||
unsigned int nSize = GetSerializeSize(TX_WITH_WITNESS(block));
|
|
||||||
fileout << GetParams().MessageStart() << nSize;
|
|
||||||
|
|
||||||
// Write block
|
|
||||||
long fileOutPos = fileout.tell();
|
|
||||||
pos.nPos = (unsigned int)fileOutPos;
|
|
||||||
fileout << TX_WITH_WITNESS(block);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
|
|
||||||
{
|
{
|
||||||
AssertLockHeld(::cs_main);
|
AssertLockHeld(::cs_main);
|
||||||
const BlockfileType type = BlockfileTypeForHeight(block.nHeight);
|
const BlockfileType type = BlockfileTypeForHeight(block.nHeight);
|
||||||
auto& cursor = *Assert(WITH_LOCK(cs_LastBlockFile, return m_blockfile_cursors[type]));
|
auto& cursor = *Assert(WITH_LOCK(cs_LastBlockFile, return m_blockfile_cursors[type]));
|
||||||
|
|
||||||
// Write undo information to disk
|
|
||||||
if (block.GetUndoPos().IsNull()) {
|
if (block.GetUndoPos().IsNull()) {
|
||||||
FlatFilePos _pos;
|
FlatFilePos pos;
|
||||||
if (!FindUndoPos(state, block.nFile, _pos, ::GetSerializeSize(blockundo) + 40)) {
|
const uint32_t blockundo_size{static_cast<uint32_t>(GetSerializeSize(blockundo))};
|
||||||
|
if (!FindUndoPos(state, block.nFile, pos, UNDO_DATA_DISK_OVERHEAD + blockundo_size)) {
|
||||||
LogError("%s: FindUndoPos failed\n", __func__);
|
LogError("%s: FindUndoPos failed\n", __func__);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!UndoWriteToDisk(blockundo, _pos, block.pprev->GetBlockHash())) {
|
BufferedWriteOnlyFile fileout{m_undo_file_seq, pos, m_xor_key};
|
||||||
|
if (fileout.IsNull()) {
|
||||||
|
LogError("%s: OpenUndoFile failed\n", __func__);
|
||||||
return FatalError(m_opts.notifications, state, _("Failed to write undo data."));
|
return FatalError(m_opts.notifications, state, _("Failed to write undo data."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write index header
|
||||||
|
fileout << GetParams().MessageStart() << blockundo_size;
|
||||||
|
pos.nPos += BLOCK_SERIALIZATION_HEADER_SIZE;
|
||||||
|
fileout << blockundo;
|
||||||
|
|
||||||
|
// calculate & write checksum
|
||||||
|
HashWriter hasher{};
|
||||||
|
hasher << block.pprev->GetBlockHash();
|
||||||
|
hasher << blockundo;
|
||||||
|
fileout << hasher.GetHash();
|
||||||
|
|
||||||
// rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
|
// rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
|
||||||
// we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
|
// we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
|
||||||
// in the block file info as below; note that this does not catch the case where the undo writes are keeping up
|
// in the block file info as below; note that this does not catch the case where the undo writes are keeping up
|
||||||
// with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
|
// with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
|
||||||
// the FindNextBlockPos function
|
// the FindNextBlockPos function
|
||||||
if (_pos.nFile < cursor.file_num && static_cast<uint32_t>(block.nHeight) == m_blockfile_info[_pos.nFile].nHeightLast) {
|
if (pos.nFile < cursor.file_num && static_cast<uint32_t>(block.nHeight) == m_blockfile_info[pos.nFile].nHeightLast) {
|
||||||
// Do not propagate the return code, a failed flush here should not
|
// Do not propagate the return code, a failed flush here should not
|
||||||
// be an indication for a failed write. If it were propagated here,
|
// be an indication for a failed write. If it were propagated here,
|
||||||
// the caller would assume the undo data not to be written, when in
|
// the caller would assume the undo data not to be written, when in
|
||||||
// fact it is. Note though, that a failed flush might leave the data
|
// fact it is. Note though, that a failed flush might leave the data
|
||||||
// file untrimmed.
|
// file untrimmed.
|
||||||
if (!FlushUndoFile(_pos.nFile, true)) {
|
if (!FlushUndoFile(pos.nFile, true)) {
|
||||||
LogPrintLevel(BCLog::BLOCKSTORAGE, BCLog::Level::Warning, "Failed to flush undo file %05i\n", _pos.nFile);
|
LogPrintLevel(BCLog::BLOCKSTORAGE, BCLog::Level::Warning, "Failed to flush undo file %05i\n", pos.nFile);
|
||||||
}
|
}
|
||||||
} else if (_pos.nFile == cursor.file_num && block.nHeight > cursor.undo_height) {
|
} else if (pos.nFile == cursor.file_num && block.nHeight > cursor.undo_height) {
|
||||||
cursor.undo_height = block.nHeight;
|
cursor.undo_height = block.nHeight;
|
||||||
}
|
}
|
||||||
// update nUndoPos in block index
|
// update nUndoPos in block index
|
||||||
block.nUndoPos = _pos.nPos;
|
block.nUndoPos = pos.nPos;
|
||||||
block.nStatus |= BLOCK_HAVE_UNDO;
|
block.nStatus |= BLOCK_HAVE_UNDO;
|
||||||
m_dirty_blockindex.insert(&block);
|
m_dirty_blockindex.insert(&block);
|
||||||
}
|
}
|
||||||
|
@ -1031,7 +997,7 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) cons
|
||||||
block.SetNull();
|
block.SetNull();
|
||||||
|
|
||||||
// Open history file to read
|
// Open history file to read
|
||||||
AutoFile filein{OpenBlockFile(pos, true)};
|
BufferedReadOnlyFile filein{m_block_file_seq, pos, m_xor_key};
|
||||||
if (filein.IsNull()) {
|
if (filein.IsNull()) {
|
||||||
LogError("%s: OpenBlockFile failed for %s\n", __func__, pos.ToString());
|
LogError("%s: OpenBlockFile failed for %s\n", __func__, pos.ToString());
|
||||||
return false;
|
return false;
|
||||||
|
@ -1119,22 +1085,25 @@ bool BlockManager::ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatF
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight)
|
FlatFilePos BlockManager::SaveBlock(const CBlock& block, int nHeight)
|
||||||
{
|
{
|
||||||
unsigned int nBlockSize = ::GetSerializeSize(TX_WITH_WITNESS(block));
|
const uint32_t block_size{static_cast<uint32_t>(GetSerializeSize(TX_WITH_WITNESS(block)))};
|
||||||
// Account for the 4 magic message start bytes + the 4 length bytes (8 bytes total,
|
FlatFilePos pos{FindNextBlockPos(BLOCK_SERIALIZATION_HEADER_SIZE + block_size, nHeight, block.GetBlockTime())};
|
||||||
// defined as BLOCK_SERIALIZATION_HEADER_SIZE)
|
if (pos.IsNull()) {
|
||||||
nBlockSize += static_cast<unsigned int>(BLOCK_SERIALIZATION_HEADER_SIZE);
|
|
||||||
FlatFilePos blockPos{FindNextBlockPos(nBlockSize, nHeight, block.GetBlockTime())};
|
|
||||||
if (blockPos.IsNull()) {
|
|
||||||
LogError("%s: FindNextBlockPos failed\n", __func__);
|
LogError("%s: FindNextBlockPos failed\n", __func__);
|
||||||
return FlatFilePos();
|
return FlatFilePos();
|
||||||
}
|
}
|
||||||
if (!WriteBlockToDisk(block, blockPos)) {
|
BufferedWriteOnlyFile fileout{m_block_file_seq, pos, m_xor_key};
|
||||||
|
if (fileout.IsNull()) {
|
||||||
|
LogError("%s: OpenBlockFile failed\n", __func__);
|
||||||
m_opts.notifications.fatalError(_("Failed to write block."));
|
m_opts.notifications.fatalError(_("Failed to write block."));
|
||||||
return FlatFilePos();
|
return FlatFilePos();
|
||||||
}
|
}
|
||||||
return blockPos;
|
|
||||||
|
fileout << GetParams().MessageStart() << block_size;
|
||||||
|
pos.nPos += BLOCK_SERIALIZATION_HEADER_SIZE;
|
||||||
|
fileout << TX_WITH_WITNESS(block);
|
||||||
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto InitBlocksdirXorKey(const BlockManager::Options& opts)
|
static auto InitBlocksdirXorKey(const BlockManager::Options& opts)
|
||||||
|
|
|
@ -74,8 +74,11 @@ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
|
||||||
/** The maximum size of a blk?????.dat file (since 0.8) */
|
/** The maximum size of a blk?????.dat file (since 0.8) */
|
||||||
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
|
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
|
||||||
|
|
||||||
/** Size of header written by WriteBlockToDisk before a serialized CBlock */
|
/** Size of header written by SaveBlock before a serialized CBlock (8 bytes) */
|
||||||
static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE = std::tuple_size_v<MessageStartChars> + sizeof(unsigned int);
|
static constexpr uint32_t BLOCK_SERIALIZATION_HEADER_SIZE = std::tuple_size_v<MessageStartChars> + sizeof(uint32_t);
|
||||||
|
|
||||||
|
/** Total overhead when writing undo data: header (8 bytes) plus checksum (32 bytes) */
|
||||||
|
static constexpr uint32_t UNDO_DATA_DISK_OVERHEAD = BLOCK_SERIALIZATION_HEADER_SIZE + uint256::size();
|
||||||
|
|
||||||
// Because validation code takes pointers to the map's CBlockIndex objects, if
|
// Because validation code takes pointers to the map's CBlockIndex objects, if
|
||||||
// we ever switch to another associative container, we need to either use a
|
// we ever switch to another associative container, we need to either use a
|
||||||
|
@ -161,7 +164,7 @@ private:
|
||||||
* blockfile info, and checks if there is enough disk space to save the block.
|
* blockfile info, and checks if there is enough disk space to save the block.
|
||||||
*
|
*
|
||||||
* The nAddSize argument passed to this function should include not just the size of the serialized CBlock, but also the size of
|
* The nAddSize argument passed to this function should include not just the size of the serialized CBlock, but also the size of
|
||||||
* separator fields which are written before it by WriteBlockToDisk (BLOCK_SERIALIZATION_HEADER_SIZE).
|
* separator fields (BLOCK_SERIALIZATION_HEADER_SIZE).
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] FlatFilePos FindNextBlockPos(unsigned int nAddSize, unsigned int nHeight, uint64_t nTime);
|
[[nodiscard]] FlatFilePos FindNextBlockPos(unsigned int nAddSize, unsigned int nHeight, uint64_t nTime);
|
||||||
[[nodiscard]] bool FlushChainstateBlockFile(int tip_height);
|
[[nodiscard]] bool FlushChainstateBlockFile(int tip_height);
|
||||||
|
@ -169,15 +172,6 @@ private:
|
||||||
|
|
||||||
AutoFile OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false) const;
|
AutoFile OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a block to disk. The pos argument passed to this function is modified by this call. Before this call, it should
|
|
||||||
* point to an unused file location where separator fields will be written, followed by the serialized CBlock data.
|
|
||||||
* After this call, it will point to the beginning of the serialized CBlock data, after the separator fields
|
|
||||||
* (BLOCK_SERIALIZATION_HEADER_SIZE)
|
|
||||||
*/
|
|
||||||
bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos) const;
|
|
||||||
bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock) const;
|
|
||||||
|
|
||||||
/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
|
/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
|
||||||
void FindFilesToPruneManual(
|
void FindFilesToPruneManual(
|
||||||
std::set<int>& setFilesToPrune,
|
std::set<int>& setFilesToPrune,
|
||||||
|
@ -330,7 +324,7 @@ public:
|
||||||
/** Get block file info entry for one block file */
|
/** Get block file info entry for one block file */
|
||||||
CBlockFileInfo* GetBlockFileInfo(size_t n);
|
CBlockFileInfo* GetBlockFileInfo(size_t n);
|
||||||
|
|
||||||
bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
|
bool SaveBlockUndo(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
/** Store block on disk and update block file statistics.
|
/** Store block on disk and update block file statistics.
|
||||||
|
@ -341,14 +335,13 @@ public:
|
||||||
* @returns in case of success, the position to which the block was written to
|
* @returns in case of success, the position to which the block was written to
|
||||||
* in case of an error, an empty FlatFilePos
|
* in case of an error, an empty FlatFilePos
|
||||||
*/
|
*/
|
||||||
FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight);
|
FlatFilePos SaveBlock(const CBlock& block, int nHeight);
|
||||||
|
|
||||||
/** Update blockfile info while processing a block during reindex. The block must be available on disk.
|
/** Update blockfile info while processing a block during reindex. The block must be available on disk.
|
||||||
*
|
*
|
||||||
* @param[in] block the block being processed
|
* @param[in] block the block being processed
|
||||||
* @param[in] nHeight the height of the block
|
* @param[in] nHeight the height of the block
|
||||||
* @param[in] pos the position of the serialized CBlock on disk. This is the position returned
|
* @param[in] pos the position of the serialized CBlock on disk
|
||||||
* by WriteBlockToDisk pointing at the CBlock, not the separator fields before it
|
|
||||||
*/
|
*/
|
||||||
void UpdateBlockInfo(const CBlock& block, unsigned int nHeight, const FlatFilePos& pos);
|
void UpdateBlockInfo(const CBlock& block, unsigned int nHeight, const FlatFilePos& pos);
|
||||||
|
|
||||||
|
|
|
@ -421,6 +421,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
||||||
}
|
}
|
||||||
|
|
||||||
++nPackagesSelected;
|
++nPackagesSelected;
|
||||||
|
pblocktemplate->m_package_feerates.emplace_back(packageFees, static_cast<int32_t>(packageSize));
|
||||||
|
|
||||||
// Update transactions that depend on each of these
|
// Update transactions that depend on each of these
|
||||||
nDescendantsUpdated += UpdatePackagesForAdded(mempool, ancestors, mapModifiedTx);
|
nDescendantsUpdated += UpdatePackagesForAdded(mempool, ancestors, mapModifiedTx);
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
#include <primitives/block.h>
|
#include <primitives/block.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
|
#include <util/feefrac.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -39,6 +40,9 @@ struct CBlockTemplate
|
||||||
std::vector<CAmount> vTxFees;
|
std::vector<CAmount> vTxFees;
|
||||||
std::vector<int64_t> vTxSigOpsCost;
|
std::vector<int64_t> vTxSigOpsCost;
|
||||||
std::vector<unsigned char> vchCoinbaseCommitment;
|
std::vector<unsigned char> vchCoinbaseCommitment;
|
||||||
|
/* A vector of package fee rates, ordered by the sequence in which
|
||||||
|
* packages are selected for inclusion in the block template.*/
|
||||||
|
std::vector<FeeFrac> m_package_feerates;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Container for tracking updates to ancestor feerate as we include (parent)
|
// Container for tracking updates to ancestor feerate as we include (parent)
|
||||||
|
|
|
@ -51,7 +51,7 @@ void AutoFile::seek(int64_t offset, int origin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t AutoFile::tell()
|
int64_t AutoFile::tell() const
|
||||||
{
|
{
|
||||||
if (!m_position.has_value()) throw std::ios_base::failure("AutoFile::tell: position unknown");
|
if (!m_position.has_value()) throw std::ios_base::failure("AutoFile::tell: position unknown");
|
||||||
return *m_position;
|
return *m_position;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <flatfile.h>
|
||||||
#include <ios>
|
#include <ios>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <util/check.h>
|
||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
inline void Xor(Span<std::byte> write, Span<const std::byte> key, size_t key_offset = 0)
|
inline void Xor(Span<std::byte> write, Span<const std::byte> key, size_t key_offset = 0)
|
||||||
|
@ -428,7 +430,7 @@ public:
|
||||||
bool IsNull() const { return m_file == nullptr; }
|
bool IsNull() const { return m_file == nullptr; }
|
||||||
|
|
||||||
/** Continue with a different XOR key */
|
/** Continue with a different XOR key */
|
||||||
void SetXor(std::vector<std::byte> data_xor) { m_xor = data_xor; }
|
void SetXor(const std::vector<std::byte>& data_xor) { m_xor = data_xor; }
|
||||||
|
|
||||||
/** Implementation detail, only used internally. */
|
/** Implementation detail, only used internally. */
|
||||||
std::size_t detail_fread(Span<std::byte> dst);
|
std::size_t detail_fread(Span<std::byte> dst);
|
||||||
|
@ -437,7 +439,7 @@ public:
|
||||||
void seek(int64_t offset, int origin);
|
void seek(int64_t offset, int origin);
|
||||||
|
|
||||||
/** Find position within the file. Will throw if unknown. */
|
/** Find position within the file. Will throw if unknown. */
|
||||||
int64_t tell();
|
int64_t tell() const;
|
||||||
|
|
||||||
/** Wrapper around FileCommit(). */
|
/** Wrapper around FileCommit(). */
|
||||||
bool Commit();
|
bool Commit();
|
||||||
|
@ -614,4 +616,87 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BufferedReadOnlyFile
|
||||||
|
{
|
||||||
|
AutoFile m_src;
|
||||||
|
std::vector<std::byte> m_buf;
|
||||||
|
size_t m_buf_start{0}, m_buf_end{0};
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BufferedReadOnlyFile(const FlatFileSeq& block_file_seq,
|
||||||
|
const FlatFilePos& pos,
|
||||||
|
const std::vector<std::byte>& m_xor_key,
|
||||||
|
const size_t buf_size = 16 << 10)
|
||||||
|
: m_src{block_file_seq.Open(pos, /*read_only=*/true), m_xor_key},
|
||||||
|
m_buf{buf_size} {}
|
||||||
|
|
||||||
|
void read(Span<std::byte> dst)
|
||||||
|
{
|
||||||
|
if (m_buf_start < m_buf_end) {
|
||||||
|
const size_t chunk = Assert(std::min(dst.size(), m_buf_end - m_buf_start));
|
||||||
|
std::memcpy(dst.data(), m_buf.data() + m_buf_start, chunk);
|
||||||
|
m_buf_start += chunk;
|
||||||
|
dst = dst.subspan(chunk);
|
||||||
|
}
|
||||||
|
if (!dst.empty()) {
|
||||||
|
Assume(m_buf_start == m_buf_end);
|
||||||
|
m_src.read(dst);
|
||||||
|
|
||||||
|
m_buf_start = 0;
|
||||||
|
m_buf_end = m_src.detail_fread(m_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNull() const { return m_src.IsNull(); }
|
||||||
|
|
||||||
|
template <typename T> void operator>>(T&& obj) { ::Unserialize(*this, obj); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferedWriteOnlyFile {
|
||||||
|
const std::vector<std::byte>& m_xor_key;
|
||||||
|
AutoFile m_dest;
|
||||||
|
std::vector<std::byte> m_buf;
|
||||||
|
size_t m_buf_pos{0};
|
||||||
|
|
||||||
|
void flush() {
|
||||||
|
if (m_buf_pos == 0) return;
|
||||||
|
|
||||||
|
const auto bytes = (m_buf_pos == m_buf.size()) ? m_buf : Span{m_buf}.first(m_buf_pos);
|
||||||
|
util::Xor(bytes, m_xor_key, m_dest.tell());
|
||||||
|
m_dest.write(bytes);
|
||||||
|
|
||||||
|
m_buf_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BufferedWriteOnlyFile(const FlatFileSeq& block_file_seq,
|
||||||
|
const FlatFilePos& pos,
|
||||||
|
const std::vector<std::byte>& m_xor_key,
|
||||||
|
const size_t buf_size = 1 << 20)
|
||||||
|
: m_xor_key{m_xor_key},
|
||||||
|
m_dest{block_file_seq.Open(pos, /*read_only=*/false), {}}, // We'll handle obfuscation internally
|
||||||
|
m_buf{buf_size} {}
|
||||||
|
|
||||||
|
~BufferedWriteOnlyFile() { flush(); }
|
||||||
|
|
||||||
|
void write(Span<const std::byte> src) {
|
||||||
|
while (!src.empty()) {
|
||||||
|
if (m_buf_pos == m_buf.size()) flush();
|
||||||
|
|
||||||
|
const size_t chunk = Assert(std::min(src.size(), m_buf.size() - m_buf_pos));
|
||||||
|
std::memcpy(m_buf.data() + m_buf_pos, src.data(), chunk);
|
||||||
|
m_buf_pos += chunk;
|
||||||
|
src = src.subspan(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNull() const { return m_dest.IsNull(); }
|
||||||
|
int64_t tell() const { return m_dest.tell() + m_buf_pos; }
|
||||||
|
|
||||||
|
template<typename T> BufferedWriteOnlyFile& operator<<(const T& obj) {
|
||||||
|
::Serialize(*this, obj);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_STREAMS_H
|
#endif // BITCOIN_STREAMS_H
|
||||||
|
|
|
@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos)
|
||||||
};
|
};
|
||||||
BlockManager blockman{*Assert(m_node.shutdown_signal), blockman_opts};
|
BlockManager blockman{*Assert(m_node.shutdown_signal), blockman_opts};
|
||||||
// simulate adding a genesis block normally
|
// simulate adding a genesis block normally
|
||||||
BOOST_CHECK_EQUAL(blockman.SaveBlockToDisk(params->GenesisBlock(), 0).nPos, BLOCK_SERIALIZATION_HEADER_SIZE);
|
BOOST_CHECK_EQUAL(blockman.SaveBlock(params->GenesisBlock(), 0).nPos, BLOCK_SERIALIZATION_HEADER_SIZE);
|
||||||
// simulate what happens during reindex
|
// simulate what happens during reindex
|
||||||
// simulate a well-formed genesis block being found at offset 8 in the blk00000.dat file
|
// simulate a well-formed genesis block being found at offset 8 in the blk00000.dat file
|
||||||
// the block is found at offset 8 because there is an 8 byte serialization header
|
// the block is found at offset 8 because there is an 8 byte serialization header
|
||||||
|
@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos)
|
||||||
// this is a check to make sure that https://github.com/bitcoin/bitcoin/issues/21379 does not recur
|
// this is a check to make sure that https://github.com/bitcoin/bitcoin/issues/21379 does not recur
|
||||||
// 8 bytes (for serialization header) + 285 (for serialized genesis block) = 293
|
// 8 bytes (for serialization header) + 285 (for serialized genesis block) = 293
|
||||||
// add another 8 bytes for the second block's serialization header and we get 293 + 8 = 301
|
// add another 8 bytes for the second block's serialization header and we get 293 + 8 = 301
|
||||||
FlatFilePos actual{blockman.SaveBlockToDisk(params->GenesisBlock(), 1)};
|
FlatFilePos actual{blockman.SaveBlock(params->GenesisBlock(), 1)};
|
||||||
BOOST_CHECK_EQUAL(actual.nPos, BLOCK_SERIALIZATION_HEADER_SIZE + ::GetSerializeSize(TX_WITH_WITNESS(params->GenesisBlock())) + BLOCK_SERIALIZATION_HEADER_SIZE);
|
BOOST_CHECK_EQUAL(actual.nPos, BLOCK_SERIALIZATION_HEADER_SIZE + ::GetSerializeSize(TX_WITH_WITNESS(params->GenesisBlock())) + BLOCK_SERIALIZATION_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,10 +158,10 @@ BOOST_AUTO_TEST_CASE(blockmanager_flush_block_file)
|
||||||
BOOST_CHECK_EQUAL(blockman.CalculateCurrentUsage(), 0);
|
BOOST_CHECK_EQUAL(blockman.CalculateCurrentUsage(), 0);
|
||||||
|
|
||||||
// Write the first block to a new location.
|
// Write the first block to a new location.
|
||||||
FlatFilePos pos1{blockman.SaveBlockToDisk(block1, /*nHeight=*/1)};
|
FlatFilePos pos1{blockman.SaveBlock(block1, /*nHeight=*/1)};
|
||||||
|
|
||||||
// Write second block
|
// Write second block
|
||||||
FlatFilePos pos2{blockman.SaveBlockToDisk(block2, /*nHeight=*/2)};
|
FlatFilePos pos2{blockman.SaveBlock(block2, /*nHeight=*/2)};
|
||||||
|
|
||||||
// Two blocks in the file
|
// Two blocks in the file
|
||||||
BOOST_CHECK_EQUAL(blockman.CalculateCurrentUsage(), (TEST_BLOCK_SIZE + BLOCK_SERIALIZATION_HEADER_SIZE) * 2);
|
BOOST_CHECK_EQUAL(blockman.CalculateCurrentUsage(), (TEST_BLOCK_SIZE + BLOCK_SERIALIZATION_HEADER_SIZE) * 2);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
#include <util/check.h>
|
#include <util/check.h>
|
||||||
|
#include <util/feefrac.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
@ -123,19 +125,22 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
||||||
tx.vout[0].nValue = 5000000000LL - 1000;
|
tx.vout[0].nValue = 5000000000LL - 1000;
|
||||||
// This tx has a low fee: 1000 satoshis
|
// This tx has a low fee: 1000 satoshis
|
||||||
Txid hashParentTx = tx.GetHash(); // save this txid for later use
|
Txid hashParentTx = tx.GetHash(); // save this txid for later use
|
||||||
AddToMempool(tx_mempool, entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
|
const auto parent_tx{entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx)};
|
||||||
|
AddToMempool(tx_mempool, parent_tx);
|
||||||
|
|
||||||
// This tx has a medium fee: 10000 satoshis
|
// This tx has a medium fee: 10000 satoshis
|
||||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||||
tx.vout[0].nValue = 5000000000LL - 10000;
|
tx.vout[0].nValue = 5000000000LL - 10000;
|
||||||
Txid hashMediumFeeTx = tx.GetHash();
|
Txid hashMediumFeeTx = tx.GetHash();
|
||||||
AddToMempool(tx_mempool, entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
|
const auto medium_fee_tx{entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx)};
|
||||||
|
AddToMempool(tx_mempool, medium_fee_tx);
|
||||||
|
|
||||||
// This tx has a high fee, but depends on the first transaction
|
// This tx has a high fee, but depends on the first transaction
|
||||||
tx.vin[0].prevout.hash = hashParentTx;
|
tx.vin[0].prevout.hash = hashParentTx;
|
||||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
|
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
|
||||||
Txid hashHighFeeTx = tx.GetHash();
|
Txid hashHighFeeTx = tx.GetHash();
|
||||||
AddToMempool(tx_mempool, entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
|
const auto high_fee_tx{entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx)};
|
||||||
|
AddToMempool(tx_mempool, high_fee_tx);
|
||||||
|
|
||||||
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options);
|
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options);
|
||||||
BOOST_REQUIRE(block_template);
|
BOOST_REQUIRE(block_template);
|
||||||
|
@ -145,6 +150,21 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
||||||
BOOST_CHECK(block.vtx[2]->GetHash() == hashHighFeeTx);
|
BOOST_CHECK(block.vtx[2]->GetHash() == hashHighFeeTx);
|
||||||
BOOST_CHECK(block.vtx[3]->GetHash() == hashMediumFeeTx);
|
BOOST_CHECK(block.vtx[3]->GetHash() == hashMediumFeeTx);
|
||||||
|
|
||||||
|
// Test the inclusion of package feerates in the block template and ensure they are sequential.
|
||||||
|
const auto block_package_feerates = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock()->m_package_feerates;
|
||||||
|
BOOST_CHECK(block_package_feerates.size() == 2);
|
||||||
|
|
||||||
|
// parent_tx and high_fee_tx are added to the block as a package.
|
||||||
|
const auto combined_txs_fee = parent_tx.GetFee() + high_fee_tx.GetFee();
|
||||||
|
const auto combined_txs_size = parent_tx.GetTxSize() + high_fee_tx.GetTxSize();
|
||||||
|
FeeFrac package_feefrac{combined_txs_fee, combined_txs_size};
|
||||||
|
// The package should be added first.
|
||||||
|
BOOST_CHECK(block_package_feerates[0] == package_feefrac);
|
||||||
|
|
||||||
|
// The medium_fee_tx should be added next.
|
||||||
|
FeeFrac medium_tx_feefrac{medium_fee_tx.GetFee(), medium_fee_tx.GetTxSize()};
|
||||||
|
BOOST_CHECK(block_package_feerates[1] == medium_tx_feefrac);
|
||||||
|
|
||||||
// Test that a package below the block min tx fee doesn't get included
|
// Test that a package below the block min tx fee doesn't get included
|
||||||
tx.vin[0].prevout.hash = hashHighFeeTx;
|
tx.vin[0].prevout.hash = hashHighFeeTx;
|
||||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
|
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
#include <node/blockstorage.h>
|
||||||
|
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
@ -553,6 +554,92 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
|
||||||
fs::remove(streams_test_filename);
|
fs::remove(streams_test_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(buffered_read_only_file_matches_autofile_random_content)
|
||||||
|
{
|
||||||
|
const FlatFileSeq test_file{m_args.GetDataDirBase(), "buffered_file_test_random", node::BLOCKFILE_CHUNK_SIZE};
|
||||||
|
constexpr size_t file_size{1 << 20};
|
||||||
|
constexpr size_t max_read_length{100};
|
||||||
|
const FlatFilePos pos{0, 0};
|
||||||
|
|
||||||
|
FastRandomContext rng{/*fDeterministic=*/false};
|
||||||
|
const std::vector obfuscation{rng.randbytes<std::byte>(8)};
|
||||||
|
AutoFile{test_file.Open(pos, false), obfuscation}.write(rng.randbytes<std::byte>(file_size));
|
||||||
|
AutoFile auto_file{test_file.Open(pos, true), obfuscation};
|
||||||
|
BufferedReadOnlyFile buffered{test_file, pos, obfuscation};
|
||||||
|
|
||||||
|
for (size_t total_read{0}; total_read < file_size;) {
|
||||||
|
const size_t read{Assert(std::min(rng.randrange(max_read_length) + 1, file_size - total_read))};
|
||||||
|
|
||||||
|
std::vector<std::byte> auto_file_buffer{read};
|
||||||
|
auto_file.read(auto_file_buffer);
|
||||||
|
|
||||||
|
std::vector<std::byte> buffered_buffer{read};
|
||||||
|
buffered.read(buffered_buffer);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL_COLLECTIONS(
|
||||||
|
auto_file_buffer.begin(), auto_file_buffer.end(),
|
||||||
|
buffered_buffer.begin(), buffered_buffer.end()
|
||||||
|
);
|
||||||
|
|
||||||
|
total_read += read;
|
||||||
|
}
|
||||||
|
std::vector<std::byte> excess{1};
|
||||||
|
BOOST_CHECK_EXCEPTION(auto_file.read(excess), std::ios_base::failure, HasReason{"end of file"});
|
||||||
|
BOOST_CHECK_EXCEPTION(buffered.read(excess), std::ios_base::failure, HasReason{"end of file"});
|
||||||
|
|
||||||
|
try { fs::remove(test_file.FileName(pos)); } catch (...) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(buffered_write_only_file_matches_autofile_random_content)
|
||||||
|
{
|
||||||
|
const FlatFileSeq test_buffered{m_args.GetDataDirBase(), "buffered_write_test", node::BLOCKFILE_CHUNK_SIZE};
|
||||||
|
const FlatFileSeq test_direct{m_args.GetDataDirBase(), "direct_write_test", node::BLOCKFILE_CHUNK_SIZE};
|
||||||
|
constexpr size_t file_size{1 << 20};
|
||||||
|
constexpr size_t max_write_length{100};
|
||||||
|
const FlatFilePos pos{0, 0};
|
||||||
|
|
||||||
|
FastRandomContext rng{/*fDeterministic=*/false};
|
||||||
|
const std::vector obfuscation{rng.randbytes<std::byte>(8)};
|
||||||
|
|
||||||
|
{
|
||||||
|
std::vector test_data{rng.randbytes<std::byte>(file_size)};
|
||||||
|
AutoFile direct_file{test_direct.Open(pos, false), obfuscation};
|
||||||
|
BufferedWriteOnlyFile buffered{test_buffered, pos, obfuscation};
|
||||||
|
BOOST_CHECK_EQUAL(direct_file.tell(), buffered.tell());
|
||||||
|
|
||||||
|
for (size_t total_written{0}; total_written < file_size;) {
|
||||||
|
const size_t write_size{Assert(std::min(rng.randrange(max_write_length) + 1, file_size - total_written))};
|
||||||
|
|
||||||
|
auto current_span = Span{test_data}.subspan(total_written, write_size);
|
||||||
|
direct_file.write(current_span);
|
||||||
|
buffered.write(current_span);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(direct_file.tell(), buffered.tell());
|
||||||
|
|
||||||
|
total_written += write_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the resulting files
|
||||||
|
AutoFile verify_direct{test_direct.Open(pos, true), obfuscation};
|
||||||
|
std::vector<std::byte> direct_result{file_size};
|
||||||
|
verify_direct.read(direct_result);
|
||||||
|
|
||||||
|
AutoFile verify_buffered{test_buffered.Open(pos, true), obfuscation};
|
||||||
|
std::vector<std::byte> buffered_result{file_size};
|
||||||
|
verify_buffered.read(buffered_result);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL_COLLECTIONS(
|
||||||
|
direct_result.begin(), direct_result.end(),
|
||||||
|
buffered_result.begin(), buffered_result.end()
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs::remove(test_direct.FileName(pos));
|
||||||
|
fs::remove(test_buffered.FileName(pos));
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(streams_hashed)
|
BOOST_AUTO_TEST_CASE(streams_hashed)
|
||||||
{
|
{
|
||||||
DataStream stream{};
|
DataStream stream{};
|
||||||
|
|
|
@ -2747,7 +2747,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_blockman.WriteUndoDataForBlock(blockundo, state, *pindex)) {
|
if (!m_blockman.SaveBlockUndo(blockundo, state, *pindex)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4564,7 +4564,7 @@ bool ChainstateManager::AcceptBlock(const std::shared_ptr<const CBlock>& pblock,
|
||||||
blockPos = *dbp;
|
blockPos = *dbp;
|
||||||
m_blockman.UpdateBlockInfo(block, pindex->nHeight, blockPos);
|
m_blockman.UpdateBlockInfo(block, pindex->nHeight, blockPos);
|
||||||
} else {
|
} else {
|
||||||
blockPos = m_blockman.SaveBlockToDisk(block, pindex->nHeight);
|
blockPos = m_blockman.SaveBlock(block, pindex->nHeight);
|
||||||
if (blockPos.IsNull()) {
|
if (blockPos.IsNull()) {
|
||||||
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
|
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
|
||||||
return false;
|
return false;
|
||||||
|
@ -5062,7 +5062,7 @@ bool Chainstate::LoadGenesisBlock()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const CBlock& block = params.GenesisBlock();
|
const CBlock& block = params.GenesisBlock();
|
||||||
FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, 0)};
|
FlatFilePos blockPos{m_blockman.SaveBlock(block, 0)};
|
||||||
if (blockPos.IsNull()) {
|
if (blockPos.IsNull()) {
|
||||||
LogError("%s: writing genesis block to disk failed\n", __func__);
|
LogError("%s: writing genesis block to disk failed\n", __func__);
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in a new issue