Bitcoin Core 28.99.0
P2P Digital Currency
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 <policy/policy.h>
9#include <pubkey.h>
10#include <script/sign.h>
12#include <test/util/random.h>
15#include <txorphanage.h>
16
17#include <array>
18#include <cstdint>
19
20#include <boost/test/unit_test.hpp>
21
23
25{
26public:
27 TxOrphanageTest(FastRandomContext& rng) : m_rng{rng} {}
28
29 inline size_t CountOrphans() const
30 {
31 return m_orphans.size();
32 }
33
35 {
36 std::map<Wtxid, OrphanTx>::iterator it;
37 it = m_orphans.lower_bound(Wtxid::FromUint256(m_rng.rand256()));
38 if (it == m_orphans.end())
39 it = m_orphans.begin();
40 return it->second.tx;
41 }
42
44};
45
47{
48 std::vector<unsigned char> keydata;
49 keydata = rand_ctx.randbytes(32);
50 key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn=*/true);
51 assert(key.IsValid());
52}
53
54// Creates a transaction with 2 outputs. Spends all outpoints. If outpoints is empty, spends a random one.
55static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& outpoints, FastRandomContext& det_rand)
56{
57 CKey key;
60 // If no outpoints are given, create a random one.
61 if (outpoints.empty()) {
62 tx.vin.emplace_back(Txid::FromUint256(det_rand.rand256()), 0);
63 } else {
64 for (const auto& outpoint : outpoints) {
65 tx.vin.emplace_back(outpoint);
66 }
67 }
68 // Ensure txid != wtxid
69 tx.vin[0].scriptWitness.stack.push_back({1});
70 tx.vout.resize(2);
71 tx.vout[0].nValue = CENT;
72 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
73 tx.vout[1].nValue = 3 * CENT;
74 tx.vout[1].scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(key.GetPubKey()));
75 return MakeTransactionRef(tx);
76}
77
78// Make another (not necessarily valid) tx with the same txid but different wtxid.
80{
81 CMutableTransaction tx(*ptx);
82 tx.vin[0].scriptWitness.stack.push_back({5});
83 auto mutated_tx = MakeTransactionRef(tx);
84 assert(ptx->GetHash() == mutated_tx->GetHash());
85 return mutated_tx;
86}
87
88static bool EqualTxns(const std::set<CTransactionRef>& set_txns, const std::vector<CTransactionRef>& vec_txns)
89{
90 if (vec_txns.size() != set_txns.size()) return false;
91 for (const auto& tx : vec_txns) {
92 if (!set_txns.contains(tx)) return false;
93 }
94 return true;
95}
96static bool EqualTxns(const std::set<CTransactionRef>& set_txns,
97 const std::vector<std::pair<CTransactionRef, NodeId>>& vec_txns)
98{
99 if (vec_txns.size() != set_txns.size()) return false;
100 for (const auto& [tx, nodeid] : vec_txns) {
101 if (!set_txns.contains(tx)) return false;
102 }
103 return true;
104}
105
106BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
107{
108 // This test had non-deterministic coverage due to
109 // randomly selected seeds.
110 // This seed is chosen so that all branches of the function
111 // ecdsa_signature_parse_der_lax are executed during this test.
112 // Specifically branches that run only when an ECDSA
113 // signature's R and S values have leading zeros.
114 m_rng.Reseed(uint256{33});
115
116 TxOrphanageTest orphanage{m_rng};
117 CKey key;
120 BOOST_CHECK(keystore.AddKey(key));
121
122 // Freeze time for length of test
123 auto now{GetTime<std::chrono::seconds>()};
124 SetMockTime(now);
125
126 // 50 orphan transactions:
127 for (int i = 0; i < 50; i++)
128 {
130 tx.vin.resize(1);
131 tx.vin[0].prevout.n = 0;
132 tx.vin[0].prevout.hash = Txid::FromUint256(m_rng.rand256());
133 tx.vin[0].scriptSig << OP_1;
134 tx.vout.resize(1);
135 tx.vout[0].nValue = 1*CENT;
136 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
137
138 orphanage.AddTx(MakeTransactionRef(tx), i);
139 }
140
141 // ... and 50 that depend on other orphans:
142 for (int i = 0; i < 50; i++)
143 {
144 CTransactionRef txPrev = orphanage.RandomOrphan();
145
147 tx.vin.resize(1);
148 tx.vin[0].prevout.n = 0;
149 tx.vin[0].prevout.hash = txPrev->GetHash();
150 tx.vout.resize(1);
151 tx.vout[0].nValue = 1*CENT;
152 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
153 SignatureData empty;
154 BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
155
156 orphanage.AddTx(MakeTransactionRef(tx), i);
157 }
158
159 // This really-big orphan should be ignored:
160 for (int i = 0; i < 10; i++)
161 {
162 CTransactionRef txPrev = orphanage.RandomOrphan();
163
165 tx.vout.resize(1);
166 tx.vout[0].nValue = 1*CENT;
167 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
168 tx.vin.resize(2777);
169 for (unsigned int j = 0; j < tx.vin.size(); j++)
170 {
171 tx.vin[j].prevout.n = j;
172 tx.vin[j].prevout.hash = txPrev->GetHash();
173 }
174 SignatureData empty;
175 BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
176 // Reuse same signature for other inputs
177 // (they don't have to be valid for this test)
178 for (unsigned int j = 1; j < tx.vin.size(); j++)
179 tx.vin[j].scriptSig = tx.vin[0].scriptSig;
180
181 BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
182 }
183
184 size_t expected_num_orphans = orphanage.CountOrphans();
185
186 // Non-existent peer; nothing should be deleted
187 orphanage.EraseForPeer(/*peer=*/-1);
188 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
189
190 // Each of first three peers stored
191 // two transactions each.
192 for (NodeId i = 0; i < 3; i++)
193 {
194 orphanage.EraseForPeer(i);
195 expected_num_orphans -= 2;
196 BOOST_CHECK(orphanage.CountOrphans() == expected_num_orphans);
197 }
198
199 // Test LimitOrphanTxSize() function, nothing should timeout:
200 FastRandomContext rng{/*fDeterministic=*/true};
201 orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng);
202 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
203 expected_num_orphans -= 1;
204 orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng);
205 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
206 assert(expected_num_orphans > 40);
207 orphanage.LimitOrphans(40, rng);
208 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 40);
209 orphanage.LimitOrphans(10, rng);
210 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 10);
211 orphanage.LimitOrphans(0, rng);
212 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0);
213
214 // Add one more orphan, check timeout logic
215 auto timeout_tx = MakeTransactionSpending(/*outpoints=*/{}, rng);
216 orphanage.AddTx(timeout_tx, 0);
217 orphanage.LimitOrphans(1, rng);
218 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
219
220 // One second shy of expiration
222 orphanage.LimitOrphans(1, rng);
223 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
224
225 // Jump one more second, orphan should be timed out on limiting
227 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
228 orphanage.LimitOrphans(1, rng);
229 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0);
230}
231
232BOOST_AUTO_TEST_CASE(same_txid_diff_witness)
233{
234 FastRandomContext det_rand{true};
235 TxOrphanage orphanage;
236 NodeId peer{0};
237
238 std::vector<COutPoint> empty_outpoints;
239 auto parent = MakeTransactionSpending(empty_outpoints, det_rand);
240
241 // Create children to go into orphanage.
242 auto child_normal = MakeTransactionSpending({{parent->GetHash(), 0}}, det_rand);
243 auto child_mutated = MakeMutation(child_normal);
244
245 const auto& normal_wtxid = child_normal->GetWitnessHash();
246 const auto& mutated_wtxid = child_mutated->GetWitnessHash();
247 BOOST_CHECK(normal_wtxid != mutated_wtxid);
248
249 BOOST_CHECK(orphanage.AddTx(child_normal, peer));
250 // EraseTx fails as transaction by this wtxid doesn't exist.
251 BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 0);
252 BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
253 BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
254
255 // Must succeed. Both transactions should be present in orphanage.
256 BOOST_CHECK(orphanage.AddTx(child_mutated, peer));
257 BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
258 BOOST_CHECK(orphanage.HaveTx(mutated_wtxid));
259
260 // Outpoints map should track all entries: check that both are returned as children of the parent.
261 std::set<CTransactionRef> expected_children{child_normal, child_mutated};
262 BOOST_CHECK(EqualTxns(expected_children, orphanage.GetChildrenFromSamePeer(parent, peer)));
263
264 // Erase by wtxid: mutated first
265 BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 1);
266 BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
267 BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
268
269 BOOST_CHECK_EQUAL(orphanage.EraseTx(normal_wtxid), 1);
270 BOOST_CHECK(!orphanage.HaveTx(normal_wtxid));
271 BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
272}
273
274
276{
277 FastRandomContext det_rand{true};
278 std::vector<COutPoint> empty_outpoints;
279
280 auto parent1 = MakeTransactionSpending(empty_outpoints, det_rand);
281 auto parent2 = MakeTransactionSpending(empty_outpoints, det_rand);
282
283 // Make sure these parents have different txids otherwise this test won't make sense.
284 while (parent1->GetHash() == parent2->GetHash()) {
285 parent2 = MakeTransactionSpending(empty_outpoints, det_rand);
286 }
287
288 // Create children to go into orphanage.
289 auto child_p1n0 = MakeTransactionSpending({{parent1->GetHash(), 0}}, det_rand);
290 auto child_p2n1 = MakeTransactionSpending({{parent2->GetHash(), 1}}, det_rand);
291 // Spends the same tx twice. Should not cause duplicates.
292 auto child_p1n0_p1n1 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent1->GetHash(), 1}}, det_rand);
293 // Spends the same outpoint as previous tx. Should still be returned; don't assume outpoints are unique.
294 auto child_p1n0_p2n0 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent2->GetHash(), 0}}, det_rand);
295
296 const NodeId node1{1};
297 const NodeId node2{2};
298
299 // All orphans provided by node1
300 {
301 TxOrphanage orphanage;
302 BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
303 BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
304 BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node1));
305 BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node1));
306
307 std::set<CTransactionRef> expected_parent1_children{child_p1n0, child_p1n0_p2n0, child_p1n0_p1n1};
308 std::set<CTransactionRef> expected_parent2_children{child_p2n1, child_p1n0_p2n0};
309
310 BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage.GetChildrenFromSamePeer(parent1, node1)));
311 BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromSamePeer(parent2, node1)));
312
313 BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage.GetChildrenFromDifferentPeer(parent1, node2)));
314 BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromDifferentPeer(parent2, node2)));
315
316 // The peer must match
317 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent1, node2).empty());
318 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent2, node2).empty());
319
320 // There shouldn't be any children of this tx in the orphanage
321 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node1).empty());
322 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node2).empty());
323 BOOST_CHECK(orphanage.GetChildrenFromDifferentPeer(child_p1n0_p2n0, node1).empty());
324 BOOST_CHECK(orphanage.GetChildrenFromDifferentPeer(child_p1n0_p2n0, node2).empty());
325 }
326
327 // Orphans provided by node1 and node2
328 {
329 TxOrphanage orphanage;
330 BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
331 BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
332 BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node2));
333 BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node2));
334
335 // +----------------+---------------+----------------------------------+
336 // | | sender=node1 | sender=node2 |
337 // +----------------+---------------+----------------------------------+
338 // | spends parent1 | child_p1n0 | child_p1n0_p1n1, child_p1n0_p2n0 |
339 // | spends parent2 | child_p2n1 | child_p1n0_p2n0 |
340 // +----------------+---------------+----------------------------------+
341
342 // Children of parent1 from node1:
343 {
344 std::set<CTransactionRef> expected_parent1_node1{child_p1n0};
345
346 BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage.GetChildrenFromSamePeer(parent1, node1)));
347 BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage.GetChildrenFromDifferentPeer(parent1, node2)));
348 }
349
350 // Children of parent2 from node1:
351 {
352 std::set<CTransactionRef> expected_parent2_node1{child_p2n1};
353
354 BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage.GetChildrenFromSamePeer(parent2, node1)));
355 BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage.GetChildrenFromDifferentPeer(parent2, node2)));
356 }
357
358 // Children of parent1 from node2:
359 {
360 std::set<CTransactionRef> expected_parent1_node2{child_p1n0_p1n1, child_p1n0_p2n0};
361
362 BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage.GetChildrenFromSamePeer(parent1, node2)));
363 BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage.GetChildrenFromDifferentPeer(parent1, node1)));
364 }
365
366 // Children of parent2 from node2:
367 {
368 std::set<CTransactionRef> expected_parent2_node2{child_p1n0_p2n0};
369
370 BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage.GetChildrenFromSamePeer(parent2, node2)));
371 BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage.GetChildrenFromDifferentPeer(parent2, node1)));
372 }
373 }
374}
375
376BOOST_AUTO_TEST_CASE(too_large_orphan_tx)
377{
378 TxOrphanage orphanage;
380 tx.vin.resize(1);
381
382 // check that txs larger than MAX_STANDARD_TX_WEIGHT are not added to the orphanage
385 BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), 0));
386
387 tx.vout.clear();
390 BOOST_CHECK(orphanage.AddTx(MakeTransactionRef(tx), 0));
391}
392
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
The basic transaction that is broadcasted on the network and contained in blocks.
Definition: transaction.h:296
Fast randomness source.
Definition: random.h:377
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
std::vector< B > randbytes(size_t len) noexcept
Generate random bytes.
Definition: random.h:297
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.
Definition: txorphanage.cpp:15
int EraseTx(const Wtxid &wtxid)
Erase an orphan by wtxid.
Definition: txorphanage.cpp:48
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)
FastRandomContext & m_rng
size_t CountOrphans() const
CTransactionRef RandomOrphan()
TxOrphanageTest(FastRandomContext &rng)
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition: uint256.h:190
static int32_t GetTransactionWeight(const CTransaction &tx)
Definition: validation.h:133
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
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)
static void MakeNewKeyWithFastRandomContext(CKey &key, FastRandomContext &rand_ctx)
BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
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 constexpr int32_t MAX_STANDARD_TX_WEIGHT
The maximum weight for transactions we're willing to relay/mine.
Definition: policy.h:27
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
@ OP_1
Definition: script.h:83
static constexpr CAmount CENT
Definition: setup_common.h:46
A mutable version of CTransaction.
Definition: transaction.h:378
std::vector< CTxOut > vout
Definition: transaction.h:380
std::vector< CTxIn > vin
Definition: transaction.h:379
Testing setup that configures a complete environment.
Definition: setup_common.h:120
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:35
void BulkTransaction(CMutableTransaction &tx, int32_t target_weight)
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).
static constexpr auto ORPHAN_TX_EXPIRE_TIME
Expiration time for orphan transactions.
Definition: txorphanage.h:18
assert(!tx.IsCoinBase())