mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-20 13:37:28 +02:00
Merge ecab855838 into merged_master (Bitcoin PR bitcoin/bitcoin#28195)
This commit is contained in:
commit
bef3aeb383
15 changed files with 252 additions and 265 deletions
|
|
@ -17,9 +17,13 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
// ELEMENTS: forward decls
|
||||
namespace node {
|
||||
struct NodeContext;
|
||||
}
|
||||
namespace kernel {
|
||||
class BlockTreeDB;
|
||||
}
|
||||
/**
|
||||
* Maximum amount of time that a block timestamp is allowed to exceed the
|
||||
* current network-adjusted time before the block will be accepted.
|
||||
|
|
@ -227,7 +231,7 @@ protected:
|
|||
bool m_trimmed_dynafed_block{false};
|
||||
bool m_stored_lvl{false};
|
||||
|
||||
friend class CBlockTreeDB;
|
||||
friend class kernel::BlockTreeDB;
|
||||
|
||||
static node::NodeContext *m_pcontext;
|
||||
|
||||
|
|
|
|||
|
|
@ -1697,11 +1697,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
// ********************************************************* Step 8: start indexers
|
||||
|
||||
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
||||
auto result{WITH_LOCK(cs_main, return CheckLegacyTxindex(*Assert(chainman.m_blockman.m_block_tree_db)))};
|
||||
if (!result) {
|
||||
return InitError(util::ErrorString(result));
|
||||
}
|
||||
|
||||
g_txindex = std::make_unique<TxIndex>(interfaces::MakeChain(node), cache_sizes.tx_index, false, fReindex);
|
||||
node.indexes.emplace_back(g_txindex.get());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <chain.h>
|
||||
#include <clientversion.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <dbwrapper.h>
|
||||
#include <flatfile.h>
|
||||
#include <hash.h>
|
||||
#include <kernel/chainparams.h>
|
||||
|
|
@ -16,15 +17,206 @@
|
|||
#include <reverse_iterator.h>
|
||||
#include <signet.h>
|
||||
#include <streams.h>
|
||||
#include <sync.h>
|
||||
#include <undo.h>
|
||||
#include <util/batchpriority.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/translation.h>
|
||||
#include <validation.h>
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace kernel {
|
||||
static constexpr uint8_t DB_BLOCK_FILES{'f'};
|
||||
static constexpr uint8_t DB_BLOCK_INDEX{'b'};
|
||||
static constexpr uint8_t DB_FLAG{'F'};
|
||||
static constexpr uint8_t DB_REINDEX_FLAG{'R'};
|
||||
static constexpr uint8_t DB_LAST_BLOCK{'l'};
|
||||
// Keys used in previous version that might still be found in the DB:
|
||||
// BlockTreeDB::DB_TXINDEX_BLOCK{'T'};
|
||||
// BlockTreeDB::DB_TXINDEX{'t'}
|
||||
// BlockTreeDB::ReadFlag("txindex")
|
||||
// ELEMENTS:
|
||||
// static constexpr uint8_t DB_INVALID_BLOCK_Q{'q'}; // No longer used, but avoid reuse.
|
||||
static constexpr uint8_t DB_PAK{'p'};
|
||||
|
||||
bool BlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo& info)
|
||||
{
|
||||
return Read(std::make_pair(DB_BLOCK_FILES, nFile), info);
|
||||
}
|
||||
|
||||
bool BlockTreeDB::WriteReindexing(bool fReindexing)
|
||||
{
|
||||
if (fReindexing) {
|
||||
return Write(DB_REINDEX_FLAG, uint8_t{'1'});
|
||||
} else {
|
||||
return Erase(DB_REINDEX_FLAG);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockTreeDB::ReadReindexing(bool& fReindexing)
|
||||
{
|
||||
fReindexing = Exists(DB_REINDEX_FLAG);
|
||||
}
|
||||
|
||||
bool BlockTreeDB::ReadLastBlockFile(int& nFile)
|
||||
{
|
||||
return Read(DB_LAST_BLOCK, nFile);
|
||||
}
|
||||
|
||||
bool BlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*>>& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo)
|
||||
{
|
||||
CDBBatch batch(*this);
|
||||
for (const auto& [file, info] : fileInfo) {
|
||||
batch.Write(std::make_pair(DB_BLOCK_FILES, file), *info);
|
||||
}
|
||||
batch.Write(DB_LAST_BLOCK, nLastFile);
|
||||
for (const CBlockIndex* bi : blockinfo) {
|
||||
batch.Write(std::make_pair(DB_BLOCK_INDEX, bi->GetBlockHash()), CDiskBlockIndex{bi});
|
||||
}
|
||||
return WriteBatch(batch, true);
|
||||
}
|
||||
|
||||
bool BlockTreeDB::WriteFlag(const std::string& name, bool fValue)
|
||||
{
|
||||
return Write(std::make_pair(DB_FLAG, name), fValue ? uint8_t{'1'} : uint8_t{'0'});
|
||||
}
|
||||
|
||||
bool BlockTreeDB::ReadFlag(const std::string& name, bool& fValue)
|
||||
{
|
||||
uint8_t ch;
|
||||
if (!Read(std::make_pair(DB_FLAG, name), ch)) {
|
||||
return false;
|
||||
}
|
||||
fValue = ch == uint8_t{'1'};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt, int trimBelowHeight)
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
std::unique_ptr<CDBIterator> pcursor(NewIterator());
|
||||
pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256()));
|
||||
|
||||
int n_untrimmed = 0;
|
||||
int n_total = 0;
|
||||
|
||||
// Load m_block_index
|
||||
while (pcursor->Valid()) {
|
||||
if (interrupt) return false;
|
||||
std::pair<uint8_t, uint256> key;
|
||||
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
|
||||
CDiskBlockIndex diskindex;
|
||||
if (pcursor->GetValue(diskindex)) {
|
||||
// Construct block index object
|
||||
CBlockIndex* pindexNew = insertBlockIndex(diskindex.ConstructBlockHash());
|
||||
pindexNew->pprev = insertBlockIndex(diskindex.hashPrev);
|
||||
pindexNew->nHeight = diskindex.nHeight;
|
||||
pindexNew->nFile = diskindex.nFile;
|
||||
pindexNew->nDataPos = diskindex.nDataPos;
|
||||
pindexNew->nUndoPos = diskindex.nUndoPos;
|
||||
pindexNew->nVersion = diskindex.nVersion;
|
||||
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
|
||||
pindexNew->nTime = diskindex.nTime;
|
||||
pindexNew->nBits = diskindex.nBits;
|
||||
pindexNew->nNonce = diskindex.nNonce;
|
||||
pindexNew->nStatus = diskindex.nStatus;
|
||||
pindexNew->nTx = diskindex.nTx;
|
||||
|
||||
pindexNew->proof = diskindex.proof;
|
||||
pindexNew->m_dynafed_params = diskindex.m_dynafed_params;
|
||||
pindexNew->m_signblock_witness = diskindex.m_signblock_witness;
|
||||
|
||||
assert(!(g_signed_blocks && diskindex.m_dynafed_params.value().IsNull() && diskindex.proof.value().IsNull()));
|
||||
|
||||
pindexNew->set_stored();
|
||||
n_total++;
|
||||
|
||||
const uint256 block_hash = pindexNew->GetBlockHash();
|
||||
// Only validate one of every 1000 block header for sanity check
|
||||
if (pindexNew->nHeight % 1000 == 0 &&
|
||||
block_hash != consensusParams.hashGenesisBlock &&
|
||||
!CheckProof(pindexNew->GetBlockHeader(), consensusParams)) {
|
||||
return error("%s: CheckProof: %s, %s", __func__, block_hash.ToString(), pindexNew->ToString());
|
||||
}
|
||||
if (diskindex.nHeight >= trimBelowHeight) {
|
||||
n_untrimmed++;
|
||||
} else {
|
||||
pindexNew->trim();
|
||||
}
|
||||
|
||||
pcursor->Next();
|
||||
} else {
|
||||
return error("%s: failed to read value", __func__);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LogPrintf("LoadBlockIndexGuts: loaded %d total / %d untrimmed (fully in-memory) headers\n", n_total, n_untrimmed);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ELEMENTS
|
||||
bool BlockTreeDB::ReadPAKList(std::vector<std::vector<unsigned char> >& offline_list, std::vector<std::vector<unsigned char> >& online_list, bool& reject)
|
||||
{
|
||||
return Read(std::make_pair(DB_PAK, uint256S("1")), offline_list) && Read(std::make_pair(DB_PAK, uint256S("2")), online_list) && Read(std::make_pair(DB_PAK, uint256S("3")), reject);
|
||||
}
|
||||
|
||||
bool BlockTreeDB::WritePAKList(const std::vector<std::vector<unsigned char> >& offline_list, const std::vector<std::vector<unsigned char> >& online_list, bool reject)
|
||||
{
|
||||
return Write(std::make_pair(DB_PAK, uint256S("1")), offline_list) && Write(std::make_pair(DB_PAK, uint256S("2")), online_list) && Write(std::make_pair(DB_PAK, uint256S("3")), reject);
|
||||
}
|
||||
|
||||
const CBlockIndex *BlockTreeDB::RegenerateFullIndex(const CBlockIndex *pindexTrimmed, CBlockIndex *pindexNew) const
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
if(!pindexTrimmed->trimmed()) {
|
||||
return pindexTrimmed;
|
||||
}
|
||||
CBlockHeader tmp;
|
||||
bool BlockRead = false;
|
||||
{
|
||||
// In unpruned nodes, same data could be read from blocks using ReadBlockFromDisk, but that turned out to
|
||||
// be about 6x slower than reading from the index
|
||||
std::pair<uint8_t, uint256> key(DB_BLOCK_INDEX, pindexTrimmed->GetBlockHash());
|
||||
CDiskBlockIndex diskindex;
|
||||
BlockRead = this->Read(key, diskindex);
|
||||
tmp = diskindex.GetBlockHeader();
|
||||
}
|
||||
assert(BlockRead);
|
||||
// Clone the needed data from the original trimmed block
|
||||
pindexNew->pprev = pindexTrimmed->pprev;
|
||||
pindexNew->phashBlock = pindexTrimmed->phashBlock;
|
||||
// Construct block index object
|
||||
pindexNew->nHeight = pindexTrimmed->nHeight;
|
||||
pindexNew->nFile = pindexTrimmed->nFile;
|
||||
pindexNew->nDataPos = pindexTrimmed->nDataPos;
|
||||
pindexNew->nUndoPos = pindexTrimmed->nUndoPos;
|
||||
pindexNew->nVersion = pindexTrimmed->nVersion;
|
||||
pindexNew->hashMerkleRoot = pindexTrimmed->hashMerkleRoot;
|
||||
pindexNew->nTime = pindexTrimmed->nTime;
|
||||
pindexNew->nBits = pindexTrimmed->nBits;
|
||||
pindexNew->nNonce = pindexTrimmed->nNonce;
|
||||
pindexNew->nStatus = pindexTrimmed->nStatus;
|
||||
pindexNew->nTx = pindexTrimmed->nTx;
|
||||
|
||||
pindexNew->proof = tmp.proof;
|
||||
pindexNew->m_dynafed_params = tmp.m_dynafed_params;
|
||||
pindexNew->m_signblock_witness = tmp.m_signblock_witness;
|
||||
|
||||
if (pindexTrimmed->nHeight && pindexTrimmed->nHeight % 1000 == 0) {
|
||||
assert(CheckProof(pindexNew->GetBlockHeader(), Params().GetConsensus()));
|
||||
}
|
||||
return pindexNew;
|
||||
}
|
||||
// END ELEMENTS
|
||||
} // namespace kernel
|
||||
|
||||
namespace node {
|
||||
std::atomic_bool fReindex(false);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,17 +7,25 @@
|
|||
|
||||
#include <attributes.h>
|
||||
#include <chain.h>
|
||||
#include <dbwrapper.h>
|
||||
#include <kernel/blockmanager_opts.h>
|
||||
#include <kernel/chainparams.h>
|
||||
#include <kernel/cs_main.h>
|
||||
#include <protocol.h>
|
||||
#include <sync.h>
|
||||
#include <txdb.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/hasher.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class BlockValidationState;
|
||||
|
|
@ -36,7 +44,30 @@ namespace util {
|
|||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
||||
namespace kernel {
|
||||
/** Access to the block database (blocks/index/) */
|
||||
class BlockTreeDB : public CDBWrapper
|
||||
{
|
||||
public:
|
||||
using CDBWrapper::CDBWrapper;
|
||||
bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*>>& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo);
|
||||
bool ReadBlockFileInfo(int nFile, CBlockFileInfo& info);
|
||||
bool ReadLastBlockFile(int& nFile);
|
||||
bool WriteReindexing(bool fReindexing);
|
||||
void ReadReindexing(bool& fReindexing);
|
||||
bool WriteFlag(const std::string& name, bool fValue);
|
||||
bool ReadFlag(const std::string& name, bool& fValue);
|
||||
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt, int trimBelowHeight)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
// ELEMENTS:
|
||||
const CBlockIndex *RegenerateFullIndex(const CBlockIndex *pindexTrimmed, CBlockIndex *pindexNew) const;
|
||||
bool ReadPAKList(std::vector<std::vector<unsigned char> >& offline_list, std::vector<std::vector<unsigned char> >& online_list, bool& reject);
|
||||
bool WritePAKList(const std::vector<std::vector<unsigned char> >& offline_list, const std::vector<std::vector<unsigned char> >& online_list, bool reject);
|
||||
};
|
||||
} // namespace kernel
|
||||
|
||||
namespace node {
|
||||
using kernel::BlockTreeDB;
|
||||
|
||||
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
|
||||
static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
|
||||
|
|
@ -195,7 +226,7 @@ public:
|
|||
*/
|
||||
std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
|
||||
|
||||
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
|
||||
std::unique_ptr<BlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
|
||||
|
||||
bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
|
|
|||
|
|
@ -37,10 +37,10 @@ static ChainstateLoadResult CompleteChainstateInitialization(
|
|||
const ChainstateLoadOptions& options) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
auto& pblocktree{chainman.m_blockman.m_block_tree_db};
|
||||
// new CBlockTreeDB tries to delete the existing file, which
|
||||
// new BlockTreeDB tries to delete the existing file, which
|
||||
// fails if it's still open from the previous loop. Close it first:
|
||||
pblocktree.reset();
|
||||
pblocktree = std::make_unique<CBlockTreeDB>(DBParams{
|
||||
pblocktree = std::make_unique<BlockTreeDB>(DBParams{
|
||||
.path = chainman.m_options.datadir / "blocks" / "index",
|
||||
.cache_bytes = static_cast<size_t>(cache_sizes.block_tree_db),
|
||||
.memory_only = options.block_tree_db_in_memory,
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@
|
|||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
using kernel::BlockTreeDB;
|
||||
using kernel::ValidationCacheSizes;
|
||||
using node::ApplyArgsManOptions;
|
||||
using node::BlockAssembler;
|
||||
|
|
@ -203,7 +204,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
|
|||
.notifications = chainman_opts.notifications,
|
||||
};
|
||||
m_node.chainman = std::make_unique<ChainstateManager>(m_node.kernel->interrupt, chainman_opts, blockman_opts);
|
||||
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<CBlockTreeDB>(DBParams{
|
||||
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<BlockTreeDB>(DBParams{
|
||||
.path = m_args.GetDataDirNet() / "blocks" / "index",
|
||||
.cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
|
||||
.memory_only = true});
|
||||
|
|
|
|||
210
src/txdb.cpp
210
src/txdb.cpp
|
|
@ -5,58 +5,31 @@
|
|||
|
||||
#include <txdb.h>
|
||||
|
||||
#include <chain.h>
|
||||
#include <coins.h>
|
||||
#include <dbwrapper.h>
|
||||
#include <logging.h>
|
||||
#include <pow.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <random.h>
|
||||
#include <serialize.h>
|
||||
#include <uint256.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/translation.h>
|
||||
#include <util/vector.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
// ELEMENTS
|
||||
#include <block_proof.h> // CheckProof
|
||||
#include <chainparams.h> // Params()
|
||||
|
||||
static constexpr uint8_t DB_COIN{'C'};
|
||||
static constexpr uint8_t DB_BLOCK_FILES{'f'};
|
||||
static constexpr uint8_t DB_BLOCK_INDEX{'b'};
|
||||
|
||||
static constexpr uint8_t DB_BEST_BLOCK{'B'};
|
||||
static constexpr uint8_t DB_HEAD_BLOCKS{'H'};
|
||||
static constexpr uint8_t DB_FLAG{'F'};
|
||||
static constexpr uint8_t DB_REINDEX_FLAG{'R'};
|
||||
static constexpr uint8_t DB_LAST_BLOCK{'l'};
|
||||
|
||||
// ELEMENTS:
|
||||
static constexpr uint8_t DB_PEGIN_FLAG{'w'};
|
||||
// static constexpr uint8_t DB_INVALID_BLOCK_Q{'q'}; // No longer used, but avoid reuse.
|
||||
static constexpr uint8_t DB_PAK{'p'};
|
||||
|
||||
// Keys used in previous version that might still be found in the DB:
|
||||
static constexpr uint8_t DB_COINS{'c'};
|
||||
static constexpr uint8_t DB_TXINDEX_BLOCK{'T'};
|
||||
// uint8_t DB_TXINDEX{'t'}
|
||||
|
||||
util::Result<void> CheckLegacyTxindex(CBlockTreeDB& block_tree_db)
|
||||
{
|
||||
CBlockLocator ignored{};
|
||||
if (block_tree_db.Read(DB_TXINDEX_BLOCK, ignored)) {
|
||||
return util::Error{_("The -txindex upgrade started by a previous version cannot be completed. Restart with the previous version or run a full -reindex.")};
|
||||
}
|
||||
bool txindex_legacy_flag{false};
|
||||
block_tree_db.ReadFlag("txindex", txindex_legacy_flag);
|
||||
if (txindex_legacy_flag) {
|
||||
// Disable legacy txindex and warn once about occupied disk space
|
||||
if (!block_tree_db.WriteFlag("txindex", false)) {
|
||||
return util::Error{Untranslated("Failed to write block index db flag 'txindex'='0'")};
|
||||
}
|
||||
return util::Error{_("The block index db contains a legacy 'txindex'. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.")};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
// ELEMENTS
|
||||
static constexpr uint8_t DB_PEGIN_FLAG{'w'};
|
||||
|
||||
bool CCoinsViewDB::NeedsUpgrade()
|
||||
{
|
||||
|
|
@ -205,25 +178,6 @@ size_t CCoinsViewDB::EstimateSize() const
|
|||
return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
|
||||
return Read(std::make_pair(DB_BLOCK_FILES, nFile), info);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
|
||||
if (fReindexing)
|
||||
return Write(DB_REINDEX_FLAG, uint8_t{'1'});
|
||||
else
|
||||
return Erase(DB_REINDEX_FLAG);
|
||||
}
|
||||
|
||||
void CBlockTreeDB::ReadReindexing(bool &fReindexing) {
|
||||
fReindexing = Exists(DB_REINDEX_FLAG);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
|
||||
return Read(DB_LAST_BLOCK, nFile);
|
||||
}
|
||||
|
||||
/** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
|
||||
class CCoinsViewDBCursor: public CCoinsViewCursor
|
||||
{
|
||||
|
|
@ -296,147 +250,3 @@ void CCoinsViewDBCursor::Next()
|
|||
keyTmp.first = entry.key;
|
||||
}
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) {
|
||||
CDBBatch batch(*this);
|
||||
for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) {
|
||||
batch.Write(std::make_pair(DB_BLOCK_FILES, it->first), *it->second);
|
||||
}
|
||||
batch.Write(DB_LAST_BLOCK, nLastFile);
|
||||
for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) {
|
||||
batch.Write(std::make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it));
|
||||
}
|
||||
return WriteBatch(batch, true);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
|
||||
return Write(std::make_pair(DB_FLAG, name), fValue ? uint8_t{'1'} : uint8_t{'0'});
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
|
||||
uint8_t ch;
|
||||
if (!Read(std::make_pair(DB_FLAG, name), ch))
|
||||
return false;
|
||||
fValue = ch == uint8_t{'1'};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadPAKList(std::vector<std::vector<unsigned char> >& offline_list, std::vector<std::vector<unsigned char> >& online_list, bool& reject)
|
||||
{
|
||||
return Read(std::make_pair(DB_PAK, uint256S("1")), offline_list) && Read(std::make_pair(DB_PAK, uint256S("2")), online_list) && Read(std::make_pair(DB_PAK, uint256S("3")), reject);
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WritePAKList(const std::vector<std::vector<unsigned char> >& offline_list, const std::vector<std::vector<unsigned char> >& online_list, bool reject)
|
||||
{
|
||||
return Write(std::make_pair(DB_PAK, uint256S("1")), offline_list) && Write(std::make_pair(DB_PAK, uint256S("2")), online_list) && Write(std::make_pair(DB_PAK, uint256S("3")), reject);
|
||||
}
|
||||
|
||||
const CBlockIndex *CBlockTreeDB::RegenerateFullIndex(const CBlockIndex *pindexTrimmed, CBlockIndex *pindexNew) const
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
if(!pindexTrimmed->trimmed()) {
|
||||
return pindexTrimmed;
|
||||
}
|
||||
CBlockHeader tmp;
|
||||
bool BlockRead = false;
|
||||
{
|
||||
// In unpruned nodes, same data could be read from blocks using ReadBlockFromDisk, but that turned out to
|
||||
// be about 6x slower than reading from the index
|
||||
std::pair<uint8_t, uint256> key(DB_BLOCK_INDEX, pindexTrimmed->GetBlockHash());
|
||||
CDiskBlockIndex diskindex;
|
||||
BlockRead = this->Read(key, diskindex);
|
||||
tmp = diskindex.GetBlockHeader();
|
||||
}
|
||||
assert(BlockRead);
|
||||
// Clone the needed data from the original trimmed block
|
||||
pindexNew->pprev = pindexTrimmed->pprev;
|
||||
pindexNew->phashBlock = pindexTrimmed->phashBlock;
|
||||
// Construct block index object
|
||||
pindexNew->nHeight = pindexTrimmed->nHeight;
|
||||
pindexNew->nFile = pindexTrimmed->nFile;
|
||||
pindexNew->nDataPos = pindexTrimmed->nDataPos;
|
||||
pindexNew->nUndoPos = pindexTrimmed->nUndoPos;
|
||||
pindexNew->nVersion = pindexTrimmed->nVersion;
|
||||
pindexNew->hashMerkleRoot = pindexTrimmed->hashMerkleRoot;
|
||||
pindexNew->nTime = pindexTrimmed->nTime;
|
||||
pindexNew->nBits = pindexTrimmed->nBits;
|
||||
pindexNew->nNonce = pindexTrimmed->nNonce;
|
||||
pindexNew->nStatus = pindexTrimmed->nStatus;
|
||||
pindexNew->nTx = pindexTrimmed->nTx;
|
||||
|
||||
pindexNew->proof = tmp.proof;
|
||||
pindexNew->m_dynafed_params = tmp.m_dynafed_params;
|
||||
pindexNew->m_signblock_witness = tmp.m_signblock_witness;
|
||||
|
||||
if (pindexTrimmed->nHeight && pindexTrimmed->nHeight % 1000 == 0) {
|
||||
assert(CheckProof(pindexNew->GetBlockHeader(), Params().GetConsensus()));
|
||||
}
|
||||
return pindexNew;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt, int trimBelowHeight)
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
std::unique_ptr<CDBIterator> pcursor(NewIterator());
|
||||
pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256()));
|
||||
|
||||
int n_untrimmed = 0;
|
||||
int n_total = 0;
|
||||
|
||||
// Load m_block_index
|
||||
while (pcursor->Valid()) {
|
||||
if (interrupt) return false;
|
||||
std::pair<uint8_t, uint256> key;
|
||||
if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
|
||||
CDiskBlockIndex diskindex;
|
||||
if (pcursor->GetValue(diskindex)) {
|
||||
// Construct block index object
|
||||
CBlockIndex* pindexNew = insertBlockIndex(diskindex.ConstructBlockHash());
|
||||
pindexNew->pprev = insertBlockIndex(diskindex.hashPrev);
|
||||
pindexNew->nHeight = diskindex.nHeight;
|
||||
pindexNew->nFile = diskindex.nFile;
|
||||
pindexNew->nDataPos = diskindex.nDataPos;
|
||||
pindexNew->nUndoPos = diskindex.nUndoPos;
|
||||
pindexNew->nVersion = diskindex.nVersion;
|
||||
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
|
||||
pindexNew->nTime = diskindex.nTime;
|
||||
pindexNew->nBits = diskindex.nBits;
|
||||
pindexNew->nNonce = diskindex.nNonce;
|
||||
pindexNew->nStatus = diskindex.nStatus;
|
||||
pindexNew->nTx = diskindex.nTx;
|
||||
|
||||
pindexNew->proof = diskindex.proof;
|
||||
pindexNew->m_dynafed_params = diskindex.m_dynafed_params;
|
||||
pindexNew->m_signblock_witness = diskindex.m_signblock_witness;
|
||||
|
||||
assert(!(g_signed_blocks && diskindex.m_dynafed_params.value().IsNull() && diskindex.proof.value().IsNull()));
|
||||
|
||||
pindexNew->set_stored();
|
||||
n_total++;
|
||||
|
||||
const uint256 block_hash = pindexNew->GetBlockHash();
|
||||
// Only validate one of every 1000 block header for sanity check
|
||||
if (pindexNew->nHeight % 1000 == 0 &&
|
||||
block_hash != consensusParams.hashGenesisBlock &&
|
||||
!CheckProof(pindexNew->GetBlockHeader(), consensusParams)) {
|
||||
return error("%s: CheckProof: %s, %s", __func__, block_hash.ToString(), pindexNew->ToString());
|
||||
}
|
||||
if (diskindex.nHeight >= trimBelowHeight) {
|
||||
n_untrimmed++;
|
||||
} else {
|
||||
pindexNew->trim();
|
||||
}
|
||||
|
||||
pcursor->Next();
|
||||
} else {
|
||||
return error("%s: failed to read value", __func__);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LogPrintf("LoadBlockIndexGuts: loaded %d total / %d untrimmed (fully in-memory) headers\n", n_total, n_untrimmed);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
34
src/txdb.h
34
src/txdb.h
|
|
@ -11,27 +11,15 @@
|
|||
#include <kernel/cs_main.h>
|
||||
#include <sync.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/result.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class CBlockFileInfo;
|
||||
class CBlockIndex;
|
||||
class COutPoint;
|
||||
class uint256;
|
||||
namespace Consensus {
|
||||
struct Params;
|
||||
};
|
||||
namespace util {
|
||||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
||||
//! -dbcache default (MiB)
|
||||
static const int64_t nDefaultDbCache = 450;
|
||||
|
|
@ -91,26 +79,4 @@ public:
|
|||
std::optional<fs::path> StoragePath() { return m_db->StoragePath(); }
|
||||
};
|
||||
|
||||
/** Access to the block database (blocks/index/) */
|
||||
class CBlockTreeDB : public CDBWrapper
|
||||
{
|
||||
public:
|
||||
using CDBWrapper::CDBWrapper;
|
||||
bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo);
|
||||
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info);
|
||||
bool ReadLastBlockFile(int &nFile);
|
||||
bool WriteReindexing(bool fReindexing);
|
||||
void ReadReindexing(bool &fReindexing);
|
||||
bool WriteFlag(const std::string &name, bool fValue);
|
||||
bool ReadFlag(const std::string &name, bool &fValue);
|
||||
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt, int trimBelowHeight)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
// ELEMENTS:
|
||||
const CBlockIndex *RegenerateFullIndex(const CBlockIndex *pindexTrimmed, CBlockIndex *pindexNew) const;
|
||||
bool ReadPAKList(std::vector<std::vector<unsigned char> >& offline_list, std::vector<std::vector<unsigned char> >& online_list, bool& reject);
|
||||
bool WritePAKList(const std::vector<std::vector<unsigned char> >& offline_list, const std::vector<std::vector<unsigned char> >& online_list, bool reject);
|
||||
};
|
||||
|
||||
[[nodiscard]] util::Result<void> CheckLegacyTxindex(CBlockTreeDB& block_tree_db);
|
||||
|
||||
#endif // BITCOIN_TXDB_H
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@
|
|||
#include <primitives/pak.h> // CPAKList
|
||||
|
||||
class Chainstate;
|
||||
class CBlockTreeDB;
|
||||
class CTxMemPool;
|
||||
class ChainstateManager;
|
||||
struct ChainTxData;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class BlocksdirTest(BitcoinTestFramework):
|
|||
|
||||
def run_test(self):
|
||||
self.stop_node(0)
|
||||
assert os.path.isdir(os.path.join(self.nodes[0].chain_path, "blocks"))
|
||||
assert os.path.isdir(os.path.join(self.nodes[0].blocks_path))
|
||||
assert not os.path.isdir(os.path.join(self.nodes[0].datadir, "blocks"))
|
||||
shutil.rmtree(self.nodes[0].datadir)
|
||||
initialize_datadir(self.options.tmpdir, 0, self.chain)
|
||||
|
|
@ -31,7 +31,7 @@ class BlocksdirTest(BitcoinTestFramework):
|
|||
self.log.info("mining blocks..")
|
||||
self.generatetoaddress(self.nodes[0], 10, self.nodes[0].get_deterministic_priv_key().address)
|
||||
assert os.path.isfile(os.path.join(blocksdir_path, self.chain, "blocks", "blk00000.dat"))
|
||||
assert os.path.isdir(os.path.join(self.nodes[0].chain_path, "blocks", "index"))
|
||||
assert os.path.isdir(os.path.join(self.nodes[0].blocks_path, "index"))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class PruneTest(BitcoinTestFramework):
|
|||
def setup_network(self):
|
||||
self.setup_nodes()
|
||||
|
||||
self.prunedir = os.path.join(self.nodes[2].chain_path, 'blocks', '')
|
||||
self.prunedir = os.path.join(self.nodes[2].blocks_path, '')
|
||||
|
||||
self.connect_nodes(0, 1)
|
||||
self.connect_nodes(1, 2)
|
||||
|
|
@ -290,7 +290,7 @@ class PruneTest(BitcoinTestFramework):
|
|||
assert_equal(ret + 1, node.getblockchaininfo()['pruneheight'])
|
||||
|
||||
def has_block(index):
|
||||
return os.path.isfile(os.path.join(self.nodes[node_number].chain_path, "blocks", f"blk{index:05}.dat"))
|
||||
return os.path.isfile(os.path.join(self.nodes[node_number].blocks_path, f"blk{index:05}.dat"))
|
||||
|
||||
# should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000)
|
||||
assert_raises_rpc_error(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500))
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
Previous releases are required by this test, see test/README.md.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
|
|
@ -55,10 +54,6 @@ class TxindexCompatibilityTest(BitcoinTestFramework):
|
|||
drop_index_chain_dir = self.nodes[1].chain_path
|
||||
shutil.rmtree(drop_index_chain_dir)
|
||||
shutil.copytree(legacy_chain_dir, drop_index_chain_dir)
|
||||
self.nodes[1].assert_start_raises_init_error(
|
||||
extra_args=["-txindex"],
|
||||
expected_msg="Error: The block index db contains a legacy 'txindex'. To clear the occupied disk space, run a full -reindex, otherwise ignore this error. This error message will not be displayed again.",
|
||||
)
|
||||
# Build txindex from scratch and check there is no error this time
|
||||
self.start_node(1, extra_args=["-txindex"])
|
||||
self.wait_until(lambda: self.nodes[1].getindexinfo()["txindex"]["synced"] == True)
|
||||
|
|
@ -66,12 +61,6 @@ class TxindexCompatibilityTest(BitcoinTestFramework):
|
|||
|
||||
self.stop_nodes()
|
||||
|
||||
self.log.info("Check migrated txindex cannot be read by legacy node")
|
||||
err_msg = f": You need to rebuild the database using -reindex to change -txindex.{os.linesep}Please restart with -reindex or -reindex-chainstate to recover."
|
||||
shutil.rmtree(legacy_chain_dir)
|
||||
shutil.copytree(drop_index_chain_dir, legacy_chain_dir)
|
||||
self.nodes[0].assert_start_raises_init_error(extra_args=["-txindex"], expected_msg=err_msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
TxindexCompatibilityTest().main()
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ class WalletBackupTest(BitcoinTestFramework):
|
|||
self.erase_three()
|
||||
|
||||
#start node2 with no chain
|
||||
shutil.rmtree(os.path.join(self.nodes[2].chain_path, 'blocks'))
|
||||
shutil.rmtree(os.path.join(self.nodes[2].blocks_path))
|
||||
shutil.rmtree(os.path.join(self.nodes[2].chain_path, 'chainstate'))
|
||||
|
||||
self.start_three(["-nowallet"])
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class WalletHDTest(BitcoinTestFramework):
|
|||
self.stop_node(1)
|
||||
# we need to delete the complete chain directory
|
||||
# otherwise node1 would auto-recover all funds in flag the keypool keys as used
|
||||
shutil.rmtree(os.path.join(self.nodes[1].chain_path, "blocks"))
|
||||
shutil.rmtree(os.path.join(self.nodes[1].blocks_path))
|
||||
shutil.rmtree(os.path.join(self.nodes[1].chain_path, "chainstate"))
|
||||
shutil.copyfile(
|
||||
os.path.join(self.nodes[1].datadir, "hd.bak"),
|
||||
|
|
@ -115,7 +115,7 @@ class WalletHDTest(BitcoinTestFramework):
|
|||
|
||||
# Try a RPC based rescan
|
||||
self.stop_node(1)
|
||||
shutil.rmtree(os.path.join(self.nodes[1].chain_path, "blocks"))
|
||||
shutil.rmtree(os.path.join(self.nodes[1].blocks_path))
|
||||
shutil.rmtree(os.path.join(self.nodes[1].chain_path, "chainstate"))
|
||||
shutil.copyfile(
|
||||
os.path.join(self.nodes[1].datadir, "hd.bak"),
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class WalletPruningTest(BitcoinTestFramework):
|
|||
|
||||
def has_block(self, block_index):
|
||||
"""Checks if the pruned node has the specific blk0000*.dat file"""
|
||||
return os.path.isfile(os.path.join(self.nodes[1].chain_path, "blocks", f"blk{block_index:05}.dat"))
|
||||
return os.path.isfile(os.path.join(self.nodes[1].blocks_path, f"blk{block_index:05}.dat"))
|
||||
|
||||
def create_wallet(self, wallet_name, *, unload=False):
|
||||
"""Creates and dumps a wallet on the non-pruned node0 to be later import by the pruned node"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue