Skip to content

Commit 6bf39d7

Browse files
committed
merge bitcoin#19806: UTXO snapshot activation
1 parent 87863a6 commit 6bf39d7

19 files changed

+718
-31
lines changed

src/chain.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,27 @@ class CBlockIndex
161161

162162
//! Number of transactions in this block.
163163
//! Note: in a potential headers-first mode, this number cannot be relied upon
164+
//! Note: this value is faked during UTXO snapshot load to ensure that
165+
//! LoadBlockIndex() will load index entries for blocks that we lack data for.
166+
//! @sa ActivateSnapshot
164167
unsigned int nTx{0};
165168

166169
//! (memory only) Number of transactions in the chain up to and including this block.
167170
//! This value will be non-zero only if and only if transactions for this block and all its parents are available.
168171
//! Change to 64-bit type when necessary; won't happen before 2030
172+
//!
173+
//! Note: this value is faked during use of a UTXO snapshot because we don't
174+
//! have the underlying block data available during snapshot load.
175+
//! @sa AssumeutxoData
176+
//! @sa ActivateSnapshot
169177
unsigned int nChainTx{0};
170178

171179
//! Verification status of this block. See enum BlockStatus
180+
//!
181+
//! Note: this value is modified to show BLOCK_OPT_WITNESS during UTXO snapshot
182+
//! load to avoid the block index being spuriously rewound.
183+
//! @sa RewindBlockIndex
184+
//! @sa ActivateSnapshot
172185
uint32_t nStatus{0};
173186

174187
//! block header

src/chainparams.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include <chainparamsseeds.h>
1010
#include <consensus/merkle.h>
1111
#include <llmq/params.h>
12-
#include <tinyformat.h>
1312
#include <util/ranges.h>
1413
#include <util/system.h>
1514
#include <util/underlying.h>
@@ -311,6 +310,10 @@ class CMainParams : public CChainParams {
311310
}
312311
};
313312

313+
m_assumeutxo_data = MapAssumeutxo{
314+
// TODO to be specified in a future patch.
315+
};
316+
314317
// getchaintxstats 17280 00000000000000261bdbe99c01fcba992e577efa6cc41aae564b8ca9f112b2a3
315318
chainTxData = ChainTxData{
316319
1680866408, // * UNIX timestamp of last known number of transactions (Block 1718597)
@@ -477,6 +480,10 @@ class CTestNetParams : public CChainParams {
477480
}
478481
};
479482

483+
m_assumeutxo_data = MapAssumeutxo{
484+
// TODO to be specified in a future patch.
485+
};
486+
480487
// getchaintxstats 17280 0000005c35514190ef3c38d322f69412553dc7e1107ed5f92adc2935b90acc51
481488
chainTxData = ChainTxData{
482489
1680868209, // * UNIX timestamp of last known number of transactions (Block 771537)
@@ -846,6 +853,17 @@ class CRegTestParams : public CChainParams {
846853
}
847854
};
848855

856+
m_assumeutxo_data = MapAssumeutxo{
857+
{
858+
110,
859+
{uint256S("0x533d91c2aee01848b86693c226da68b1ba3f47bf266d9082a32bb2df4dafd7d8"), 110},
860+
},
861+
{
862+
210,
863+
{uint256S("0x4282d2b2a90444a8e5d3f80250c5a0cacde5a7000f2b03b0982013be98c2ba53"), 210},
864+
},
865+
};
866+
849867
chainTxData = ChainTxData{
850868
0,
851869
0,
@@ -1326,3 +1344,9 @@ void SelectParams(const std::string& network)
13261344
SelectBaseParams(network);
13271345
globalChainParams = CreateChainParams(network);
13281346
}
1347+
1348+
std::ostream& operator<<(std::ostream& o, const AssumeutxoData& aud)
1349+
{
1350+
o << strprintf("AssumeutxoData(%s, %s)", aud.hash_serialized.ToString(), aud.nChainTx);
1351+
return o;
1352+
}

src/chainparams.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,26 @@ struct CCheckpointData {
2626
MapCheckpoints mapCheckpoints;
2727
};
2828

29+
/**
30+
* Holds configuration for use during UTXO snapshot load and validation. The contents
31+
* here are security critical, since they dictate which UTXO snapshots are recognized
32+
* as valid.
33+
*/
34+
struct AssumeutxoData {
35+
//! The expected hash of the deserialized UTXO set.
36+
const uint256 hash_serialized;
37+
38+
//! Used to populate the nChainTx value, which is used during BlockManager::LoadBlockIndex().
39+
//!
40+
//! We need to hardcode the value here because this is computed cumulatively using block data,
41+
//! which we do not necessarily have at the time of snapshot load.
42+
const unsigned int nChainTx;
43+
};
44+
45+
std::ostream& operator<<(std::ostream& o, const AssumeutxoData& aud);
46+
47+
using MapAssumeutxo = std::map<int, const AssumeutxoData>;
48+
2949
/**
3050
* Holds various statistics on transactions within a chain. Used to estimate
3151
* verification progress during chain sync.
@@ -97,6 +117,11 @@ class CChainParams
97117
int ExtCoinType() const { return nExtCoinType; }
98118
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
99119
const CCheckpointData& Checkpoints() const { return checkpointData; }
120+
121+
//! Get allowed assumeutxo configuration.
122+
//! @see ChainstateManager
123+
const MapAssumeutxo& Assumeutxo() const { return m_assumeutxo_data; }
124+
100125
const ChainTxData& TxData() const { return chainTxData; }
101126
void UpdateDIP3Parameters(int nActivationHeight, int nEnforcementHeight);
102127
void UpdateDIP8Parameters(int nActivationHeight);
@@ -139,6 +164,7 @@ class CChainParams
139164
bool m_is_mockable_chain;
140165
int nLLMQConnectionRetryTimeout;
141166
CCheckpointData checkpointData;
167+
MapAssumeutxo m_assumeutxo_data;
142168
ChainTxData chainTxData;
143169
int nPoolMinParticipants;
144170
int nPoolMaxParticipants;

src/coins.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
9999
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
100100
}
101101

102+
void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin) {
103+
cachedCoinsUsage += coin.DynamicMemoryUsage();
104+
cacheCoins.emplace(
105+
std::piecewise_construct,
106+
std::forward_as_tuple(std::move(outpoint)),
107+
std::forward_as_tuple(std::move(coin), CCoinsCacheEntry::DIRTY));
108+
}
109+
102110
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
103111
bool fCoinbase = tx.IsCoinBase();
104112
const uint256& txid = tx.GetHash();

src/coins.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <functional>
2121
#include <unordered_map>
2222

23+
class ChainstateManager;
24+
2325
/**
2426
* A UTXO entry.
2527
*
@@ -155,6 +157,7 @@ struct CCoinsCacheEntry
155157

156158
CCoinsCacheEntry() : flags(0) {}
157159
explicit CCoinsCacheEntry(Coin&& coin_) : coin(std::move(coin_)), flags(0) {}
160+
CCoinsCacheEntry(Coin&& coin_, unsigned char flag) : coin(std::move(coin_)), flags(flag) {}
158161
};
159162

160163
typedef std::unordered_map<COutPoint, CCoinsCacheEntry, SaltedOutpointHasher> CCoinsMap;
@@ -292,6 +295,15 @@ class CCoinsViewCache : public CCoinsViewBacked
292295
*/
293296
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite);
294297

298+
/**
299+
* Emplace a coin into cacheCoins without performing any checks, marking
300+
* the emplaced coin as dirty.
301+
*
302+
* NOT FOR GENERAL USE. Used only when loading coins from a UTXO snapshot.
303+
* @sa ChainstateManager::PopulateAndValidateSnapshot()
304+
*/
305+
void EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin);
306+
295307
/**
296308
* Spend a coin. Pass moveto in order to get the deleted data.
297309
* If no unspent output exists for the passed outpoint, this call

src/node/coinstats.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ static void ApplyHash(CCoinsStats& stats, MuHash3072& muhash, const uint256& has
5656
muhash.Insert(MakeUCharSpan(ss));
5757
}
5858

59+
//! Warning: be very careful when changing this! assumeutxo and UTXO snapshot
60+
//! validation commitments are reliant on the hash constructed by this
61+
//! function.
62+
//!
63+
//! If the construction of this hash is changed, it will invalidate
64+
//! existing UTXO snapshots. This will not result in any kind of consensus
65+
//! failure, but it will force clients that were expecting to make use of
66+
//! assumeutxo to do traditional IBD instead.
67+
//!
68+
//! It is also possible, though very unlikely, that a change in this
69+
//! construction could cause a previously invalid (and potentially malicious)
70+
//! UTXO snapshot to be considered valid.
5971
template <typename T>
6072
static void ApplyStats(CCoinsStats& stats, T& hash_obj, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
6173
{

src/rpc/blockchain.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,10 +2716,19 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
27162716

27172717
FILE* file{fsbridge::fopen(temppath, "wb")};
27182718
CAutoFile afile{file, SER_DISK, CLIENT_VERSION};
2719+
NodeContext& node = EnsureNodeContext(request.context);
2720+
UniValue result = CreateUTXOSnapshot(node, node.chainman->ActiveChainstate(), afile);
2721+
fs::rename(temppath, path);
2722+
2723+
result.pushKV("path", path.string());
2724+
return result;
2725+
}
2726+
2727+
UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFile& afile)
2728+
{
27192729
std::unique_ptr<CCoinsViewCursor> pcursor;
27202730
CCoinsStats stats;
27212731
CBlockIndex* tip;
2722-
NodeContext& node = EnsureNodeContext(request.context);
27232732

27242733
{
27252734
// We need to lock cs_main to ensure that the coinsdb isn't written to
@@ -2736,13 +2745,13 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
27362745
//
27372746
LOCK(::cs_main);
27382747

2739-
::ChainstateActive().ForceFlushStateToDisk();
2748+
chainstate.ForceFlushStateToDisk();
27402749

2741-
if (!GetUTXOStats(&::ChainstateActive().CoinsDB(), stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
2750+
if (!GetUTXOStats(&chainstate.CoinsDB(), stats, CoinStatsHashType::NONE, node.rpc_interruption_point)) {
27422751
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
27432752
}
27442753

2745-
pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
2754+
pcursor = std::unique_ptr<CCoinsViewCursor>(chainstate.CoinsDB().Cursor());
27462755
tip = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock);
27472756
CHECK_NONFATAL(tip);
27482757
}
@@ -2767,13 +2776,12 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
27672776
}
27682777

27692778
afile.fclose();
2770-
fs::rename(temppath, path);
27712779

27722780
UniValue result(UniValue::VOBJ);
27732781
result.pushKV("coins_written", stats.coins_count);
27742782
result.pushKV("base_hash", tip->GetBlockHash().ToString());
27752783
result.pushKV("base_height", tip->nHeight);
2776-
result.pushKV("path", path.string());
2784+
27772785
return result;
27782786
}
27792787

src/rpc/blockchain.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <amount.h>
99
#include <context.h>
10+
#include <streams.h>
1011
#include <sync.h>
1112

1213
#include <stdint.h>
@@ -17,6 +18,7 @@ extern RecursiveMutex cs_main;
1718
class CBlock;
1819
class CBlockIndex;
1920
class CBlockPolicyEstimator;
21+
class CChainState;
2022
class CTxMemPool;
2123
class ChainstateManager;
2224
class UniValue;
@@ -61,4 +63,10 @@ ChainstateManager& EnsureChainman(const CoreContext& context);
6163
CBlockPolicyEstimator& EnsureFeeEstimator(const CoreContext& context);
6264
LLMQContext& EnsureLLMQContext(const CoreContext& context);
6365

66+
/**
67+
* Helper to create UTXO snapshots given a chainstate and a file handle.
68+
* @return a UniValue map containing metadata about the snapshot.
69+
*/
70+
UniValue CreateUTXOSnapshot(NodeContext& node, CChainState& chainstate, CAutoFile& afile);
71+
6472
#endif

src/test/block_reward_reallocation_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ using SimpleUTXOMap = std::map<COutPoint, std::pair<int, CAmount>>;
3838
struct TestChainBRRBeforeActivationSetup : public TestChainSetup
3939
{
4040
// Force fast DIP3 activation
41-
TestChainBRRBeforeActivationSetup() : TestChainSetup(497, {"-dip3params=30:50"}) {}
41+
TestChainBRRBeforeActivationSetup() : TestChainSetup(497, false, {"-dip3params=30:50"}) {}
4242
};
4343

4444
static SimpleUTXOMap BuildSimpleUtxoMap(const std::vector<CTransactionRef>& txs)

src/test/dynamic_activation_thresholds_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static constexpr int threshold(int attempt)
3434

3535
struct TestChainDATSetup : public TestChainSetup
3636
{
37-
TestChainDATSetup() : TestChainSetup(window - 2, {"-vbparams=testdummy:0:999999999999:100:80:60:5"}) {}
37+
TestChainDATSetup() : TestChainSetup(window - 2, false, {"-vbparams=testdummy:0:999999999999:100:80:60:5"}) {}
3838

3939
void signal(int num_blocks, bool expected_lockin)
4040
{

0 commit comments

Comments
 (0)