Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
orphanage_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2022 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#include <arith_uint256.h>
7#include <pubkey.h>
8#include <script/sign.h>
10#include <test/util/random.h>
12#include <txorphanage.h>
13
14#include <array>
15#include <cstdint>
16
17#include <boost/test/unit_test.hpp>
18
19BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
20
22{
23public:
24 inline size_t CountOrphans() const
25 {
26 return m_orphans.size();
27 }
28
30 {
31 std::map<Wtxid, OrphanTx>::iterator it;
32 it = m_orphans.lower_bound(Wtxid::FromUint256(InsecureRand256()));
33 if (it == m_orphans.end())
34 it = m_orphans.begin();
35 return it->second.tx;
36 }
37};
38
40{
41 std::vector<unsigned char> keydata;
42 keydata = rand_ctx.randbytes(32);
43 key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn=*/true);
44 assert(key.IsValid());
45}
46
47// Creates a transaction with 2 outputs. Spends all outpoints. If outpoints is empty, spends a random one.
48static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& outpoints, FastRandomContext& det_rand)
49{
50 CKey key;
53 // If no outpoints are given, create a random one.
54 if (outpoints.empty()) {
55 tx.vin.emplace_back(Txid::FromUint256(det_rand.rand256()), 0);
56 } else {
57 for (const auto& outpoint : outpoints) {
58 tx.vin.emplace_back(outpoint);
59 }
60 }
61 // Ensure txid != wtxid
62 tx.vin[0].scriptWitness.stack.push_back({1});
63 tx.vout.resize(2);
64 tx.vout[0].nValue = CENT;
65 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
66 tx.vout[1].nValue = 3 * CENT;
67 tx.vout[1].scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(key.GetPubKey()));
68 return MakeTransactionRef(tx);
69}
70
71// Make another (not necessarily valid) tx with the same txid but different wtxid.
73{
74 CMutableTransaction tx(*ptx);
75 tx.vin[0].scriptWitness.stack.push_back({5});
76 auto mutated_tx = MakeTransactionRef(tx);
77 assert(ptx->GetHash() == mutated_tx->GetHash());
78 return mutated_tx;
79}
80
81static bool EqualTxns(const std::set<CTransactionRef>& set_txns, const std::vector<CTransactionRef>& vec_txns)
82{
83 if (vec_txns.size() != set_txns.size()) return false;
84 for (const auto& tx : vec_txns) {
85 if (!set_txns.contains(tx)) return false;
86 }
87 return true;
88}
89static bool EqualTxns(const std::set<CTransactionRef>& set_txns,
90 const std::vector<std::pair<CTransactionRef, NodeId>>& vec_txns)
91{
92 if (vec_txns.size() != set_txns.size()) return false;
93 for (const auto& [tx, nodeid] : vec_txns) {
94 if (!set_txns.contains(tx)) return false;
95 }
96 return true;
97}
98
99BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
100{
101 // This test had non-deterministic coverage due to
102 // randomly selected seeds.
103 // This seed is chosen so that all branches of the function
104 // ecdsa_signature_parse_der_lax are executed during this test.
105 // Specifically branches that run only when an ECDSA
106 // signature's R and S values have leading zeros.
108
109 TxOrphanageTest orphanage;
110 CKey key;
113 BOOST_CHECK(keystore.AddKey(key));
114
115 // Freeze time for length of test
117 SetMockTime(now);
118
119 // 50 orphan transactions:
120 for (int i = 0; i < 50; i++)
121 {
123 tx.vin.resize(1);
124 tx.vin[0].prevout.n = 0;
125 tx.vin[0].prevout.hash = Txid::FromUint256(InsecureRand256());
126 tx.vin[0].scriptSig << OP_1;
127 tx.vout.resize(1);
128 tx.vout[0].nValue = 1*CENT;
129 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
130
131 orphanage.AddTx(MakeTransactionRef(tx), i);
132 }
133
134 // ... and 50 that depend on other orphans:
135 for (int i = 0; i < 50; i++)
136 {
137 CTransactionRef txPrev = orphanage.RandomOrphan();
138
140 tx.vin.resize(1);
141 tx.vin[0].prevout.n = 0;
142 tx.vin[0].prevout.hash = txPrev->GetHash();
143 tx.vout.resize(1);
144 tx.vout[0].nValue = 1*CENT;
145 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
146 SignatureData empty;
147 BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
148
149 orphanage.AddTx(MakeTransactionRef(tx), i);
150 }
151
152 // This really-big orphan should be ignored:
153 for (int i = 0; i < 10; i++)
154 {
155 CTransactionRef txPrev = orphanage.RandomOrphan();
156
158 tx.vout.resize(1);
159 tx.vout[0].nValue = 1*CENT;
160 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
161 tx.vin.resize(2777);
162 for (unsigned int j = 0; j < tx.vin.size(); j++)
163 {
164 tx.vin[j].prevout.n = j;
165 tx.vin[j].prevout.hash = txPrev->GetHash();
166 }
167 SignatureData empty;
168 BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
169 // Reuse same signature for other inputs
170 // (they don't have to be valid for this test)
171 for (unsigned int j = 1; j < tx.vin.size(); j++)
172 tx.vin[j].scriptSig = tx.vin[0].scriptSig;
173
174 BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
175 }
176
177 size_t expected_num_orphans = orphanage.CountOrphans();
178
179 // Non-existent peer; nothing should be deleted
180 orphanage.EraseForPeer(/*peer=*/-1);
181 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
182
183 // Each of first three peers stored
184 // two transactions each.
185 for (NodeId i = 0; i < 3; i++)
186 {
187 orphanage.EraseForPeer(i);
188 expected_num_orphans -= 2;
189 BOOST_CHECK(orphanage.CountOrphans() == expected_num_orphans);
190 }
191
192 // Test LimitOrphanTxSize() function, nothing should timeout:
193 FastRandomContext rng{/*fDeterministic=*/true};
194 orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng);
195 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
196 expected_num_orphans -= 1;
197 orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng);
198 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
199 assert(expected_num_orphans > 40);
200 orphanage.LimitOrphans(40, rng);
201 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 40);
202 orphanage.LimitOrphans(10, rng);
203 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 10);
204 orphanage.LimitOrphans(0, rng);
205 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0);
206
207 // Add one more orphan, check timeout logic
208 auto timeout_tx = MakeTransactionSpending(/*outpoints=*/{}, rng);
209 orphanage.AddTx(timeout_tx, 0);
210 orphanage.LimitOrphans(1, rng);
211 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
212
213 // One second shy of expiration
215 orphanage.LimitOrphans(1, rng);
216 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
217
218 // Jump one more second, orphan should be timed out on limiting
220 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
221 orphanage.LimitOrphans(1, rng);
222 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0);
223}
224
225BOOST_AUTO_TEST_CASE(same_txid_diff_witness)
226{
227 FastRandomContext det_rand{true};
228 TxOrphanage orphanage;
229 NodeId peer{0};
230
231 std::vector<COutPoint> empty_outpoints;
232 auto parent = MakeTransactionSpending(empty_outpoints, det_rand);
233
234 // Create children to go into orphanage.
235 auto child_normal = MakeTransactionSpending({{parent->GetHash(), 0}}, det_rand);
236 auto child_mutated = MakeMutation(child_normal);
237
238 const auto& normal_wtxid = child_normal->GetWitnessHash();
239 const auto& mutated_wtxid = child_mutated->GetWitnessHash();
240 BOOST_CHECK(normal_wtxid != mutated_wtxid);
241
242 BOOST_CHECK(orphanage.AddTx(child_normal, peer));
243 // EraseTx fails as transaction by this wtxid doesn't exist.
244 BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 0);
245 BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
246 BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
247
248 // Must succeed. Both transactions should be present in orphanage.
249 BOOST_CHECK(orphanage.AddTx(child_mutated, peer));
250 BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
251 BOOST_CHECK(orphanage.HaveTx(mutated_wtxid));
252
253 // Outpoints map should track all entries: check that both are returned as children of the parent.
254 std::set<CTransactionRef> expected_children{child_normal, child_mutated};
255 BOOST_CHECK(EqualTxns(expected_children, orphanage.GetChildrenFromSamePeer(parent, peer)));
256
257 // Erase by wtxid: mutated first
258 BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 1);
259 BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
260 BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
261
262 BOOST_CHECK_EQUAL(orphanage.EraseTx(normal_wtxid), 1);
263 BOOST_CHECK(!orphanage.HaveTx(normal_wtxid));
264 BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
265}
266
267
269{
270 FastRandomContext det_rand{true};
271 std::vector<COutPoint> empty_outpoints;
272
273 auto parent1 = MakeTransactionSpending(empty_outpoints, det_rand);
274 auto parent2 = MakeTransactionSpending(empty_outpoints, det_rand);
275
276 // Make sure these parents have different txids otherwise this test won't make sense.
277 while (parent1->GetHash() == parent2->GetHash()) {
278 parent2 = MakeTransactionSpending(empty_outpoints, det_rand);
279 }
280
281 // Create children to go into orphanage.
282 auto child_p1n0 = MakeTransactionSpending({{parent1->GetHash(), 0}}, det_rand);
283 auto child_p2n1 = MakeTransactionSpending({{parent2->GetHash(), 1}}, det_rand);
284 // Spends the same tx twice. Should not cause duplicates.
285 auto child_p1n0_p1n1 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent1->GetHash(), 1}}, det_rand);
286 // Spends the same outpoint as previous tx. Should still be returned; don't assume outpoints are unique.
287 auto child_p1n0_p2n0 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent2->GetHash(), 0}}, det_rand);
288
289 const NodeId node1{1};
290 const NodeId node2{2};
291
292 // All orphans provided by node1
293 {
294 TxOrphanage orphanage;
295 BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
296 BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
297 BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node1));
298 BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node1));
299
300 std::set<CTransactionRef> expected_parent1_children{child_p1n0, child_p1n0_p2n0, child_p1n0_p1n1};
301 std::set<CTransactionRef> expected_parent2_children{child_p2n1, child_p1n0_p2n0};
302
303 BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage.GetChildrenFromSamePeer(parent1, node1)));
304 BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromSamePeer(parent2, node1)));
305
306 BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage.GetChildrenFromDifferentPeer(parent1, node2)));
307 BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromDifferentPeer(parent2, node2)));
308
309 // The peer must match
310 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent1, node2).empty());
311 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent2, node2).empty());
312
313 // There shouldn't be any children of this tx in the orphanage
314 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node1).empty());
315 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node2).empty());
316 BOOST_CHECK(orphanage.GetChildrenFromDifferentPeer(child_p1n0_p2n0, node1).empty());
317 BOOST_CHECK(orphanage.GetChildrenFromDifferentPeer(child_p1n0_p2n0, node2).empty());
318 }
319
320 // Orphans provided by node1 and node2
321 {
322 TxOrphanage orphanage;
323 BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
324 BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
325 BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node2));
326 BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node2));
327
328 // +----------------+---------------+----------------------------------+
329 // | | sender=node1 | sender=node2 |
330 // +----------------+---------------+----------------------------------+
331 // | spends parent1 | child_p1n0 | child_p1n0_p1n1, child_p1n0_p2n0 |
332 // | spends parent2 | child_p2n1 | child_p1n0_p2n0 |
333 // +----------------+---------------+----------------------------------+
334
335 // Children of parent1 from node1:
336 {
337 std::set<CTransactionRef> expected_parent1_node1{child_p1n0};
338
339 BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage.GetChildrenFromSamePeer(parent1, node1)));
340 BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage.GetChildrenFromDifferentPeer(parent1, node2)));
341 }
342
343 // Children of parent2 from node1:
344 {
345 std::set<CTransactionRef> expected_parent2_node1{child_p2n1};
346
347 BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage.GetChildrenFromSamePeer(parent2, node1)));
348 BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage.GetChildrenFromDifferentPeer(parent2, node2)));
349 }
350
351 // Children of parent1 from node2:
352 {
353 std::set<CTransactionRef> expected_parent1_node2{child_p1n0_p1n1, child_p1n0_p2n0};
354
355 BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage.GetChildrenFromSamePeer(parent1, node2)));
356 BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage.GetChildrenFromDifferentPeer(parent1, node1)));
357 }
358
359 // Children of parent2 from node2:
360 {
361 std::set<CTransactionRef> expected_parent2_node2{child_p1n0_p2n0};
362
363 BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage.GetChildrenFromSamePeer(parent2, node2)));
364 BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage.GetChildrenFromDifferentPeer(parent2, node1)));
365 }
366 }
367}
368
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
An encapsulated private key.
Definition key.h:35
bool IsValid() const
Check whether this private key is valid.
Definition key.h:123
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition key.cpp:182
void Set(const T pbegin, const T pend, bool fCompressedIn)
Initialize using begin and end iterators to byte data.
Definition key.h:103
Fast randomness source.
Definition random.h:377
void Reseed(const uint256 &seed) noexcept
Reseed with explicit seed (only for testing).
Definition random.cpp:707
Fillable signing provider that keeps keys in an address->secret map.
virtual bool AddKey(const CKey &key)
uint256 rand256() noexcept
generate a random uint256.
Definition random.h:308
A class to track orphan transactions (failed on TX_MISSING_INPUTS) Since we cannot distinguish orphan...
Definition txorphanage.h:28
bool AddTx(const CTransactionRef &tx, NodeId peer)
Add a new orphan transaction.
void EraseForPeer(NodeId peer)
Erase all orphans announced by a peer (eg, after that peer disconnects)
void LimitOrphans(unsigned int max_orphans, FastRandomContext &rng)
Limit the orphanage to the given maximum.
int EraseTx(const Wtxid &wtxid)
Erase an orphan by wtxid.
std::vector< std::pair< CTransactionRef, NodeId > > GetChildrenFromDifferentPeer(const CTransactionRef &parent, NodeId nodeid) const
Get all children that spend from this tx but were not received from nodeid.
std::vector< CTransactionRef > GetChildrenFromSamePeer(const CTransactionRef &parent, NodeId nodeid) const
Get all children that spend from this tx and were received from nodeid.
bool HaveTx(const Wtxid &wtxid) const
Check if we already have an orphan transaction (by wtxid only)
size_t CountOrphans() const
CTransactionRef RandomOrphan()
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition uint256.h:178
BOOST_AUTO_TEST_SUITE_END()
@ SIGHASH_ALL
Definition interpreter.h:30
int64_t NodeId
Definition net.h:97
#define BOOST_CHECK_EQUAL(v1, v2)
Definition object.cpp:18
#define BOOST_CHECK(expr)
Definition object.cpp:17
static CTransactionRef MakeMutation(const CTransactionRef &ptx)
BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
static void MakeNewKeyWithFastRandomContext(CKey &key, FastRandomContext &rand_ctx=g_insecure_rand_ctx)
static CTransactionRef MakeTransactionSpending(const std::vector< COutPoint > &outpoints, FastRandomContext &det_rand)
static bool EqualTxns(const std::set< CTransactionRef > &set_txns, const std::vector< CTransactionRef > &vec_txns)
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
@ OP_1
Definition script.h:82
static constexpr CAmount CENT
bool SignSignature(const SigningProvider &provider, const CScript &fromPubKey, CMutableTransaction &txTo, unsigned int nIn, const CAmount &amount, int nHashType, SignatureData &sig_data)
Produce a satisfying script (scriptSig or witness).
Definition sign.cpp:697
A mutable version of CTransaction.
std::vector< CTxOut > vout
std::vector< CTxIn > vin
Testing setup that configures a complete environment.
FastRandomContext g_insecure_rand_ctx
This global and the helpers that use it are not thread-safe.
Definition random.cpp:14
static uint256 InsecureRand256()
Definition random.h:35
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
Definition time.cpp:44
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition time.cpp:32
static constexpr auto ORPHAN_TX_EXPIRE_TIME
Expiration time for orphan transactions.
Definition txorphanage.h:18
assert(!tx.IsCoinBase())