Merge #19413: refactor: Remove confusing BlockIndex global

fa0dfdf447 refactor: Remove confusing BlockIndex global (MarcoFalke)

Pull request description:

  The global `::BlockIndex()` is problematic for several reasons:

  * It returns a mutable reference to the block tree, without the appropriate lock annotation (`m_block_index` is guarded by `cs_main`). The current code is fine, but in the future this might lead to accidental races and data corruption.
  * The rpc server shouldn't rely on node globals, but rather a context that is passed in to the RPC method.
  * Tests might want to spin up their own block tree, and thus should also not rely on a single global.

  Fix all issues by removing the global

ACKs for top commit:
  promag:
    Code review ACK fa0dfdf447.
  jonatack:
    re-ACK fa0dfdf

Tree-SHA512: 8f158fc5e1c67e73588a21c25677b3fa0fe442313b13ec24b87054806c59607d6ba0c062a865ce3e0ee568706bd0d1faa84febda21aff5bcd65dab172f74c52f
pull/764/head
MarcoFalke 4 years ago
commit 915ac8a861
No known key found for this signature in database
GPG Key ID: CE2B75697E69A548

@ -1588,7 +1588,7 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
if (!::BlockIndex().empty() &&
if (!chainman.BlockIndex().empty() &&
!LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
}
@ -1868,8 +1868,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node)
//// debug print
{
LOCK(cs_main);
LogPrintf("block tree size = %u\n", ::BlockIndex().size());
chain_active_height = ::ChainActive().Height();
LogPrintf("block tree size = %u\n", chainman.BlockIndex().size());
chain_active_height = chainman.ActiveChain().Height();
}
LogPrintf("nBestHeight = %d\n", chain_active_height);

@ -1325,50 +1325,48 @@ static UniValue getchaintips(const JSONRPCRequest& request)
},
}.Check(request);
ChainstateManager& chainman = EnsureChainman(request.context);
LOCK(cs_main);
/*
* Idea: the set of chain tips is ::ChainActive().tip, plus orphan blocks which do not have another orphan building off of them.
* Idea: The set of chain tips is the active chain tip, plus orphan blocks which do not have another orphan building off of them.
* Algorithm:
* - Make one pass through BlockIndex(), picking out the orphan blocks, and also storing a set of the orphan block's pprev pointers.
* - Iterate through the orphan blocks. If the block isn't pointed to by another orphan, it is a chain tip.
* - add ::ChainActive().Tip()
* - Add the active chain tip
*/
std::set<const CBlockIndex*, CompareBlocksByHeight> setTips;
std::set<const CBlockIndex*> setOrphans;
std::set<const CBlockIndex*> setPrevs;
for (const std::pair<const uint256, CBlockIndex*>& item : ::BlockIndex())
{
if (!::ChainActive().Contains(item.second)) {
for (const std::pair<const uint256, CBlockIndex*>& item : chainman.BlockIndex()) {
if (!chainman.ActiveChain().Contains(item.second)) {
setOrphans.insert(item.second);
setPrevs.insert(item.second->pprev);
}
}
for (std::set<const CBlockIndex*>::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it)
{
for (std::set<const CBlockIndex*>::iterator it = setOrphans.begin(); it != setOrphans.end(); ++it) {
if (setPrevs.erase(*it) == 0) {
setTips.insert(*it);
}
}
// Always report the currently active tip.
setTips.insert(::ChainActive().Tip());
setTips.insert(chainman.ActiveChain().Tip());
/* Construct the output array. */
UniValue res(UniValue::VARR);
for (const CBlockIndex* block : setTips)
{
for (const CBlockIndex* block : setTips) {
UniValue obj(UniValue::VOBJ);
obj.pushKV("height", block->nHeight);
obj.pushKV("hash", block->phashBlock->GetHex());
const int branchLen = block->nHeight - ::ChainActive().FindFork(block)->nHeight;
const int branchLen = block->nHeight - chainman.ActiveChain().FindFork(block)->nHeight;
obj.pushKV("branchlen", branchLen);
std::string status;
if (::ChainActive().Contains(block)) {
if (chainman.ActiveChain().Contains(block)) {
// This block is part of the currently active chain.
status = "active";
} else if (block->nStatus & BLOCK_FAILED_MASK) {

@ -1321,12 +1321,6 @@ bool CChainState::IsInitialBlockDownload() const
static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
BlockMap& BlockIndex()
{
LOCK(::cs_main);
return g_chainman.m_blockman.m_block_index;
}
static void AlertNotify(const std::string& strMessage)
{
uiInterface.NotifyAlertChanged();

@ -886,9 +886,6 @@ CChainState& ChainstateActive();
/** Please prefer the identical ChainstateManager::ActiveChain */
CChain& ChainActive();
/** Please prefer the identical ChainstateManager::BlockIndex */
BlockMap& BlockIndex();
/** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree;

@ -333,7 +333,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN);
}
static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
static int64_t AddTx(ChainstateManager& chainman, CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
{
CMutableTransaction tx;
CWalletTx::Confirmation confirm;
@ -341,7 +341,8 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
SetMockTime(mockTime);
CBlockIndex* block = nullptr;
if (blockTime > 0) {
auto inserted = ::BlockIndex().emplace(GetRandHash(), new CBlockIndex);
LOCK(cs_main);
auto inserted = chainman.BlockIndex().emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
block = inserted.first->second;
@ -363,24 +364,24 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
{
// New transaction should use clock time if lower than block time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 100, 120), 100);
BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 1, 100, 120), 100);
// Test that updating existing transaction does not change smart time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 200, 220), 100);
BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 1, 200, 220), 100);
// New transaction should use clock time if there's no block time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 2, 300, 0), 300);
BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 2, 300, 0), 300);
// New transaction should use block time if lower than clock time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 3, 420, 400), 400);
BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 3, 420, 400), 400);
// New transaction should use latest entry time if higher than
// min(block time, clock time).
BOOST_CHECK_EQUAL(AddTx(m_wallet, 4, 500, 390), 400);
BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 4, 500, 390), 400);
// If there are future entries, new transaction should use time of the
// newest entry that is no more than 300 seconds ahead of the clock time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 5, 50, 600), 300);
BOOST_CHECK_EQUAL(AddTx(*m_node.chainman, m_wallet, 5, 50, 600), 300);
// Reset mock time for other tests.
SetMockTime(0);

Loading…
Cancel
Save