move txindex back to its own thread
This commit is contained in:
parent
10a898607f
commit
3fd448e552
16 changed files with 292 additions and 95 deletions
|
@ -139,6 +139,7 @@ BITCOIN_CORE_H = \
|
|||
httpserver.h \
|
||||
index/base.h \
|
||||
index/blockfilterindex.h \
|
||||
index/txindex.h \
|
||||
indirectmap.h \
|
||||
init.h \
|
||||
interfaces/chain.h \
|
||||
|
@ -279,6 +280,7 @@ libbitcoin_server_a_SOURCES = \
|
|||
httpserver.cpp \
|
||||
index/base.cpp \
|
||||
index/blockfilterindex.cpp \
|
||||
index/txindex.cpp \
|
||||
interfaces/chain.cpp \
|
||||
interfaces/node.cpp \
|
||||
init.cpp \
|
||||
|
|
|
@ -149,6 +149,7 @@ BITCOIN_TESTS =\
|
|||
test/timedata_tests.cpp \
|
||||
test/torcontrol_tests.cpp \
|
||||
test/transaction_tests.cpp \
|
||||
test/txindex_tests.cpp \
|
||||
test/txvalidation_tests.cpp \
|
||||
test/txvalidationcache_tests.cpp \
|
||||
test/uint256_tests.cpp \
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <validation.h>
|
||||
#include <warnings.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
constexpr int64_t SYNC_LOG_INTERVAL = 30; // seconds
|
||||
constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
|
||||
|
||||
|
@ -105,6 +107,7 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev) EXCLUSIV
|
|||
|
||||
void BaseIndex::ThreadSync()
|
||||
{
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
const CBlockIndex* pindex = m_best_block_index.load();
|
||||
if (!m_synced) {
|
||||
auto& consensus_params = Params().GetConsensus();
|
||||
|
@ -172,10 +175,11 @@ void BaseIndex::ThreadSync()
|
|||
}
|
||||
}
|
||||
|
||||
auto done = 1e-9 * std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - start).count();
|
||||
if (pindex) {
|
||||
LogPrintf("%s is enabled at height %d\n", GetName(), pindex->nHeight);
|
||||
LogPrintf("%s is enabled at height %d. Indexing time: %f sec.\n", GetName(), pindex->nHeight, done);
|
||||
} else {
|
||||
LogPrintf("%s is enabled\n", GetName());
|
||||
LogPrintf("%s is enabled. Indexing time: %f sec.\n", GetName(), done);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +213,7 @@ bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_ti
|
|||
|
||||
// In the case of a reorg, ensure persisted block locator is not stale.
|
||||
m_best_block_index = new_tip;
|
||||
if (!Commit()) {
|
||||
if (!CommitInternal()) {
|
||||
// If commit fails, revert the best block index to avoid corruption.
|
||||
m_best_block_index = current_tip;
|
||||
return false;
|
||||
|
|
|
@ -60,7 +60,7 @@ BlockFilterIndex::BlockFilterIndex(BlockFilterType filter_type,
|
|||
(*m_db) << "CREATE TABLE IF NOT EXISTS block (height INTEGER, hash BLOB NOT NULL COLLATE BINARY, "
|
||||
"filter_hash BLOB NOT NULL COLLATE BINARY, header BLOB NOT NULL COLLATE BINARY, "
|
||||
"file INTEGER NOT NULL, pos INTEGER NOT NULL, "
|
||||
"PRIMARY KEY(height, hash), UNIQUE(filter_hash, header, file, pos));";
|
||||
"PRIMARY KEY(height, hash));";
|
||||
|
||||
(*m_db) << "CREATE TABLE IF NOT EXISTS file_pos (file INTEGER NOT NULL, pos INTEGER NOT NULL);";
|
||||
|
||||
|
@ -214,7 +214,7 @@ bool BlockFilterIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex
|
|||
|
||||
const auto filterHash = filter.GetHash(); // trying to avoid temps
|
||||
const auto filterHeader = filter.ComputeHeader(prev_header);
|
||||
(*m_db) << "INSERT INTO block VALUES(?, ?, ?, ?, ?, ?)"
|
||||
(*m_db) << "INSERT OR REPLACE INTO block VALUES(?, ?, ?, ?, ?, ?)"
|
||||
<< pindex->nHeight
|
||||
<< pindex->hash
|
||||
<< filterHash
|
||||
|
|
85
src/index/txindex.cpp
Normal file
85
src/index/txindex.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2017-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 <clientversion.h>
|
||||
#include <index/txindex.h>
|
||||
#include <streams.h>
|
||||
#include <ui_interface.h>
|
||||
#include <util/system.h>
|
||||
#include <validation.h>
|
||||
|
||||
std::unique_ptr<TxIndex> g_txindex;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
TxIndex::TxIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
|
||||
: m_db(MakeUnique<BaseIndex::DB>(GetDataDir() / "txindex", n_cache_size, f_memory, f_wipe))
|
||||
{
|
||||
(*m_db) << "CREATE TABLE IF NOT EXISTS tx_to_block ("
|
||||
"txID BLOB NOT NULL PRIMARY KEY, "
|
||||
"file INTEGER NOT NULL, "
|
||||
"blockPos INTEGER NOT NULL, "
|
||||
"txPos INTEGER NOT NULL)";
|
||||
|
||||
if (f_wipe)
|
||||
(*m_db) << "DELETE FROM tx_to_block";
|
||||
}
|
||||
|
||||
bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
||||
{
|
||||
// Exclude genesis block transaction because outputs are not spendable.
|
||||
if (pindex->nHeight == 0) return true;
|
||||
|
||||
// transaction is begin and commited in the caller of this method
|
||||
auto query = (*m_db) << "INSERT OR REPLACE INTO tx_to_block VALUES(?,?,?,?)";
|
||||
auto pos = pindex->GetBlockPos();
|
||||
std::size_t offset = ::GetSizeOfCompactSize(block.vtx.size());
|
||||
for (const auto& tx : block.vtx) {
|
||||
query << tx->GetHash() << pos.nFile << pos.nPos << offset;
|
||||
query++;
|
||||
offset += ::GetSerializeSize(*tx, CLIENT_VERSION);
|
||||
}
|
||||
query.used(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const
|
||||
{
|
||||
FlatFilePos postx;
|
||||
long txPos = 0;
|
||||
{
|
||||
auto query = (*m_db) << "SELECT file, blockPos, txPos FROM tx_to_block WHERE txID = ?" << tx_hash;
|
||||
auto it = query.begin();
|
||||
if (it == query.end())
|
||||
return false;
|
||||
*it >> postx.nFile >> postx.nPos >> txPos;
|
||||
}
|
||||
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
||||
if (file.IsNull()) {
|
||||
return error("%s: OpenBlockFile failed", __func__);
|
||||
}
|
||||
CBlockHeader header;
|
||||
try {
|
||||
file >> header;
|
||||
if (txPos > 0 && fseek(file.Get(), txPos, SEEK_CUR)) {
|
||||
return error("%s: fseek(...) failed", __func__);
|
||||
}
|
||||
file >> tx;
|
||||
} catch (const std::exception& e) {
|
||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
||||
}
|
||||
if (tx->GetHash() != tx_hash) {
|
||||
return error("%s: txid mismatch", __func__);
|
||||
}
|
||||
block_hash = header.GetHash();
|
||||
return true;
|
||||
}
|
47
src/index/txindex.h
Normal file
47
src/index/txindex.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
// 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.
|
||||
|
||||
#ifndef BITCOIN_INDEX_TXINDEX_H
|
||||
#define BITCOIN_INDEX_TXINDEX_H
|
||||
|
||||
#include <chain.h>
|
||||
#include <index/base.h>
|
||||
#include <txdb.h>
|
||||
|
||||
/**
|
||||
* TxIndex is used to look up transactions included in the blockchain by hash.
|
||||
* The index is written to a LevelDB database and records the filesystem
|
||||
* location of each transaction by transaction hash.
|
||||
*/
|
||||
class TxIndex final : public BaseIndex
|
||||
{
|
||||
const std::unique_ptr<BaseIndex::DB> m_db;
|
||||
|
||||
protected:
|
||||
bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) override;
|
||||
|
||||
BaseIndex::DB& GetDB() const override { return *m_db; }
|
||||
|
||||
const char* GetName() const override { return "txindex"; }
|
||||
|
||||
public:
|
||||
/// Constructs the index, which becomes available to be queried.
|
||||
explicit TxIndex(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
|
||||
|
||||
// Destructor is declared because this class contains a unique_ptr to an incomplete type.
|
||||
virtual ~TxIndex() override = default;
|
||||
|
||||
/// Look up a transaction by hash.
|
||||
///
|
||||
/// @param[in] tx_hash The hash of the transaction to be returned.
|
||||
/// @param[out] block_hash The hash of the block the transaction is found in.
|
||||
/// @param[out] tx The transaction itself.
|
||||
/// @return true if transaction is found, false otherwise
|
||||
bool FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const;
|
||||
};
|
||||
|
||||
/// The global transaction index, used in GetTransaction. May be null.
|
||||
extern std::unique_ptr<TxIndex> g_txindex;
|
||||
|
||||
#endif // BITCOIN_INDEX_TXINDEX_H
|
30
src/init.cpp
30
src/init.cpp
|
@ -24,6 +24,7 @@
|
|||
#include <httprpc.h>
|
||||
#include <httpserver.h>
|
||||
#include <index/blockfilterindex.h>
|
||||
#include <index/txindex.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <key.h>
|
||||
#include <lbry.h>
|
||||
|
@ -168,6 +169,8 @@ void Interrupt()
|
|||
InterruptMapPort();
|
||||
if (g_connman)
|
||||
g_connman->Interrupt();
|
||||
if (g_txindex)
|
||||
g_txindex->Interrupt();
|
||||
ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Interrupt(); });
|
||||
}
|
||||
|
||||
|
@ -245,7 +248,10 @@ void Shutdown(InitInterfaces& interfaces)
|
|||
GetMainSignals().FlushBackgroundCallbacks();
|
||||
|
||||
// Stop and delete all indexes only after flushing background callbacks.
|
||||
ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Stop(); });
|
||||
if (g_txindex) {
|
||||
g_txindex->Stop();
|
||||
g_txindex.reset();
|
||||
}ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Stop(); });
|
||||
DestroyAllBlockFilterIndexes();
|
||||
|
||||
// Any future callbacks will be dropped. This should absolutely be safe - if
|
||||
|
@ -385,7 +391,7 @@ void SetupServerArgs()
|
|||
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -rescan. "
|
||||
gArgs.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -rescan and -txindex. "
|
||||
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
|
||||
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
|
@ -395,6 +401,7 @@ void SetupServerArgs()
|
|||
#else
|
||||
hidden_args.emplace_back("-sysperms");
|
||||
#endif
|
||||
gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-blockfilterindex=<type>",
|
||||
strprintf("Maintain an index of compact filters by block (default: %s, values: %s).", DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
|
||||
" If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.",
|
||||
|
@ -856,6 +863,9 @@ void InitLogging()
|
|||
version_string += " (release build)";
|
||||
#endif
|
||||
LogPrintf(PACKAGE_NAME " version %s\n", version_string);
|
||||
|
||||
if (LogInstance().Enabled() && LogAcceptCategory(BCLog::CLAIMS))
|
||||
CLogPrint::global().setLogger(&LogInstance());
|
||||
}
|
||||
|
||||
namespace { // Variables internal to initialization process only
|
||||
|
@ -963,6 +973,8 @@ bool AppInitParameterInteraction()
|
|||
}
|
||||
|
||||
if (gArgs.GetArg("-prune", 0)) {
|
||||
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
|
||||
return InitError(_("Prune mode is incompatible with -txindex.").translated);
|
||||
if (!g_enabled_filter_types.empty()) {
|
||||
return InitError(_("Prune mode is incompatible with -blockfilterindex.").translated);
|
||||
}
|
||||
|
@ -1254,9 +1266,6 @@ bool AppInitMain(InitInterfaces& interfaces)
|
|||
}
|
||||
);
|
||||
|
||||
if (LogInstance().Enabled() && LogAcceptCategory(BCLog::CLAIMS))
|
||||
CLogPrint::global().setLogger(&LogInstance());
|
||||
|
||||
InitSignatureCache();
|
||||
InitScriptExecutionCache();
|
||||
|
||||
|
@ -1438,17 +1447,19 @@ bool AppInitMain(InitInterfaces& interfaces)
|
|||
// the coin cache, the block cache, the claimtrie cache
|
||||
// however, we want the claimtrie cache to be larger than the others
|
||||
|
||||
int64_t nBlockTreeDBCache = std::min(nTotalCache / 4, nMaxBlockDBCache << 20);
|
||||
int64_t nBlockTreeDBCache = std::min(nTotalCache / 5, nMaxBlockDBCache << 20);
|
||||
int64_t nCoinDBCache = std::min(nTotalCache / 4, nMaxCoinsDBCache << 20);
|
||||
int64_t nClaimtrieCache = ::Claimtrie().cache();
|
||||
nTotalCache -= nBlockTreeDBCache;
|
||||
int64_t nTxIndexCache = std::min(nTotalCache / 4, gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? int64_t(1 << 24) : 0);
|
||||
int64_t filter_index_cache = 0;
|
||||
if (!g_enabled_filter_types.empty()) {
|
||||
size_t n_indexes = g_enabled_filter_types.size();
|
||||
int64_t max_cache = std::min(nTotalCache / 8, max_filter_index_cache << 20);
|
||||
int64_t max_cache = std::min(nTotalCache / 4, max_filter_index_cache << 20);
|
||||
filter_index_cache = max_cache / n_indexes;
|
||||
nTotalCache -= filter_index_cache * n_indexes;
|
||||
}
|
||||
|
||||
nTotalCache -= nCoinDBCache;
|
||||
nTotalCache -= nClaimtrieCache;
|
||||
nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
|
||||
|
@ -1458,6 +1469,8 @@ bool AppInitMain(InitInterfaces& interfaces)
|
|||
LogPrintf("* Using %.1fMiB for block index database cache\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
|
||||
LogPrintf("* Using %.1fMiB for chain state database cache\n", nCoinDBCache * (1.0 / 1024 / 1024));
|
||||
LogPrintf("* Using %.1fMiB for claimtrie database cache\n", nClaimtrieCache * (1.0 / 1024 / 1024));
|
||||
if (nTxIndexCache)
|
||||
LogPrintf("* Using %.1fMiB for txindex database cache\n", nTxIndexCache * (1.0 / 1024 / 1024));
|
||||
for (BlockFilterType filter_type : g_enabled_filter_types) {
|
||||
LogPrintf("* Using %.1f MiB for %s block filter index database\n",
|
||||
filter_index_cache * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
|
||||
|
@ -1671,7 +1684,8 @@ bool AppInitMain(InitInterfaces& interfaces)
|
|||
|
||||
// ********************************************************* Step 8: start indexers
|
||||
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
||||
LogPrintf("The txindex parameter is no longer necessary. It is always on.\n");
|
||||
g_txindex = MakeUnique<TxIndex>(nTxIndexCache, false, fReindex);
|
||||
g_txindex->Start();
|
||||
}
|
||||
|
||||
for (const auto& filter_type : g_enabled_filter_types) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <chain.h>
|
||||
#include <chainparams.h>
|
||||
#include <core_io.h>
|
||||
#include <index/txindex.h>
|
||||
#include <httpserver.h>
|
||||
#include <primitives/block.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
@ -358,6 +359,10 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
|
|||
if (!ParseHashStr(hashStr, hash))
|
||||
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
|
||||
|
||||
if (g_txindex) {
|
||||
g_txindex->BlockUntilSyncedToCurrentChain();
|
||||
}
|
||||
|
||||
CTransactionRef tx;
|
||||
uint256 hashBlock = uint256();
|
||||
if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock))
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <compat/byteswap.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <core_io.h>
|
||||
#include <index/txindex.h>
|
||||
#include <key_io.h>
|
||||
#include <merkleblock.h>
|
||||
#include <node/coin.h>
|
||||
|
@ -178,6 +179,11 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
|
|||
in_active_chain = ::ChainActive().Contains(blockindex);
|
||||
}
|
||||
|
||||
bool f_txindex_ready = false;
|
||||
if (g_txindex && !blockindex) {
|
||||
f_txindex_ready = g_txindex->BlockUntilSyncedToCurrentChain();
|
||||
}
|
||||
|
||||
CTransactionRef tx;
|
||||
uint256 hash_block;
|
||||
if (!GetTransaction(hash, tx, Params().GetConsensus(), hash_block, blockindex)) {
|
||||
|
@ -187,6 +193,10 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
|
|||
throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
|
||||
}
|
||||
errmsg = "No such transaction found in the provided block";
|
||||
} else if (!g_txindex) {
|
||||
errmsg = "No such mempool transaction. Use -txindex or provide a block hash to enable blockchain transaction queries";
|
||||
} else if (!f_txindex_ready) {
|
||||
errmsg = "No such mempool transaction. Blockchain transactions are still in the process of being indexed";
|
||||
} else {
|
||||
errmsg = "No such mempool or blockchain transaction";
|
||||
}
|
||||
|
@ -259,6 +269,10 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
|
|||
}
|
||||
}
|
||||
|
||||
if (g_txindex && !pblockindex) {
|
||||
g_txindex->BlockUntilSyncedToCurrentChain();
|
||||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
if (pblockindex == nullptr)
|
||||
|
|
|
@ -33,6 +33,11 @@
|
|||
|
||||
#include <functional>
|
||||
|
||||
#ifdef BOOST_TEST_DYN_LINK // do not include in qt
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/unit_test_parameters.hpp>
|
||||
#endif
|
||||
|
||||
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
|
||||
|
||||
FastRandomContext g_insecure_rand_ctx;
|
||||
|
@ -70,10 +75,20 @@ std::ostream& operator<<(std::ostream& os, const CSupportValue& support)
|
|||
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
|
||||
: m_path_root(fs::temp_directory_path() / "test_common_" PACKAGE_TARNAME / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
|
||||
{
|
||||
gArgs.ForceSetArg("-printtoconsole", "0");
|
||||
#ifdef BOOST_TEST_DYN_LINK // do not include in qt
|
||||
// for debugging:
|
||||
if (boost::unit_test::runtime_config::get<boost::unit_test::log_level>(boost::unit_test::runtime_config::btrt_log_level)
|
||||
<= boost::unit_test::log_level::log_messages) {
|
||||
gArgs.ForceSetArg("-printtoconsole", "1");
|
||||
gArgs.ForceSetArg("-logtimemicros", "1");
|
||||
LogInstance().EnableCategory(BCLog::ALL);
|
||||
}
|
||||
#endif
|
||||
|
||||
gArgs.ForceSetArg("-datadir", m_path_root.string());
|
||||
ClearDatadirCache();
|
||||
SelectParams(chainName);
|
||||
gArgs.ForceSetArg("-printtoconsole", "0");
|
||||
InitLogging();
|
||||
LogInstance().StartLogging();
|
||||
SHA256AutoDetect();
|
||||
|
|
76
src/test/txindex_tests.cpp
Normal file
76
src/test/txindex_tests.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) 2017-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 <chainparams.h>
|
||||
#include <index/txindex.h>
|
||||
#include <script/standard.h>
|
||||
#include <test/setup_common.h>
|
||||
#include <util/time.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(txindex_tests)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup)
|
||||
{
|
||||
TxIndex txindex(1 << 20, true);
|
||||
|
||||
CTransactionRef tx_disk;
|
||||
uint256 block_hash;
|
||||
|
||||
// Transaction should not be found in the index before it is started.
|
||||
for (const auto& txn : m_coinbase_txns) {
|
||||
BOOST_CHECK(!txindex.FindTx(txn.GetHash(), block_hash, tx_disk));
|
||||
}
|
||||
|
||||
// BlockUntilSyncedToCurrentChain should return false before txindex is started.
|
||||
BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
|
||||
|
||||
txindex.Start();
|
||||
|
||||
// Allow tx index to catch up with the block index.
|
||||
constexpr int64_t timeout_ms = 10 * 1000;
|
||||
int64_t time_start = GetTimeMillis();
|
||||
while (!txindex.BlockUntilSyncedToCurrentChain()) {
|
||||
BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
|
||||
MilliSleep(50);
|
||||
}
|
||||
|
||||
// Check that txindex excludes genesis block transactions.
|
||||
const CBlock& genesis_block = Params().GenesisBlock();
|
||||
for (const auto& txn : genesis_block.vtx) {
|
||||
BOOST_CHECK(!txindex.FindTx(txn->GetHash(), block_hash, tx_disk));
|
||||
}
|
||||
|
||||
// Check that txindex has all txs that were in the chain before it started.
|
||||
for (const auto& txn : m_coinbase_txns) {
|
||||
if (!txindex.FindTx(txn.GetHash(), block_hash, tx_disk)) {
|
||||
BOOST_ERROR("FindTx failed");
|
||||
} else if (tx_disk->GetHash() != txn.GetHash()) {
|
||||
BOOST_ERROR("Read incorrect tx");
|
||||
}
|
||||
}
|
||||
|
||||
// Check that new transactions in new blocks make it into the index.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
CScript coinbase_script_pub_key = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
|
||||
std::vector<CMutableTransaction> no_txns;
|
||||
const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key);
|
||||
const CTransaction& txn = *block.vtx[0];
|
||||
|
||||
BOOST_CHECK(txindex.BlockUntilSyncedToCurrentChain());
|
||||
if (!txindex.FindTx(txn.GetHash(), block_hash, tx_disk)) {
|
||||
BOOST_ERROR("FindTx failed");
|
||||
} else if (tx_disk->GetHash() != txn.GetHash()) {
|
||||
BOOST_ERROR("Read incorrect tx");
|
||||
}
|
||||
}
|
||||
|
||||
// shutdown sequence (c.f. Shutdown() in init.cpp)
|
||||
txindex.Stop();
|
||||
|
||||
// Rest of shutdown sequence and destructors happen in ~TestingSetup()
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
38
src/txdb.cpp
38
src/txdb.cpp
|
@ -18,8 +18,6 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
static const sqlite::sqlite_config sharedConfig {
|
||||
sqlite::OpenFlags::READWRITE | sqlite::OpenFlags::CREATE,
|
||||
nullptr, sqlite::Encoding::UTF8
|
||||
|
@ -188,11 +186,7 @@ CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe)
|
|||
"nonce INTEGER NOT NULL "
|
||||
")";
|
||||
|
||||
db << "CREATE TABLE IF NOT EXISTS tx_to_block ("
|
||||
"txID BLOB NOT NULL PRIMARY KEY, "
|
||||
"file INTEGER NOT NULL, "
|
||||
"blockPos INTEGER NOT NULL, "
|
||||
"txPos INTEGER NOT NULL)";
|
||||
db << "DROP TABLE IF EXISTS tx_to_block";
|
||||
|
||||
db << "CREATE TABLE IF NOT EXISTS flag ("
|
||||
"name TEXT NOT NULL PRIMARY KEY, "
|
||||
|
@ -392,32 +386,4 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::WriteTxIndex(const FlatFilePos& file, const std::vector<std::pair<uint256, uint32_t>>& txOffsets)
|
||||
{
|
||||
if (txOffsets.empty()) return true;
|
||||
db << "begin";
|
||||
auto query = db << "INSERT OR REPLACE INTO tx_to_block VALUES(?,?,?,?)";
|
||||
for (auto& kvp: txOffsets) {
|
||||
query << kvp.first << file.nFile << file.nPos << kvp.second;
|
||||
query++;
|
||||
}
|
||||
query.used(true);
|
||||
auto code = sqlite::commit(db);
|
||||
if (code != SQLITE_OK) {
|
||||
LogPrintf("%s: Error committing tx to database. SQLite error: %d\n", __func__, code);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, FlatFilePos &file, uint32_t& offset)
|
||||
{
|
||||
auto query = db << "SELECT file, blockPos, txPos FROM tx_to_block WHERE txID = ?" << txid;
|
||||
for (auto&& row: query) {
|
||||
row >> file.nFile >> file.nPos >> offset;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -130,8 +130,6 @@ public:
|
|||
void ReadReindexing(bool &fReindexing);
|
||||
bool WriteFlag(const std::string &name, bool fValue);
|
||||
bool ReadFlag(const std::string &name, bool &fValue);
|
||||
bool ReadTxIndex(const uint256 &txid, FlatFilePos &file, uint32_t& offset);
|
||||
bool WriteTxIndex(const FlatFilePos& file, const std::vector<std::pair<uint256, uint32_t>>& txOffsets);
|
||||
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <consensus/validation.h>
|
||||
#include <cuckoocache.h>
|
||||
#include <flatfile.h>
|
||||
#include <index/txindex.h>
|
||||
#include <hash.h>
|
||||
#include <nameclaim.h>
|
||||
#include <policy/fees.h>
|
||||
|
@ -1137,29 +1138,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept);
|
||||
}
|
||||
|
||||
bool FillTx(const uint256& tx_hash, const FlatFilePos& pos, uint32_t offset, uint256& block_hash, CTransactionRef& tx)
|
||||
{
|
||||
CAutoFile file(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
|
||||
if (file.IsNull()) {
|
||||
return error("%s: OpenBlockFile failed", __func__);
|
||||
}
|
||||
CBlockHeader header;
|
||||
try {
|
||||
file >> header;
|
||||
if (fseek(file.Get(), offset, SEEK_CUR)) {
|
||||
return error("%s: fseek(...) failed", __func__);
|
||||
}
|
||||
file >> tx;
|
||||
} catch (const std::exception& e) {
|
||||
return error("%s: Deserialize or I/O error - %s", __func__, e.what());
|
||||
}
|
||||
if (tx->GetHash() != tx_hash) {
|
||||
return error("%s: txid mismatch", __func__);
|
||||
}
|
||||
block_hash = header.GetHash();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock.
|
||||
* If blockIndex is provided, the transaction is fetched from the corresponding block.
|
||||
|
@ -1174,13 +1152,9 @@ bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus
|
|||
txOut = ptx;
|
||||
return true;
|
||||
}
|
||||
if (pblocktree) {
|
||||
uint32_t offset;
|
||||
FlatFilePos pos;
|
||||
if (pblocktree->ReadTxIndex(hash, pos, offset) && FillTx(hash, pos, offset, hashBlock, ptx)) {
|
||||
txOut = ptx;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (g_txindex) {
|
||||
return g_txindex->FindTx(hash, hashBlock, txOut);
|
||||
}
|
||||
} else {
|
||||
CBlock block;
|
||||
|
@ -2261,9 +2235,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
|||
CAmount nFees = 0;
|
||||
int nInputs = 0;
|
||||
int64_t nSigOpsCost = 0;
|
||||
std::vector<std::pair<uint256, uint32_t>> txOffsets;
|
||||
uint32_t offset = ::GetSizeOfCompactSize(block.vtx.size());
|
||||
txOffsets.reserve(block.vtx.size());
|
||||
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
||||
std::vector<PrecomputedTransactionData> txdata;
|
||||
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
|
||||
|
@ -2371,9 +2342,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
|||
// or cache when trying to spend it, we shouldn't try to put a claim or support back
|
||||
// in. Some OP_UPDATE_CLAIM's, for example, may be invalid, and so may never have been
|
||||
// inserted into the trie in the first place.
|
||||
|
||||
txOffsets.push_back(std::make_pair(tx.GetHash(), offset));
|
||||
offset += ::GetSerializeSize(tx, CLIENT_VERSION);
|
||||
}
|
||||
|
||||
// TODO: if the "just check" flag is set, we should reduce the work done here. Incrementing blocks twice per mine is not efficient.
|
||||
|
@ -2406,8 +2374,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
|||
|
||||
if (pindex->pprev && !WriteUndoDataForBlock(blockundo, state, pindex, chainparams))
|
||||
return false;
|
||||
if (!pblocktree->WriteTxIndex(pindex->GetBlockPos(), txOffsets))
|
||||
return false;
|
||||
|
||||
if (!pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
|
||||
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
|
||||
|
|
|
@ -122,7 +122,7 @@ static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
|
|||
static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60;
|
||||
|
||||
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
||||
static const bool DEFAULT_TXINDEX = false;
|
||||
static const bool DEFAULT_TXINDEX = true;
|
||||
static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
|
||||
static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100;
|
||||
/** Default for -persistmempool */
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <consensus/validation.h>
|
||||
#include <core_io.h>
|
||||
#include <claimtrie/forks.h>
|
||||
#include <index/txindex.h>
|
||||
#include <init.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <key_io.h>
|
||||
|
@ -788,6 +789,13 @@ UniValue supportclaim(const JSONRPCRequest& request)
|
|||
if (sClaimId.length() > claimLength)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("claimid must be maximum of length %d", claimLength));
|
||||
|
||||
auto isTip = false;
|
||||
if (request.params.size() > 4)
|
||||
isTip = request.params[4].get_bool();
|
||||
|
||||
if (isTip && g_txindex)
|
||||
g_txindex->BlockUntilSyncedToCurrentChain();
|
||||
|
||||
pwallet->BlockUntilSyncedToCurrentChain();
|
||||
auto locked_chain = pwallet->chain().lock();
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
@ -822,10 +830,6 @@ UniValue supportclaim(const JSONRPCRequest& request)
|
|||
|
||||
supportScript = supportScript << OP_2DROP << lastOp;
|
||||
|
||||
auto isTip = false;
|
||||
if (request.params.size() > 4)
|
||||
isTip = request.params[4].get_bool();
|
||||
|
||||
CTxDestination dest;
|
||||
if (isTip) {
|
||||
CTransactionRef ref;
|
||||
|
|
Loading…
Add table
Reference in a new issue