Skip to content

Commit a08f2f4

Browse files
committed
merge bitcoin#21526: UpdateTip/CheckBlockIndex assumeutxo support
1 parent 472caa0 commit a08f2f4

11 files changed

+326
-135
lines changed

src/Makefile.test_util.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ EXTRA_LIBRARIES += \
99

1010
TEST_UTIL_H = \
1111
test/util/blockfilter.h \
12+
test/util/chainstate.h \
1213
test/util/index.h \
1314
test/util/logging.h \
1415
test/util/mining.h \

src/chain.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,14 @@ enum BlockStatus: uint32_t {
124124
BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD,
125125

126126
BLOCK_CONFLICT_CHAINLOCK = 128, //!< conflicts with chainlock system
127+
128+
/**
129+
* If set, this indicates that the block index entry is assumed-valid.
130+
* Certain diagnostics will be skipped in e.g. CheckBlockIndex().
131+
* It almost certainly means that the block's full validation is pending
132+
* on a background chainstate. See `doc/assumeutxo.md`.
133+
*/
134+
BLOCK_ASSUMED_VALID = 256,
127135
};
128136

129137
/** The block chain is a tree shaped structure starting with the
@@ -291,14 +299,24 @@ class CBlockIndex
291299
return ((nStatus & BLOCK_VALID_MASK) >= nUpTo);
292300
}
293301

302+
//! @returns true if the block is assumed-valid; this means it is queued to be
303+
//! validated by a background chainstate.
304+
bool IsAssumedValid() const { return nStatus & BLOCK_ASSUMED_VALID; }
305+
294306
//! Raise the validity level of this block index entry.
295307
//! Returns true if the validity was changed.
296308
bool RaiseValidity(enum BlockStatus nUpTo)
297309
{
298310
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
299-
if (nStatus & BLOCK_FAILED_MASK)
300-
return false;
311+
if (nStatus & BLOCK_FAILED_MASK) return false;
312+
301313
if ((nStatus & BLOCK_VALID_MASK) < nUpTo) {
314+
// If this block had been marked assumed-valid and we're raising
315+
// its validity to a certain point, there is no longer an assumption.
316+
if (nStatus & BLOCK_ASSUMED_VALID && nUpTo >= BLOCK_VALID_SCRIPTS) {
317+
nStatus &= ~BLOCK_ASSUMED_VALID;
318+
}
319+
302320
nStatus = (nStatus & ~BLOCK_VALID_MASK) | nUpTo;
303321
return true;
304322
}

src/test/evo_deterministicmns_tests.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ void FuncDIP3Activation(TestChainSetup& setup)
251251
int nHeight = chainman.ActiveChain().Height();
252252

253253
// We start one block before DIP3 activation, so mining a block with a DIP3 transaction should fail
254-
auto block = std::make_shared<CBlock>(setup.CreateBlock(txns, setup.coinbaseKey));
254+
auto block = std::make_shared<CBlock>(setup.CreateBlock(txns, setup.coinbaseKey, chainman.ActiveChainstate()));
255255
chainman.ProcessNewBlock(Params(), block, true, nullptr);
256256
BOOST_CHECK_EQUAL(chainman.ActiveChain().Height(), nHeight);
257257
BOOST_ASSERT(block->GetHash() != chainman.ActiveChain().Tip()->GetBlockHash());
@@ -261,7 +261,7 @@ void FuncDIP3Activation(TestChainSetup& setup)
261261
setup.CreateAndProcessBlock({}, setup.coinbaseKey);
262262
BOOST_CHECK_EQUAL(chainman.ActiveChain().Height(), nHeight + 1);
263263
// Mining a block with a DIP3 transaction should succeed now
264-
block = std::make_shared<CBlock>(setup.CreateBlock(txns, setup.coinbaseKey));
264+
block = std::make_shared<CBlock>(setup.CreateBlock(txns, setup.coinbaseKey, chainman.ActiveChainstate()));
265265
BOOST_ASSERT(chainman.ProcessNewBlock(Params(), block, true, nullptr));
266266
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
267267
BOOST_CHECK_EQUAL(chainman.ActiveChain().Height(), nHeight + 2);
@@ -288,7 +288,7 @@ void FuncV19Activation(TestChainSetup& setup)
288288

289289
int nHeight = chainman.ActiveChain().Height();
290290

291-
auto block = std::make_shared<CBlock>(setup.CreateBlock({tx_reg}, setup.coinbaseKey));
291+
auto block = std::make_shared<CBlock>(setup.CreateBlock({tx_reg}, setup.coinbaseKey, chainman.ActiveChainstate()));
292292
BOOST_ASSERT(chainman.ProcessNewBlock(Params(), block, true, nullptr));
293293
BOOST_ASSERT(!DeploymentActiveAfter(chainman.ActiveChain().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_V19));
294294
++nHeight;
@@ -306,7 +306,7 @@ void FuncV19Activation(TestChainSetup& setup)
306306
operator_key_new.MakeNewKey();
307307
auto tx_upreg = CreateProUpRegTx(chainman.ActiveChain(), *(setup.m_node.mempool), utxos, tx_reg_hash, owner_key, operator_key_new.GetPublicKey(), owner_key.GetPubKey().GetID(), collateralScript, setup.coinbaseKey);
308308

309-
block = std::make_shared<CBlock>(setup.CreateBlock({tx_upreg}, setup.coinbaseKey));
309+
block = std::make_shared<CBlock>(setup.CreateBlock({tx_upreg}, setup.coinbaseKey, chainman.ActiveChainstate()));
310310
BOOST_ASSERT(chainman.ProcessNewBlock(Params(), block, true, nullptr));
311311
BOOST_ASSERT(!DeploymentActiveAfter(chainman.ActiveChain().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_V19));
312312
++nHeight;
@@ -326,7 +326,7 @@ void FuncV19Activation(TestChainSetup& setup)
326326
FillableSigningProvider signing_provider;
327327
signing_provider.AddKeyPubKey(collateral_key, collateral_key.GetPubKey());
328328
BOOST_ASSERT(SignSignature(signing_provider, CTransaction(tx_reg), tx_spend, 0, SIGHASH_ALL));
329-
block = std::make_shared<CBlock>(setup.CreateBlock({tx_spend}, setup.coinbaseKey));
329+
block = std::make_shared<CBlock>(setup.CreateBlock({tx_spend}, setup.coinbaseKey, chainman.ActiveChainstate()));
330330
BOOST_ASSERT(chainman.ProcessNewBlock(Params(), block, true, nullptr));
331331
BOOST_ASSERT(!DeploymentActiveAfter(chainman.ActiveChain().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_V19));
332332
++nHeight;
@@ -614,7 +614,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup)
614614
FundTransaction(chainman.ActiveChain(), tx_collateral, utxos, scriptCollateral, dmn_types::Regular.collat_amount, setup.coinbaseKey);
615615
SignTransaction(*(setup.m_node.mempool), tx_collateral, setup.coinbaseKey);
616616

617-
auto block = std::make_shared<CBlock>(setup.CreateBlock({tx_collateral}, setup.coinbaseKey));
617+
auto block = std::make_shared<CBlock>(setup.CreateBlock({tx_collateral}, setup.coinbaseKey, chainman.ActiveChainstate()));
618618
BOOST_ASSERT(chainman.ProcessNewBlock(Params(), block, true, nullptr));
619619
setup.m_node.dmnman->UpdatedBlockTip(chainman.ActiveChain().Tip());
620620
BOOST_CHECK_EQUAL(chainman.ActiveChain().Height(), nHeight + 1);
@@ -756,7 +756,7 @@ void FuncVerifyDB(TestChainSetup& setup)
756756
FundTransaction(chainman.ActiveChain(), tx_collateral, utxos, scriptCollateral, dmn_types::Regular.collat_amount, setup.coinbaseKey);
757757
SignTransaction(*(setup.m_node.mempool), tx_collateral, setup.coinbaseKey);
758758

759-
auto block = std::make_shared<CBlock>(setup.CreateBlock({tx_collateral}, setup.coinbaseKey));
759+
auto block = std::make_shared<CBlock>(setup.CreateBlock({tx_collateral}, setup.coinbaseKey, chainman.ActiveChainstate()));
760760
BOOST_ASSERT(chainman.ProcessNewBlock(Params(), block, true, nullptr));
761761
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
762762
BOOST_CHECK_EQUAL(chainman.ActiveChain().Height(), nHeight + 1);
@@ -788,7 +788,7 @@ void FuncVerifyDB(TestChainSetup& setup)
788788

789789
auto tx_reg_hash = tx_reg.GetHash();
790790

791-
block = std::make_shared<CBlock>(setup.CreateBlock({tx_reg}, setup.coinbaseKey));
791+
block = std::make_shared<CBlock>(setup.CreateBlock({tx_reg}, setup.coinbaseKey, chainman.ActiveChainstate()));
792792
BOOST_ASSERT(chainman.ProcessNewBlock(Params(), block, true, nullptr));
793793
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
794794
BOOST_CHECK_EQUAL(chainman.ActiveChain().Height(), nHeight + 2);
@@ -800,7 +800,7 @@ void FuncVerifyDB(TestChainSetup& setup)
800800
collateral_utxos.emplace(payload.collateralOutpoint, std::make_pair(1, 1000));
801801
auto proUpRevTx = CreateProUpRevTx(chainman.ActiveChain(), *(setup.m_node.mempool), collateral_utxos, tx_reg_hash, operatorKey, collateralKey);
802802

803-
block = std::make_shared<CBlock>(setup.CreateBlock({proUpRevTx}, setup.coinbaseKey));
803+
block = std::make_shared<CBlock>(setup.CreateBlock({proUpRevTx}, setup.coinbaseKey, chainman.ActiveChainstate()));
804804
BOOST_ASSERT(chainman.ProcessNewBlock(Params(), block, true, nullptr));
805805
dmnman.UpdatedBlockTip(chainman.ActiveChain().Tip());
806806
BOOST_CHECK_EQUAL(chainman.ActiveChain().Height(), nHeight + 3);

src/test/util/chainstate.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) 2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
//
5+
#ifndef BITCOIN_TEST_UTIL_CHAINSTATE_H
6+
#define BITCOIN_TEST_UTIL_CHAINSTATE_H
7+
8+
#include <clientversion.h>
9+
#include <fs.h>
10+
#include <node/context.h>
11+
#include <node/utxo_snapshot.h>
12+
#include <rpc/blockchain.h>
13+
#include <validation.h>
14+
15+
#include <univalue.h>
16+
17+
#include <boost/test/unit_test.hpp>
18+
19+
const auto NoMalleation = [](CAutoFile& file, SnapshotMetadata& meta){};
20+
21+
/**
22+
* Create and activate a UTXO snapshot, optionally providing a function to
23+
* malleate the snapshot.
24+
*/
25+
template<typename F = decltype(NoMalleation)>
26+
static bool
27+
CreateAndActivateUTXOSnapshot(NodeContext& node, const fs::path root, F malleation = NoMalleation)
28+
{
29+
// Write out a snapshot to the test's tempdir.
30+
//
31+
int height;
32+
WITH_LOCK(::cs_main, height = node.chainman->ActiveHeight());
33+
fs::path snapshot_path = root / tfm::format("test_snapshot.%d.dat", height);
34+
FILE* outfile{fsbridge::fopen(snapshot_path, "wb")};
35+
CAutoFile auto_outfile{outfile, SER_DISK, CLIENT_VERSION};
36+
37+
UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), auto_outfile);
38+
BOOST_TEST_MESSAGE(
39+
"Wrote UTXO snapshot to " << snapshot_path.make_preferred().string() << ": " << result.write());
40+
41+
// Read the written snapshot in and then activate it.
42+
//
43+
FILE* infile{fsbridge::fopen(snapshot_path, "rb")};
44+
CAutoFile auto_infile{infile, SER_DISK, CLIENT_VERSION};
45+
SnapshotMetadata metadata;
46+
auto_infile >> metadata;
47+
48+
malleation(auto_infile, metadata);
49+
50+
return node.chainman->ActivateSnapshot(auto_infile, metadata, /*in_memory*/ true);
51+
}
52+
53+
54+
#endif // BITCOIN_TEST_UTIL_CHAINSTATE_H

src/test/util/setup_common.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -373,28 +373,41 @@ void TestChainSetup::mineBlocks(int num_blocks)
373373
}
374374
}
375375

376-
CBlock TestChainSetup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
376+
CBlock TestChainSetup::CreateAndProcessBlock(
377+
const std::vector<CMutableTransaction>& txns,
378+
const CScript& scriptPubKey,
379+
CChainState* chainstate)
377380
{
381+
if (!chainstate) {
382+
chainstate = &Assert(m_node.chainman)->ActiveChainstate();
383+
}
384+
378385
const CChainParams& chainparams = Params();
379-
auto block = CreateBlock(txns, scriptPubKey);
386+
auto block = this->CreateBlock(txns, scriptPubKey, *chainstate);
380387

381388
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
382389
Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr);
383390

384391
return block;
385392
}
386393

387-
CBlock TestChainSetup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CKey& scriptKey)
394+
CBlock TestChainSetup::CreateAndProcessBlock(
395+
const std::vector<CMutableTransaction>& txns,
396+
const CKey& scriptKey,
397+
CChainState* chainstate)
388398
{
389399
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
390-
return CreateAndProcessBlock(txns, scriptPubKey);
400+
return CreateAndProcessBlock(txns, scriptPubKey, chainstate);
391401
}
392402

393-
CBlock TestChainSetup::CreateBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey)
403+
CBlock TestChainSetup::CreateBlock(
404+
const std::vector<CMutableTransaction>& txns,
405+
const CScript& scriptPubKey,
406+
CChainState& chainstate)
394407
{
395408
const CChainParams& chainparams = Params();
396409
CTxMemPool empty_pool;
397-
CBlock block = BlockAssembler(m_node.chainman->ActiveChainstate(), m_node, empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
410+
CBlock block = BlockAssembler(chainstate, m_node, empty_pool, chainparams).CreateNewBlock(scriptPubKey)->block;
398411

399412
std::vector<CTransactionRef> llmqCommitments;
400413
for (const auto& tx : block.vtx) {
@@ -443,10 +456,13 @@ CBlock TestChainSetup::CreateBlock(const std::vector<CMutableTransaction>& txns,
443456
return result;
444457
}
445458

446-
CBlock TestChainSetup::CreateBlock(const std::vector<CMutableTransaction>& txns, const CKey& scriptKey)
459+
CBlock TestChainSetup::CreateBlock(
460+
const std::vector<CMutableTransaction>& txns,
461+
const CKey& scriptKey,
462+
CChainState& chainstate)
447463
{
448464
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
449-
return CreateBlock(txns, scriptPubKey);
465+
return CreateBlock(txns, scriptPubKey, chainstate);
450466
}
451467

452468

src/test/util/setup_common.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,25 @@ struct TestChainSetup : public RegTestingSetup
127127
/**
128128
* Create a new block with just given transactions, coinbase paying to
129129
* scriptPubKey, and try to add it to the current chain.
130+
* If no chainstate is specified, default to the active.
130131
*/
131132
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
132-
const CScript& scriptPubKey);
133+
const CScript& scriptPubKey,
134+
CChainState* chainstate = nullptr);
133135
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
134-
const CKey& scriptKey);
136+
const CKey& scriptKey,
137+
CChainState* chainstate = nullptr);
138+
139+
/**
140+
* Create a new block with just given transactions, coinbase paying to
141+
* scriptPubKey.
142+
*/
135143
CBlock CreateBlock(const std::vector<CMutableTransaction>& txns,
136-
const CScript& scriptPubKey);
144+
const CScript& scriptPubKey,
145+
CChainState& chainstate);
137146
CBlock CreateBlock(const std::vector<CMutableTransaction>& txns,
138-
const CKey& scriptKey);
147+
const CKey& scriptKey,
148+
CChainState& chainstate);
139149

140150
//! Mine a series of new blocks on the active chain.
141151
void mineBlocks(int num_blocks);

src/test/validation_chainstate_tests.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44
//
5+
#include <chainparams.h>
56
#include <consensus/validation.h>
67
#include <evo/evodb.h>
78
#include <index/txindex.h>
@@ -10,6 +11,8 @@
1011
#include <llmq/instantsend.h>
1112
#include <random.h>
1213
#include <sync.h>
14+
#include <rpc/blockchain.h>
15+
#include <test/util/chainstate.h>
1316
#include <test/util/setup_common.h>
1417
#include <uint256.h>
1518
#include <validation.h>
@@ -78,4 +81,77 @@ BOOST_AUTO_TEST_CASE(validation_chainstate_resize_caches)
7881
WITH_LOCK(::cs_main, manager.Unload());
7982
}
8083

84+
//! Test UpdateTip behavior for both active and background chainstates.
85+
//!
86+
//! When run on the background chainstate, UpdateTip should do a subset
87+
//! of what it does for the active chainstate.
88+
BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
89+
{
90+
ChainstateManager& chainman = *Assert(m_node.chainman);
91+
uint256 curr_tip = ::g_best_block;
92+
93+
// Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
94+
// be found.
95+
mineBlocks(10);
96+
97+
// After adding some blocks to the tip, best block should have changed.
98+
BOOST_CHECK(::g_best_block != curr_tip);
99+
100+
BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
101+
102+
// Ensure our active chain is the snapshot chainstate.
103+
BOOST_CHECK(chainman.IsSnapshotActive());
104+
105+
curr_tip = ::g_best_block;
106+
107+
// Mine a new block on top of the activated snapshot chainstate.
108+
mineBlocks(1); // Defined in TestChain100Setup.
109+
110+
// After adding some blocks to the snapshot tip, best block should have changed.
111+
BOOST_CHECK(::g_best_block != curr_tip);
112+
113+
curr_tip = ::g_best_block;
114+
115+
CChainState* background_cs;
116+
117+
BOOST_CHECK_EQUAL(chainman.GetAll().size(), 2);
118+
for (CChainState* cs : chainman.GetAll()) {
119+
if (cs != &chainman.ActiveChainstate()) {
120+
background_cs = cs;
121+
}
122+
}
123+
BOOST_CHECK(background_cs);
124+
125+
// Create a block to append to the validation chain.
126+
std::vector<CMutableTransaction> noTxns;
127+
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
128+
CBlock validation_block = this->CreateBlock(noTxns, scriptPubKey, *background_cs);
129+
auto pblock = std::make_shared<const CBlock>(validation_block);
130+
BlockValidationState state;
131+
CBlockIndex* pindex = nullptr;
132+
const CChainParams& chainparams = Params();
133+
bool newblock = false;
134+
135+
// TODO: much of this is inlined from ProcessNewBlock(); just reuse PNB()
136+
// once it is changed to support multiple chainstates.
137+
{
138+
LOCK(::cs_main);
139+
bool checked = CheckBlock(*pblock, state, chainparams.GetConsensus());
140+
BOOST_CHECK(checked);
141+
bool accepted = background_cs->AcceptBlock(
142+
pblock, state, &pindex, true, nullptr, &newblock);
143+
BOOST_CHECK(accepted);
144+
}
145+
// UpdateTip is called here
146+
bool block_added = background_cs->ActivateBestChain(state, pblock);
147+
148+
// Ensure tip is as expected
149+
BOOST_CHECK_EQUAL(background_cs->m_chain.Tip()->GetBlockHash(), validation_block.GetHash());
150+
151+
// g_best_block should be unchanged after adding a block to the background
152+
// validation chain.
153+
BOOST_CHECK(block_added);
154+
BOOST_CHECK_EQUAL(curr_tip, ::g_best_block);
155+
}
156+
81157
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)