diff --git a/src/chain.h b/src/chain.h index fd136c115..81b4fc20c 100644 --- a/src/chain.h +++ b/src/chain.h @@ -1,427 +1,427 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 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_CHAIN_H #define BITCOIN_CHAIN_H #include #include #include #include #include #include #include #include #include #include #include #include /** * Maximum amount of time that a block timestamp is allowed to exceed the * current network-adjusted time before the block will be accepted. */ static constexpr int64_t MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60; /** * Timestamp window used as a grace period by code that compares external * timestamps (such as timestamps passed to RPCs, or wallet key creation times) * to block timestamps. This should be set at least as high as * MAX_FUTURE_BLOCK_TIME. */ static constexpr int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME; /** * Maximum gap between node time and block time used * for the "Catching up..." mode in GUI. * * Ref: https://github.com/bitcoin/bitcoin/pull/1026 */ static constexpr int64_t MAX_BLOCK_TIME_GAP = 90 * 60; /** * The block chain is a tree shaped structure starting with the genesis block at * the root, with each block potentially having multiple candidates to be the * next block. A blockindex may have multiple pprev pointing to it, but at most * one of them can be part of the currently active branch. */ class CBlockIndex { public: //! pointer to the hash of the block, if any. Memory is owned by this //! CBlockIndex const uint256 *phashBlock; //! pointer to the index of the predecessor of this block CBlockIndex *pprev; //! pointer to the index of some further predecessor of this block CBlockIndex *pskip; //! height of the entry in the chain. The genesis block has height 0 int nHeight; //! Which # file this block is stored in (blk?????.dat) int nFile; //! Byte offset within blk?????.dat where this block's data is stored unsigned int nDataPos; //! Byte offset within rev?????.dat where this block's undo data is stored unsigned int nUndoPos; //! (memory only) Total amount of work (expected number of hashes) in the //! chain up to and including this block arith_uint256 nChainWork; //! Number of transactions in this block. //! Note: in a potential headers-first mode, this number cannot be relied //! upon unsigned int nTx; //! (memory only) Number of transactions in the chain up to and including //! this block. //! This value will be non-zero only if and only if transactions for this //! block and all its parents are available. Change to 64-bit type when //! necessary; won't happen before 2030 unsigned int nChainTx; //! Verification status of this block. See enum BlockStatus BlockStatus nStatus; //! block header int32_t nVersion; uint256 hashMerkleRoot; uint32_t nTime; uint32_t nBits; uint32_t nNonce; //! (memory only) Sequential id assigned to distinguish order in which //! blocks are received. int32_t nSequenceId; //! (memory only) block header metadata uint64_t nTimeReceived; //! (memory only) Maximum nTime in the chain up to and including this block. unsigned int nTimeMax; void SetNull() { phashBlock = nullptr; pprev = nullptr; pskip = nullptr; nHeight = 0; nFile = 0; nDataPos = 0; nUndoPos = 0; nChainWork = arith_uint256(); nTx = 0; nChainTx = 0; nStatus = BlockStatus(); nSequenceId = 0; nTimeMax = 0; nVersion = 0; hashMerkleRoot = uint256(); nTime = 0; nTimeReceived = 0; nBits = 0; nNonce = 0; } CBlockIndex() { SetNull(); } explicit CBlockIndex(const CBlockHeader &block) { SetNull(); nVersion = block.nVersion; hashMerkleRoot = block.hashMerkleRoot; nTime = block.nTime; nTimeReceived = 0; nBits = block.nBits; nNonce = block.nNonce; } - CDiskBlockPos GetBlockPos() const { - CDiskBlockPos ret; + FlatFilePos GetBlockPos() const { + FlatFilePos ret; if (nStatus.hasData()) { ret.nFile = nFile; ret.nPos = nDataPos; } return ret; } - CDiskBlockPos GetUndoPos() const { - CDiskBlockPos ret; + FlatFilePos GetUndoPos() const { + FlatFilePos ret; if (nStatus.hasUndo()) { ret.nFile = nFile; ret.nPos = nUndoPos; } return ret; } CBlockHeader GetBlockHeader() const { CBlockHeader block; block.nVersion = nVersion; if (pprev) { block.hashPrevBlock = pprev->GetBlockHash(); } block.hashMerkleRoot = hashMerkleRoot; block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; return block; } uint256 GetBlockHash() const { return *phashBlock; } int64_t GetBlockTime() const { return int64_t(nTime); } int64_t GetBlockTimeMax() const { return int64_t(nTimeMax); } int64_t GetHeaderReceivedTime() const { return nTimeReceived; } int64_t GetReceivedTimeDiff() const { return GetHeaderReceivedTime() - GetBlockTime(); } static constexpr int nMedianTimeSpan = 11; int64_t GetMedianTimePast() const { int64_t pmedian[nMedianTimeSpan]; int64_t *pbegin = &pmedian[nMedianTimeSpan]; int64_t *pend = &pmedian[nMedianTimeSpan]; const CBlockIndex *pindex = this; for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev) { *(--pbegin) = pindex->GetBlockTime(); } std::sort(pbegin, pend); return pbegin[(pend - pbegin) / 2]; } std::string ToString() const { return strprintf( "CBlockIndex(pprev=%p, nHeight=%d, merkle=%s, hashBlock=%s)", pprev, nHeight, hashMerkleRoot.ToString(), GetBlockHash().ToString()); } //! Check whether this block index entry is valid up to the passed validity //! level. bool IsValid(enum BlockValidity nUpTo = BlockValidity::TRANSACTIONS) const { return nStatus.isValid(nUpTo); } //! Raise the validity level of this block index entry. //! Returns true if the validity was changed. bool RaiseValidity(enum BlockValidity nUpTo) { // Only validity flags allowed. if (nStatus.isInvalid()) { return false; } if (nStatus.getValidity() >= nUpTo) { return false; } nStatus = nStatus.withValidity(nUpTo); return true; } //! Build the skiplist pointer for this entry. void BuildSkip(); //! Efficiently find an ancestor of this block. CBlockIndex *GetAncestor(int height); const CBlockIndex *GetAncestor(int height) const; }; /** * Maintain a map of CBlockIndex for all known headers. */ struct BlockHasher { size_t operator()(const uint256 &hash) const { return hash.GetCheapHash(); } }; typedef std::unordered_map BlockMap; extern BlockMap &mapBlockIndex; extern CCriticalSection cs_main; inline CBlockIndex *LookupBlockIndex(const uint256 &hash) { AssertLockHeld(cs_main); BlockMap::const_iterator it = mapBlockIndex.find(hash); return it == mapBlockIndex.end() ? nullptr : it->second; } arith_uint256 GetBlockProof(const CBlockIndex &block); /** * Return the time it would take to redo the work difference between from and * to, assuming the current hashrate corresponds to the difficulty at tip, in * seconds. */ int64_t GetBlockProofEquivalentTime(const CBlockIndex &to, const CBlockIndex &from, const CBlockIndex &tip, const Consensus::Params &); /** * Find the forking point between two chain tips. */ const CBlockIndex *LastCommonAncestor(const CBlockIndex *pa, const CBlockIndex *pb); /** * Check if two block index are on the same fork. */ bool AreOnTheSameFork(const CBlockIndex *pa, const CBlockIndex *pb); /** Used to marshal pointers into hashes for db storage. */ class CDiskBlockIndex : public CBlockIndex { public: uint256 hashPrev; CDiskBlockIndex() { hashPrev = uint256(); } explicit CDiskBlockIndex(const CBlockIndex *pindex) : CBlockIndex(*pindex) { hashPrev = (pprev ? pprev->GetBlockHash() : uint256()); } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { int _nVersion = s.GetVersion(); if (!(s.GetType() & SER_GETHASH)) { READWRITE(VARINT(_nVersion)); } READWRITE(VARINT(nHeight)); READWRITE(nStatus); READWRITE(VARINT(nTx)); if (nStatus.hasData() || nStatus.hasUndo()) { READWRITE(VARINT(nFile)); } if (nStatus.hasData()) { READWRITE(VARINT(nDataPos)); } if (nStatus.hasUndo()) { READWRITE(VARINT(nUndoPos)); } // block header READWRITE(this->nVersion); READWRITE(hashPrev); READWRITE(hashMerkleRoot); READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); } uint256 GetBlockHash() const { CBlockHeader block; block.nVersion = nVersion; block.hashPrevBlock = hashPrev; block.hashMerkleRoot = hashMerkleRoot; block.nTime = nTime; block.nBits = nBits; block.nNonce = nNonce; return block.GetHash(); } std::string ToString() const { std::string str = "CDiskBlockIndex("; str += CBlockIndex::ToString(); str += strprintf("\n hashBlock=%s, hashPrev=%s)", GetBlockHash().ToString(), hashPrev.ToString()); return str; } }; /** * An in-memory indexed chain of blocks. */ class CChain { private: std::vector vChain; public: /** * Returns the index entry for the genesis block of this chain, or nullptr * if none. */ CBlockIndex *Genesis() const { return vChain.size() > 0 ? vChain[0] : nullptr; } /** * Returns the index entry for the tip of this chain, or nullptr if none. */ CBlockIndex *Tip() const { return vChain.size() > 0 ? vChain[vChain.size() - 1] : nullptr; } /** * Returns the index entry at a particular height in this chain, or nullptr * if no such height exists. */ CBlockIndex *operator[](int nHeight) const { if (nHeight < 0 || nHeight >= (int)vChain.size()) { return nullptr; } return vChain[nHeight]; } /** Compare two chains efficiently. */ friend bool operator==(const CChain &a, const CChain &b) { return a.vChain.size() == b.vChain.size() && a.vChain[a.vChain.size() - 1] == b.vChain[b.vChain.size() - 1]; } /** Efficiently check whether a block is present in this chain. */ bool Contains(const CBlockIndex *pindex) const { return (*this)[pindex->nHeight] == pindex; } /** * Find the successor of a block in this chain, or nullptr if the given * index is not found or is the tip. */ CBlockIndex *Next(const CBlockIndex *pindex) const { if (!Contains(pindex)) { return nullptr; } return (*this)[pindex->nHeight + 1]; } /** * Return the maximal height in the chain. Is equal to chain.Tip() ? * chain.Tip()->nHeight : -1. */ int Height() const { return vChain.size() - 1; } /** Set/initialize a chain with a given tip. */ void SetTip(CBlockIndex *pindex); /** * Return a CBlockLocator that refers to a block in this chain (by default * the tip). */ CBlockLocator GetLocator(const CBlockIndex *pindex = nullptr) const; /** * Find the last common block between this chain and a block index entry. */ const CBlockIndex *FindFork(const CBlockIndex *pindex) const; /** * Find the earliest block with timestamp equal or greater than the given. */ CBlockIndex *FindEarliestAtLeast(int64_t nTime) const; }; #endif // BITCOIN_CHAIN_H diff --git a/src/flatfile.cpp b/src/flatfile.cpp index fbce95729..0cd932dfc 100644 --- a/src/flatfile.cpp +++ b/src/flatfile.cpp @@ -1,98 +1,98 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2019 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 #include #include #include #include FlatFileSeq::FlatFileSeq(fs::path dir, const char *prefix, size_t chunk_size) : m_dir(std::move(dir)), m_prefix(prefix), m_chunk_size(chunk_size) { if (chunk_size == 0) { throw std::invalid_argument("chunk_size must be positive"); } } -std::string CDiskBlockPos::ToString() const { - return strprintf("CDiskBlockPos(nFile=%i, nPos=%i)", nFile, nPos); +std::string FlatFilePos::ToString() const { + return strprintf("FlatFilePos(nFile=%i, nPos=%i)", nFile, nPos); } -fs::path FlatFileSeq::FileName(const CDiskBlockPos &pos) const { +fs::path FlatFileSeq::FileName(const FlatFilePos &pos) const { return m_dir / strprintf("%s%05u.dat", m_prefix, pos.nFile); } -FILE *FlatFileSeq::Open(const CDiskBlockPos &pos, bool fReadOnly) { +FILE *FlatFileSeq::Open(const FlatFilePos &pos, bool fReadOnly) { if (pos.IsNull()) { return nullptr; } fs::path path = FileName(pos); fs::create_directories(path.parent_path()); FILE *file = fsbridge::fopen(path, fReadOnly ? "rb" : "rb+"); if (!file && !fReadOnly) { file = fsbridge::fopen(path, "wb+"); } if (!file) { LogPrintf("Unable to open file %s\n", path.string()); return nullptr; } if (pos.nPos) { if (fseek(file, pos.nPos, SEEK_SET)) { LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); fclose(file); return nullptr; } } return file; } -size_t FlatFileSeq::Allocate(const CDiskBlockPos &pos, size_t add_size, +size_t FlatFileSeq::Allocate(const FlatFilePos &pos, size_t add_size, bool &out_of_space) { out_of_space = false; unsigned int n_old_chunks = (pos.nPos + m_chunk_size - 1) / m_chunk_size; unsigned int n_new_chunks = (pos.nPos + add_size + m_chunk_size - 1) / m_chunk_size; if (n_new_chunks > n_old_chunks) { size_t old_size = pos.nPos; size_t new_size = n_new_chunks * m_chunk_size; size_t inc_size = new_size - old_size; if (CheckDiskSpace(m_dir, inc_size)) { FILE *file = Open(pos); if (file) { LogPrintf("Pre-allocating up to position 0x%x in %s%05u.dat\n", new_size, m_prefix, pos.nFile); AllocateFileRange(file, pos.nPos, inc_size); fclose(file); return inc_size; } } else { out_of_space = true; } } return 0; } -bool FlatFileSeq::Flush(const CDiskBlockPos &pos, bool finalize) { +bool FlatFileSeq::Flush(const FlatFilePos &pos, bool finalize) { // Avoid fseek to nPos - FILE *file = Open(CDiskBlockPos(pos.nFile, 0)); + FILE *file = Open(FlatFilePos(pos.nFile, 0)); if (!file) { return error("%s: failed to open file %d", __func__, pos.nFile); } if (finalize && !TruncateFile(file, pos.nPos)) { fclose(file); return error("%s: failed to truncate file %d", __func__, pos.nFile); } if (!FileCommit(file)) { fclose(file); return error("%s: failed to commit file %d", __func__, pos.nFile); } fclose(file); return true; } diff --git a/src/flatfile.h b/src/flatfile.h index c276dfc96..9c3147600 100644 --- a/src/flatfile.h +++ b/src/flatfile.h @@ -1,102 +1,102 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2019 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_FLATFILE_H #define BITCOIN_FLATFILE_H #include #include #include -struct CDiskBlockPos { +struct FlatFilePos { int nFile; unsigned int nPos; ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { READWRITE(VARINT(nFile)); READWRITE(VARINT(nPos)); } - CDiskBlockPos() { SetNull(); } + FlatFilePos() { SetNull(); } - CDiskBlockPos(int nFileIn, unsigned int nPosIn) { + FlatFilePos(int nFileIn, unsigned int nPosIn) { nFile = nFileIn; nPos = nPosIn; } - friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) { + friend bool operator==(const FlatFilePos &a, const FlatFilePos &b) { return (a.nFile == b.nFile && a.nPos == b.nPos); } - friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) { + friend bool operator!=(const FlatFilePos &a, const FlatFilePos &b) { return !(a == b); } void SetNull() { nFile = -1; nPos = 0; } bool IsNull() const { return (nFile == -1); } std::string ToString() const; }; /** * FlatFileSeq represents a sequence of numbered files storing raw data. This * class facilitates access to and efficient management of these files. */ class FlatFileSeq { private: const fs::path m_dir; const char *const m_prefix; const size_t m_chunk_size; public: /** * Constructor * * @param dir The base directory that all files live in. * @param prefix A short prefix given to all file names. * @param chunk_size Disk space is pre-allocated in multiples of this * amount. */ FlatFileSeq(fs::path dir, const char *prefix, size_t chunk_size); /** Get the name of the file at the given position. */ - fs::path FileName(const CDiskBlockPos &pos) const; + fs::path FileName(const FlatFilePos &pos) const; /** Open a handle to the file at the given position. */ - FILE *Open(const CDiskBlockPos &pos, bool fReadOnly = false); + FILE *Open(const FlatFilePos &pos, bool fReadOnly = false); /** * Allocate additional space in a file after the given starting position. * The amount allocated will be the minimum multiple of the sequence chunk * size greater than add_size. * * @param[in] pos The starting position that bytes will be allocated after. * @param[in] add_size The minimum number of bytes to be allocated. * @param[out] out_of_space Whether the allocation failed due to * insufficient disk space. * @return The number of bytes successfully allocated. */ - size_t Allocate(const CDiskBlockPos &pos, size_t add_size, + size_t Allocate(const FlatFilePos &pos, size_t add_size, bool &out_of_space); /** * Commit a file to disk, and optionally truncate off extra pre-allocated * bytes if final. * * @param[in] pos The first unwritten position in the file to be flushed. * @param[in] finalize True if no more data will be written to this file. * @return true on success, false on failure. */ - bool Flush(const CDiskBlockPos &pos, bool finalize = false); + bool Flush(const FlatFilePos &pos, bool finalize = false); }; #endif // BITCOIN_FLATFILE_H diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index 913947875..10b12247a 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -1,293 +1,293 @@ // Copyright (c) 2017-2018 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 #include #include #include #include #include #include constexpr char DB_BEST_BLOCK = 'B'; constexpr char DB_TXINDEX = 't'; constexpr char DB_TXINDEX_BLOCK = 'T'; std::unique_ptr g_txindex; -struct CDiskTxPos : public CDiskBlockPos { +struct CDiskTxPos : public FlatFilePos { unsigned int nTxOffset; // after header ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream &s, Operation ser_action) { - READWRITEAS(CDiskBlockPos, *this); + READWRITEAS(FlatFilePos, *this); READWRITE(VARINT(nTxOffset)); } - CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) - : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {} + CDiskTxPos(const FlatFilePos &blockIn, unsigned int nTxOffsetIn) + : FlatFilePos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {} CDiskTxPos() { SetNull(); } void SetNull() { - CDiskBlockPos::SetNull(); + FlatFilePos::SetNull(); nTxOffset = 0; } }; /** * Access to the txindex database (indexes/txindex/) * * The database stores a block locator of the chain the database is synced to * so that the TxIndex can efficiently determine the point it last stopped at. * A locator is used instead of a simple hash of the chain tip because blocks * and block index entries may not be flushed to disk until after this database * is updated. */ class TxIndex::DB : public BaseIndex::DB { public: explicit DB(size_t n_cache_size, bool f_memory = false, bool f_wipe = false); /// Read the disk location of the transaction data with the given ID. /// Returns false if the transaction ID is not indexed. bool ReadTxPos(const TxId &txid, CDiskTxPos &pos) const; /// Write a batch of transaction positions to the DB. bool WriteTxs(const std::vector> &v_pos); /// Migrate txindex data from the block tree DB, where it may be for older /// nodes that have not been upgraded yet to the new database. bool MigrateData(CBlockTreeDB &block_tree_db, const CBlockLocator &best_locator); }; TxIndex::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) : BaseIndex::DB(GetDataDir() / "indexes" / "txindex", n_cache_size, f_memory, f_wipe) {} bool TxIndex::DB::ReadTxPos(const TxId &txid, CDiskTxPos &pos) const { return Read(std::make_pair(DB_TXINDEX, txid), pos); } bool TxIndex::DB::WriteTxs( const std::vector> &v_pos) { CDBBatch batch(*this); for (const auto &tuple : v_pos) { batch.Write(std::make_pair(DB_TXINDEX, tuple.first), tuple.second); } return WriteBatch(batch); } /* * Safely persist a transfer of data from the old txindex database to the new * one, and compact the range of keys updated. This is used internally by * MigrateData. */ static void WriteTxIndexMigrationBatches(CDBWrapper &newdb, CDBWrapper &olddb, CDBBatch &batch_newdb, CDBBatch &batch_olddb, const std::pair &begin_key, const std::pair &end_key) { // Sync new DB changes to disk before deleting from old DB. newdb.WriteBatch(batch_newdb, /*fSync=*/true); olddb.WriteBatch(batch_olddb); olddb.CompactRange(begin_key, end_key); batch_newdb.Clear(); batch_olddb.Clear(); } bool TxIndex::DB::MigrateData(CBlockTreeDB &block_tree_db, const CBlockLocator &best_locator) { // The prior implementation of txindex was always in sync with block index // and presence was indicated with a boolean DB flag. If the flag is set, // this means the txindex from a previous version is valid and in sync with // the chain tip. The first step of the migration is to unset the flag and // write the chain hash to a separate key, DB_TXINDEX_BLOCK. After that, the // index entries are copied over in batches to the new database. Finally, // DB_TXINDEX_BLOCK is erased from the old database and the block hash is // written to the new database. // // Unsetting the boolean flag ensures that if the node is downgraded to a // previous version, it will not see a corrupted, partially migrated index // -- it will see that the txindex is disabled. When the node is upgraded // again, the migration will pick up where it left off and sync to the block // with hash DB_TXINDEX_BLOCK. bool f_legacy_flag = false; block_tree_db.ReadFlag("txindex", f_legacy_flag); if (f_legacy_flag) { if (!block_tree_db.Write(DB_TXINDEX_BLOCK, best_locator)) { return error("%s: cannot write block indicator", __func__); } if (!block_tree_db.WriteFlag("txindex", false)) { return error("%s: cannot write block index db flag", __func__); } } CBlockLocator locator; if (!block_tree_db.Read(DB_TXINDEX_BLOCK, locator)) { return true; } int64_t count = 0; uiInterface.InitMessage(_("Upgrading txindex database")); LogPrintf("Upgrading txindex database... [0%%]\n"); uiInterface.ShowProgress(_("Upgrading txindex database"), 0, true); int report_done = 0; const size_t batch_size = 1 << 24; // 16 MiB CDBBatch batch_newdb(*this); CDBBatch batch_olddb(block_tree_db); std::pair key; std::pair begin_key{DB_TXINDEX, uint256()}; std::pair prev_key = begin_key; bool interrupted = false; std::unique_ptr cursor(block_tree_db.NewIterator()); for (cursor->Seek(begin_key); cursor->Valid(); cursor->Next()) { boost::this_thread::interruption_point(); if (ShutdownRequested()) { interrupted = true; break; } if (!cursor->GetKey(key)) { return error("%s: cannot get key from valid cursor", __func__); } if (key.first != DB_TXINDEX) { break; } // Log progress every 10%. if (++count % 256 == 0) { // Since txids are uniformly random and traversed in increasing // order, the high 16 bits of the ID can be used to estimate the // current progress. const uint256 &txid = key.second; uint32_t high_nibble = (static_cast(*(txid.begin() + 0)) << 8) + (static_cast(*(txid.begin() + 1)) << 0); int percentage_done = (int)(high_nibble * 100.0 / 65536.0 + 0.5); uiInterface.ShowProgress(_("Upgrading txindex database"), percentage_done, true); if (report_done < percentage_done / 10) { LogPrintf("Upgrading txindex database... [%d%%]\n", percentage_done); report_done = percentage_done / 10; } } CDiskTxPos value; if (!cursor->GetValue(value)) { return error("%s: cannot parse txindex record", __func__); } batch_newdb.Write(key, value); batch_olddb.Erase(key); if (batch_newdb.SizeEstimate() > batch_size || batch_olddb.SizeEstimate() > batch_size) { // NOTE: it's OK to delete the key pointed at by the current DB // cursor while iterating because LevelDB iterators are guaranteed // to provide a consistent view of the underlying data, like a // lightweight snapshot. WriteTxIndexMigrationBatches(*this, block_tree_db, batch_newdb, batch_olddb, prev_key, key); prev_key = key; } } // If these final DB batches complete the migration, write the best block // hash marker to the new database and delete from the old one. This signals // that the former is fully caught up to that point in the blockchain and // that all txindex entries have been removed from the latter. if (!interrupted) { batch_olddb.Erase(DB_TXINDEX_BLOCK); batch_newdb.Write(DB_BEST_BLOCK, locator); } WriteTxIndexMigrationBatches(*this, block_tree_db, batch_newdb, batch_olddb, begin_key, key); if (interrupted) { LogPrintf("[CANCELLED].\n"); return false; } uiInterface.ShowProgress("", 100, false); LogPrintf("[DONE].\n"); return true; } TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe) : m_db(std::make_unique(n_cache_size, f_memory, f_wipe)) {} TxIndex::~TxIndex() {} bool TxIndex::Init() { LOCK(cs_main); // Attempt to migrate txindex from the old database to the new one. Even if // chain_tip is null, the node could be reindexing and we still want to // delete txindex records in the old database. if (!m_db->MigrateData(*pblocktree, chainActive.GetLocator())) { return false; } return BaseIndex::Init(); } bool TxIndex::WriteBlock(const CBlock &block, const CBlockIndex *pindex) { // Exclude genesis block transaction because outputs are not spendable. if (pindex->nHeight == 0) { return true; } CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector> vPos; vPos.reserve(block.vtx.size()); for (const auto &tx : block.vtx) { vPos.emplace_back(tx->GetId(), pos); pos.nTxOffset += ::GetSerializeSize(*tx, SER_DISK, CLIENT_VERSION); } return m_db->WriteTxs(vPos); } BaseIndex::DB &TxIndex::GetDB() const { return *m_db; } bool TxIndex::FindTx(const TxId &txid, uint256 &block_hash, CTransactionRef &tx) const { CDiskTxPos postx; if (!m_db->ReadTxPos(txid, postx)) { return false; } CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); if (file.IsNull()) { return error("%s: OpenBlockFile failed", __func__); } CBlockHeader header; try { file >> header; fseek(file.Get(), postx.nTxOffset, SEEK_CUR); file >> tx; } catch (const std::exception &e) { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } if (tx->GetId() != txid) { return error("%s: txid mismatch", __func__); } block_hash = header.GetHash(); return true; } diff --git a/src/init.cpp b/src/init.cpp index 301fc6f0b..1a876a94a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1,2488 +1,2488 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include