diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -36,6 +36,44 @@ class Handler; class Wallet; +//! Helper for findBlock to selectively return pieces of block data. +class FoundBlock { +public: + FoundBlock &hash(BlockHash &hash) { + m_hash = &hash; + return *this; + } + FoundBlock &height(int &height) { + m_height = &height; + return *this; + } + FoundBlock &time(int64_t &time) { + m_time = &time; + return *this; + } + FoundBlock &maxTime(int64_t &max_time) { + m_max_time = &max_time; + return *this; + } + FoundBlock &mtpTime(int64_t &mtp_time) { + m_mtp_time = &mtp_time; + return *this; + } + //! Read block data from disk. If the block exists but doesn't have data + //! (for example due to pruning), the CBlock variable will be set to null. + FoundBlock &data(CBlock &data) { + m_data = &data; + return *this; + } + + BlockHash *m_hash = nullptr; + int *m_height = nullptr; + int64_t *m_time = nullptr; + int64_t *m_max_time = nullptr; + int64_t *m_mtp_time = nullptr; + CBlock *m_data = nullptr; +}; + //! Interface giving clients (wallet processes, maybe other analysis tools in //! the future) ability to access to the chain state, receive notifications, //! estimate fees, and submit transactions. @@ -138,13 +176,8 @@ //! Return whether node has the block and optionally return block metadata //! or contents. - //! - //! If a block pointer is provided to retrieve the block contents, and the - //! block exists but doesn't have data (for example due to pruning), the - //! block will be empty and all fields set to null. - virtual bool findBlock(const BlockHash &hash, CBlock *block = nullptr, - int64_t *time = nullptr, - int64_t *max_time = nullptr) = 0; + virtual bool findBlock(const BlockHash &hash, + const FoundBlock &block = {}) = 0; //! Look up unspent output information. Returns coins in the mempool and in //! the current chain UTXO set. Iterates through all the keys in the map and diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -35,6 +35,36 @@ namespace interfaces { namespace { + bool FillBlock(const CBlockIndex *index, const FoundBlock &block, + UniqueLock &lock) { + if (!index) { + return false; + } + if (block.m_hash) { + *block.m_hash = index->GetBlockHash(); + } + if (block.m_height) { + *block.m_height = index->nHeight; + } + if (block.m_time) { + *block.m_time = index->GetBlockTime(); + } + if (block.m_max_time) { + *block.m_max_time = index->GetBlockTimeMax(); + } + if (block.m_mtp_time) { + *block.m_mtp_time = index->GetMedianTimePast(); + } + if (block.m_data) { + REVERSE_LOCK(lock); + if (!ReadBlockFromDisk(*block.m_data, index, + Params().GetConsensus())) { + block.m_data->SetNull(); + } + } + return true; + } + class LockImpl : public Chain::Lock, public UniqueLock { Optional getHeight() override { LockAssertion lock(::cs_main); @@ -254,27 +284,10 @@ std::unique_ptr result = std::move(lock); return result; } - bool findBlock(const BlockHash &hash, CBlock *block, int64_t *time, - int64_t *time_max) override { - CBlockIndex *index; - { - LOCK(cs_main); - index = LookupBlockIndex(hash); - if (!index) { - return false; - } - if (time) { - *time = index->GetBlockTime(); - } - if (time_max) { - *time_max = index->GetBlockTimeMax(); - } - } - if (block && - !ReadBlockFromDisk(*block, index, Params().GetConsensus())) { - block->SetNull(); - } - return true; + bool findBlock(const BlockHash &hash, + const FoundBlock &block) override { + WAIT_LOCK(cs_main, lock); + return FillBlock(LookupBlockIndex(hash), block, lock); } void findCoins(std::map &coins) override { return FindCoins(coins); diff --git a/src/sync.h b/src/sync.h --- a/src/sync.h +++ b/src/sync.h @@ -216,8 +216,8 @@ }; #define REVERSE_LOCK(g) \ - decltype(g)::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, \ - __LINE__) + typename std::decay::type::reverse_lock PASTE2( \ + revlock, __COUNTER__)(g, #g, __FILE__, __LINE__) template using DebugLock = UniqueLock +#include +#include +#include + +#include + +using interfaces::FoundBlock; + +BOOST_FIXTURE_TEST_SUITE(interfaces_tests, TestChain100Setup) + +BOOST_AUTO_TEST_CASE(findBlock) { + auto chain = interfaces::MakeChain(m_node, Params()); + auto &active = ChainActive(); + + BlockHash hash; + BOOST_CHECK( + chain->findBlock(active[10]->GetBlockHash(), FoundBlock().hash(hash))); + BOOST_CHECK_EQUAL(hash, active[10]->GetBlockHash()); + + int height = -1; + BOOST_CHECK(chain->findBlock(active[20]->GetBlockHash(), + FoundBlock().height(height))); + BOOST_CHECK_EQUAL(height, active[20]->nHeight); + + CBlock data; + BOOST_CHECK( + chain->findBlock(active[30]->GetBlockHash(), FoundBlock().data(data))); + BOOST_CHECK_EQUAL(data.GetHash(), active[30]->GetBlockHash()); + + int64_t time = -1; + BOOST_CHECK( + chain->findBlock(active[40]->GetBlockHash(), FoundBlock().time(time))); + BOOST_CHECK_EQUAL(time, active[40]->GetBlockTime()); + + int64_t max_time = -1; + BOOST_CHECK(chain->findBlock(active[50]->GetBlockHash(), + FoundBlock().maxTime(max_time))); + BOOST_CHECK_EQUAL(max_time, active[50]->GetBlockTimeMax()); + + int64_t mtp_time = -1; + BOOST_CHECK(chain->findBlock(active[60]->GetBlockHash(), + FoundBlock().mtpTime(mtp_time))); + BOOST_CHECK_EQUAL(mtp_time, active[60]->GetMedianTimePast()); + + BOOST_CHECK(!chain->findBlock(BlockHash(), FoundBlock())); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -38,6 +38,8 @@ #include +using interfaces::FoundBlock; + static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; static inline bool GetAvoidReuseFlag(CWallet *const pwallet, @@ -172,9 +174,8 @@ entry.pushKV("blockheight", wtx.m_confirm.block_height); entry.pushKV("blockindex", wtx.m_confirm.nIndex); int64_t block_time; - bool found_block = chain.findBlock(wtx.m_confirm.hashBlock, - nullptr /* block */, &block_time); - CHECK_NONFATAL(found_block); + CHECK_NONFATAL(chain.findBlock(wtx.m_confirm.hashBlock, + FoundBlock().time(block_time))); entry.pushKV("blocktime", block_time); } else { entry.pushKV("trusted", wtx.IsTrusted(locked_chain)); @@ -1909,7 +1910,8 @@ UniValue removed(UniValue::VARR); while (include_removed && altheight && *altheight > *height) { CBlock block; - if (!pwallet->chain().findBlock(blockId, &block) || block.IsNull()) { + if (!pwallet->chain().findBlock(blockId, FoundBlock().data(block)) || + block.IsNull()) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -25,6 +25,7 @@ #include