Skip to content

Commit 04bde80

Browse files
committed
net: Add block undo data messages
1 parent 8c07800 commit 04bde80

File tree

8 files changed

+195
-0
lines changed

8 files changed

+195
-0
lines changed

src/init.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
557557
argsman.AddArg("-v2transport", strprintf("Support v2 transport (default: %u)", DEFAULT_V2_TRANSPORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
558558
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
559559
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
560+
argsman.AddArg("-peerblockinputs", strprintf("Serve historical inputs to blocks (default: %u)", DEFAULT_PEERBLOCKUNDO), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
560561
argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
561562
argsman.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet3: %u, testnet4: %u, signet: %u, regtest: %u). Not relevant for I2P (see doc/i2p.md). If set to a value x, the default onion listening port will be set to x+1.", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), testnet4ChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
562563
const std::string proxy_doc_for_value =
@@ -984,6 +985,10 @@ bool AppInitParameterInteraction(const ArgsManager& args)
984985
g_local_services = ServiceFlags(g_local_services | NODE_COMPACT_FILTERS);
985986
}
986987

988+
if (args.GetBoolArg("-peerblockinputs"), DEFAULT_PEERBLOCKUNDO) {
989+
g_local_services = ServiceFlags(g_local_services | NODE_BLOCK_UNDO);
990+
}
991+
987992
if (args.GetIntArg("-prune", 0)) {
988993
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
989994
return InitError(_("Prune mode is incompatible with -txindex."));

src/net_processing.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include <tinyformat.h>
5757
#include <txmempool.h>
5858
#include <uint256.h>
59+
#include <undo.h>
5960
#include <util/check.h>
6061
#include <util/strencodings.h>
6162
#include <util/time.h>
@@ -2370,6 +2371,22 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
23702371
block_pos = pindex->GetBlockPos();
23712372
}
23722373

2374+
if (inv.IsMsgBlockUndo()) {
2375+
if ((peer.m_our_services & NODE_BLOCK_UNDO) != NODE_BLOCK_UNDO) {
2376+
LogDebug(BCLog::NET, "ignoring unsupported block undo request, %s\n", pfrom.DisconnectMsg(fLogIPs));
2377+
pfrom.fDisconnect = true;
2378+
return;
2379+
}
2380+
CBlockUndo block_undo{};
2381+
if (m_chainman.m_blockman.ReadBlockUndo(block_undo, *pindex)) {
2382+
auto hash = pindex->GetBlockHash();
2383+
NetworkBlockUndo undo{hash, block_undo};
2384+
MakeAndPushMessage(pfrom, NetMsgType::BLOCKUNDO, undo);
2385+
} else {
2386+
LogError("Cannot load block undo from disk, %s\n", pfrom.DisconnectMsg(fLogIPs));
2387+
}
2388+
}
2389+
23732390
std::shared_ptr<const CBlock> pblock;
23742391
if (a_recent_block && a_recent_block->GetHash() == inv.hash) {
23752392
pblock = a_recent_block;

src/net_processing.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ static constexpr bool DEFAULT_TXRECONCILIATION_ENABLE{false};
4242
static const uint32_t DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN{100};
4343
static const bool DEFAULT_PEERBLOOMFILTERS = false;
4444
static const bool DEFAULT_PEERBLOCKFILTERS = false;
45+
static const bool DEFAULT_PEERBLOCKUNDO = false;
4546
/** Maximum number of outstanding CMPCTBLOCK requests for the same block. */
4647
static const unsigned int MAX_CMPCTBLOCKS_INFLIGHT_PER_BLOCK = 3;
4748
/** Number of headers sent in one getheaders result. We rely on the assumption that if a peer sends

src/protocol.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ std::string CInv::GetMessageType() const
6969
case MSG_BLOCK: return cmd.append(NetMsgType::BLOCK);
7070
case MSG_FILTERED_BLOCK: return cmd.append(NetMsgType::MERKLEBLOCK);
7171
case MSG_CMPCT_BLOCK: return cmd.append(NetMsgType::CMPCTBLOCK);
72+
case MSG_BLOCK_UNDO: return cmd.append(NetMsgType::BLOCKUNDO);
7273
default:
7374
throw std::out_of_range(strprintf("CInv::GetMessageType(): type=%d unknown type", type));
7475
}
@@ -99,6 +100,7 @@ static std::string serviceFlagToStr(size_t bit)
99100
case NODE_COMPACT_FILTERS: return "COMPACT_FILTERS";
100101
case NODE_NETWORK_LIMITED: return "NETWORK_LIMITED";
101102
case NODE_P2P_V2: return "P2P_V2";
103+
case NODE_BLOCK_UNDO: return "BLOCK_UNDO";
102104
// Not using default, so we get warned when a case is missing
103105
}
104106

src/protocol.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ inline constexpr const char* GETBLOCKTXN{"getblocktxn"};
216216
* @since protocol version 70014 as described by BIP 152
217217
*/
218218
inline constexpr const char* BLOCKTXN{"blocktxn"};
219+
220+
inline constexpr const char* GETBLOCKUNDO{"getblockundo"};
221+
222+
inline constexpr const char* BLOCKUNDO{"blockundo"};
219223
/**
220224
* getcfilters requests compact filters for a range of blocks.
221225
* Only available with service bit NODE_COMPACT_FILTERS as described by
@@ -303,6 +307,8 @@ inline const std::array ALL_NET_MESSAGE_TYPES{std::to_array<std::string>({
303307
NetMsgType::CFCHECKPT,
304308
NetMsgType::WTXIDRELAY,
305309
NetMsgType::SENDTXRCNCL,
310+
NetMsgType::BLOCKUNDO,
311+
NetMsgType::GETBLOCKUNDO,
306312
})};
307313

308314
/** nServices flags */
@@ -336,6 +342,7 @@ enum ServiceFlags : uint64_t {
336342
// collisions and other cases where nodes may be advertising a service they
337343
// do not actually support. Other service bits should be allocated via the
338344
// BIP process.
345+
NODE_BLOCK_UNDO = (1 << 21),
339346
};
340347

341348
/**
@@ -484,6 +491,7 @@ enum GetDataMsg : uint32_t {
484491
MSG_CMPCT_BLOCK = 4, //!< Defined in BIP152
485492
MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, //!< Defined in BIP144
486493
MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG, //!< Defined in BIP144
494+
MSG_BLOCK_UNDO = 6,
487495
// MSG_FILTERED_WITNESS_BLOCK is defined in BIP144 as reserved for future
488496
// use and remains unused.
489497
// MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG,
@@ -510,6 +518,7 @@ class CInv
510518
bool IsMsgFilteredBlk() const { return type == MSG_FILTERED_BLOCK; }
511519
bool IsMsgCmpctBlk() const { return type == MSG_CMPCT_BLOCK; }
512520
bool IsMsgWitnessBlk() const { return type == MSG_WITNESS_BLOCK; }
521+
bool IsMsgBlockUndo() const { return type == MSG_BLOCK_UNDO; }
513522

514523
// Combined-message helper methods
515524
bool IsGenTxMsg() const

src/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ add_executable(test_bitcoin
120120
txvalidation_tests.cpp
121121
txvalidationcache_tests.cpp
122122
uint256_tests.cpp
123+
undo_tests.cpp
123124
util_check_tests.cpp
124125
util_expected_tests.cpp
125126
util_string_tests.cpp

src/test/undo_tests.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright (c) 2025-present 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+
#include <arith_uint256.h>
5+
#include <coins.h>
6+
#include <consensus/amount.h>
7+
#include <key.h>
8+
#include <primitives/transaction.h>
9+
#include <pubkey.h>
10+
#include <random.h>
11+
#include <script/script.h>
12+
#include <serialize.h>
13+
#include <streams.h>
14+
#include <test/util/setup_common.h>
15+
#include <undo.h>
16+
17+
#include <boost/test/unit_test.hpp>
18+
19+
namespace {
20+
CAmount amt_1{111};
21+
CAmount amt_2{4321};
22+
CAmount amt_3{12345};
23+
CAmount amt_4{94949};
24+
CAmount amt_5{5222322};
25+
CAmount amt_6{34112};
26+
int height_1{3};
27+
int height_2{424002};
28+
int height_3{2244002};
29+
int height_4{983999};
30+
int height_5{2455};
31+
int height_6{20};
32+
} // namespace
33+
34+
BOOST_FIXTURE_TEST_SUITE(undo_tests, BasicTestingSetup)
35+
36+
BOOST_AUTO_TEST_CASE(network_undo_serialization)
37+
{
38+
// case one
39+
CPubKey key_1 = GenerateRandomKey().GetPubKey();
40+
CScript script_1 = CScript() << OP_DUP << OP_HASH160 << ToByteVector(key_1.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
41+
Coin coin_1{
42+
CTxOut{amt_1, script_1},
43+
height_1,
44+
false,
45+
};
46+
// case two
47+
CScript redeem{};
48+
CScript script_2 = CScript() << OP_HASH160 << ToByteVector(CScriptID(redeem)) << OP_EQUAL;
49+
Coin coin_2{
50+
CTxOut{amt_2, script_2},
51+
height_2,
52+
false,
53+
};
54+
// case three
55+
CKey key_2 = GenerateRandomKey();
56+
CScript script_3 = CScript() << ToByteVector(key_2.GetPubKey()) << OP_CHECKSIG;
57+
Coin coin_3{
58+
CTxOut{amt_3, script_3},
59+
height_3,
60+
false,
61+
};
62+
// case four
63+
CKey key_3 = GenerateRandomKey(/*compressed=*/false);
64+
CScript script_4 = CScript() << ToByteVector(key_3.GetPubKey()) << OP_CHECKSIG;
65+
Coin coin_4{
66+
CTxOut{amt_4, script_4},
67+
height_4,
68+
false,
69+
};
70+
CKey key_4 = GenerateRandomKey();
71+
XOnlyPubKey x_only{key_4.GetPubKey()};
72+
CScript script_5 = CScript() << OP_1 << 0x20 << ToByteVector(x_only);
73+
Coin coin_5{
74+
CTxOut{amt_5, script_5},
75+
height_5,
76+
false,
77+
};
78+
CPubKey key_5 = GenerateRandomKey().GetPubKey();
79+
CScript script_6 = CScript() << OP_DUP << OP_HASH160 << ToByteVector(key_5.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
80+
Coin coin_6{
81+
CTxOut{amt_6, script_6},
82+
height_6,
83+
true,
84+
};
85+
86+
CTxUndo undo_1{std::vector{coin_1, coin_2}};
87+
CTxUndo undo_2{std::vector{coin_3, coin_4}};
88+
CTxUndo undo_3{std::vector{coin_5, coin_6}};
89+
CBlockUndo undo{std::vector{undo_1, undo_2, undo_3}};
90+
uint256 block_hash{GetRandHash()};
91+
NetworkBlockUndo want{block_hash, undo};
92+
BOOST_CHECK(want.m_coins.size() > 0);
93+
DataStream undo_byte_stream{};
94+
Serialize(undo_byte_stream, want);
95+
BOOST_CHECK(undo_byte_stream.size() > 0);
96+
NetworkBlockUndo got;
97+
Unserialize(undo_byte_stream, got);
98+
BOOST_CHECK_EQUAL(want.m_coins.size(), got.m_coins.size());
99+
for (size_t i = 0; i < want.m_coins.size(); ++i) {
100+
const auto& coin_1 = want.m_coins[i];
101+
const auto& coin_2 = got.m_coins[i];
102+
BOOST_CHECK_EQUAL(coin_1.fCoinBase, coin_2.fCoinBase);
103+
BOOST_CHECK_EQUAL(coin_1.nHeight, coin_2.nHeight);
104+
BOOST_CHECK_EQUAL(coin_1.out.nValue, coin_2.out.nValue);
105+
const auto sct_1{coin_1.out.scriptPubKey};
106+
const auto sct_2{coin_2.out.scriptPubKey};
107+
BOOST_CHECK(sct_1 == sct_2);
108+
}
109+
}
110+
111+
BOOST_AUTO_TEST_SUITE_END();

src/undo.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <consensus/consensus.h>
1212
#include <primitives/transaction.h>
1313
#include <serialize.h>
14+
#include <uint256.h>
1415

1516
/** Formatter for undo information for a CTxIn
1617
*
@@ -48,6 +49,26 @@ struct TxInUndoFormatter
4849
}
4950
};
5051

52+
struct NetworkCoinFormatter
53+
{
54+
template<typename Stream>
55+
void Ser(Stream& s, const Coin& coin) {
56+
::Serialize(s, coin.nHeight * uint32_t{2} + coin.fCoinBase);
57+
::Serialize(s, coin.out.nValue);
58+
::Serialize(s, Using<ScriptCompression>(coin.out.scriptPubKey));
59+
}
60+
61+
template<typename Stream>
62+
void Unser(Stream& s, Coin& coin) {
63+
uint32_t n_code = 0x00;
64+
::Unserialize(s, n_code);
65+
coin.nHeight = n_code >> 1;
66+
coin.fCoinBase = n_code & 1;
67+
::Unserialize(s, coin.out.nValue);
68+
::Unserialize(s, Using<ScriptCompression>(coin.out.scriptPubKey));
69+
}
70+
};
71+
5172
/** Undo information for a CTransaction */
5273
class CTxUndo
5374
{
@@ -67,4 +88,32 @@ class CBlockUndo
6788
SERIALIZE_METHODS(CBlockUndo, obj) { READWRITE(obj.vtxundo); }
6889
};
6990

91+
class NetworkBlockUndo
92+
{
93+
public:
94+
std::vector<Coin> m_coins;
95+
uint256 m_hash;
96+
97+
NetworkBlockUndo(): m_coins{}, m_hash{} {};
98+
99+
NetworkBlockUndo(uint256 hash, CBlockUndo& undo): m_hash{hash} {
100+
for (const auto& tx_undo : undo.vtxundo) {
101+
for (const auto& coin : tx_undo.vprevout) {
102+
m_coins.push_back(coin);
103+
}
104+
}
105+
}
106+
107+
template <typename Stream>
108+
void Serialize(Stream& s) const {
109+
::Serialize(s, m_hash);
110+
::Serialize(s, Using<VectorFormatter<NetworkCoinFormatter>>(m_coins));
111+
}
112+
113+
template <typename Stream>
114+
void Unserialize(Stream& s) {
115+
::Unserialize(s, m_hash);
116+
::Unserialize(s, Using<VectorFormatter<NetworkCoinFormatter>>(m_coins));
117+
}
118+
};
70119
#endif // BITCOIN_UNDO_H

0 commit comments

Comments
 (0)