Skip to content

Commit

Permalink
refactor: Add interfaces::FoundBlock class to selectively return bloc…
Browse files Browse the repository at this point in the history
…k data

FoundBlock class allows interfaces::Chain::findBlock to return more block
information without having lots of optional output parameters. FoundBlock class
is also used by other chain methods in upcoming commits.

There is mostly no change in behavior. Only exception is
CWallet::RescanFromTime now throwing NonFatalCheckError instead of
std::logic_error.
  • Loading branch information
ryanofsky committed Mar 31, 2020
1 parent d52ba21 commit bf30cd4
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 36 deletions.
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ BITCOIN_TESTS =\
test/fs_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
test/interfaces_tests.cpp \
test/key_io_tests.cpp \
test/key_tests.cpp \
test/limitedmap_tests.cpp \
Expand Down
37 changes: 18 additions & 19 deletions src/interfaces/chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@
namespace interfaces {
namespace {

bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<RecursiveMutex>& 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<RecursiveMutex>
{
Optional<int> getHeight() override
Expand Down Expand Up @@ -247,26 +262,10 @@ class ChainImpl : public Chain
std::unique_ptr<Chain::Lock> result = std::move(lock); // Temporary to avoid CWG 1579
return result;
}
bool findBlock(const uint256& hash, CBlock* block, int64_t* time, int64_t* time_max) override
bool findBlock(const uint256& hash, const FoundBlock& block) 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;
WAIT_LOCK(cs_main, lock);
return FillBlock(LookupBlockIndex(hash), block, lock);
}
void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(m_node, coins); }
double guessVerificationProgress(const uint256& block_hash) override
Expand Down
30 changes: 22 additions & 8 deletions src/interfaces/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,27 @@ namespace interfaces {
class Handler;
class Wallet;

//! Helper for findBlock to selectively return pieces of block data.
class FoundBlock
{
public:
FoundBlock& hash(uint256& 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; }

uint256* 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.
Expand Down Expand Up @@ -127,14 +148,7 @@ class Chain

//! 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 uint256& hash,
CBlock* block = nullptr,
int64_t* time = nullptr,
int64_t* max_time = nullptr) = 0;
virtual bool findBlock(const uint256& 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
Expand Down
2 changes: 1 addition & 1 deletion src/sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class SCOPED_LOCKABLE UniqueLock : public Base
friend class reverse_lock;
};

#define REVERSE_LOCK(g) decltype(g)::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)

template<typename MutexArg>
using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
Expand Down
47 changes: 47 additions & 0 deletions src/test/interfaces_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) 2020 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 <interfaces/chain.h>
#include <test/util/setup_common.h>
#include <validation.h>

#include <boost/test/unit_test.hpp>

using interfaces::FoundBlock;

BOOST_FIXTURE_TEST_SUITE(interfaces_tests, TestChain100Setup)

BOOST_AUTO_TEST_CASE(findBlock)
{
auto chain = interfaces::MakeChain(m_node);
auto& active = ChainActive();

uint256 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({}, FoundBlock()));
}

BOOST_AUTO_TEST_SUITE_END()
7 changes: 4 additions & 3 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include <univalue.h>


using interfaces::FoundBlock;

static const std::string WALLET_ENDPOINT_BASE = "/wallet/";

static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValue& param) {
Expand Down Expand Up @@ -149,8 +151,7 @@ static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& lo
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));
Expand Down Expand Up @@ -1618,7 +1619,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
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");
}
for (const CTransactionRef& tx : block.vtx) {
Expand Down
11 changes: 6 additions & 5 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <script/script.h>
#include <script/signingprovider.h>
#include <util/bip32.h>
#include <util/check.h>
#include <util/error.h>
#include <util/fees.h>
#include <util/moneystr.h>
Expand All @@ -35,6 +36,8 @@

#include <boost/algorithm/string/replace.hpp>

using interfaces::FoundBlock;

const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{
{WALLET_FLAG_AVOID_REUSE,
"You need to rescan the blockchain in order to correctly mark used "
Expand Down Expand Up @@ -1601,9 +1604,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
ScanResult result = ScanForWalletTransactions(start_block, {} /* stop_block */, reserver, update);
if (result.status == ScanResult::FAILURE) {
int64_t time_max;
if (!chain().findBlock(result.last_failed_block, nullptr /* block */, nullptr /* time */, &time_max)) {
throw std::logic_error("ScanForWalletTransactions returned invalid block hash");
}
CHECK_NONFATAL(chain().findBlock(result.last_failed_block, FoundBlock().maxTime(time_max)));
return time_max + TIMESTAMP_WINDOW + 1;
}
}
Expand Down Expand Up @@ -1671,7 +1672,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
}

CBlock block;
if (chain().findBlock(block_hash, &block) && !block.IsNull()) {
if (chain().findBlock(block_hash, FoundBlock().data(block)) && !block.IsNull()) {
auto locked_chain = chain().lock();
LOCK(cs_wallet);
if (!locked_chain->getBlockHeight(block_hash)) {
Expand Down Expand Up @@ -3622,7 +3623,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
unsigned int nTimeSmart = wtx.nTimeReceived;
if (!wtx.isUnconfirmed() && !wtx.isAbandoned()) {
int64_t blocktime;
if (chain().findBlock(wtx.m_confirm.hashBlock, nullptr /* block */, &blocktime)) {
if (chain().findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(blocktime))) {
int64_t latestNow = wtx.nTimeReceived;
int64_t latestEntry = 0;

Expand Down

0 comments on commit bf30cd4

Please sign in to comment.