Bitcoin Core  27.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 <pubkey.h>
8 #include <script/sign.h>
10 #include <test/util/random.h>
11 #include <test/util/setup_common.h>
12 #include <txorphanage.h>
13 
14 #include <array>
15 #include <cstdint>
16 
17 #include <boost/test/unit_test.hpp>
18 
19 BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
20 
22 {
23 public:
24  inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
25  {
26  LOCK(m_mutex);
27  return m_orphans.size();
28  }
29 
31  {
32  LOCK(m_mutex);
33  std::map<Txid, OrphanTx>::iterator it;
34  it = m_orphans.lower_bound(Txid::FromUint256(InsecureRand256()));
35  if (it == m_orphans.end())
36  it = m_orphans.begin();
37  return it->second.tx;
38  }
39 };
40 
42 {
43  std::vector<unsigned char> keydata;
44  keydata = rand_ctx.randbytes(32);
45  key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn=*/true);
46  assert(key.IsValid());
47 }
48 
49 // Creates a transaction with 2 outputs. Spends all outpoints. If outpoints is empty, spends a random one.
50 static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& outpoints, FastRandomContext& det_rand)
51 {
52  CKey key;
53  MakeNewKeyWithFastRandomContext(key, det_rand);
55  // If no outpoints are given, create a random one.
56  if (outpoints.empty()) {
57  tx.vin.emplace_back(Txid::FromUint256(det_rand.rand256()), 0);
58  } else {
59  for (const auto& outpoint : outpoints) {
60  tx.vin.emplace_back(outpoint);
61  }
62  }
63  // Ensure txid != wtxid
64  tx.vin[0].scriptWitness.stack.push_back({1});
65  tx.vout.resize(2);
66  tx.vout[0].nValue = CENT;
67  tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
68  tx.vout[1].nValue = 3 * CENT;
69  tx.vout[1].scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(key.GetPubKey()));
70  return MakeTransactionRef(tx);
71 }
72 
73 static bool EqualTxns(const std::set<CTransactionRef>& set_txns, const std::vector<CTransactionRef>& vec_txns)
74 {
75  if (vec_txns.size() != set_txns.size()) return false;
76  for (const auto& tx : vec_txns) {
77  if (!set_txns.contains(tx)) return false;
78  }
79  return true;
80 }
81 static bool EqualTxns(const std::set<CTransactionRef>& set_txns,
82  const std::vector<std::pair<CTransactionRef, NodeId>>& vec_txns)
83 {
84  if (vec_txns.size() != set_txns.size()) return false;
85  for (const auto& [tx, nodeid] : vec_txns) {
86  if (!set_txns.contains(tx)) return false;
87  }
88  return true;
89 }
90 
91 BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
92 {
93  // This test had non-deterministic coverage due to
94  // randomly selected seeds.
95  // This seed is chosen so that all branches of the function
96  // ecdsa_signature_parse_der_lax are executed during this test.
97  // Specifically branches that run only when an ECDSA
98  // signature's R and S values have leading zeros.
100 
101  TxOrphanageTest orphanage;
102  CKey key;
104  FillableSigningProvider keystore;
105  BOOST_CHECK(keystore.AddKey(key));
106 
107  // 50 orphan transactions:
108  for (int i = 0; i < 50; i++)
109  {
111  tx.vin.resize(1);
112  tx.vin[0].prevout.n = 0;
113  tx.vin[0].prevout.hash = Txid::FromUint256(InsecureRand256());
114  tx.vin[0].scriptSig << OP_1;
115  tx.vout.resize(1);
116  tx.vout[0].nValue = 1*CENT;
117  tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
118 
119  orphanage.AddTx(MakeTransactionRef(tx), i);
120  }
121 
122  // ... and 50 that depend on other orphans:
123  for (int i = 0; i < 50; i++)
124  {
125  CTransactionRef txPrev = orphanage.RandomOrphan();
126 
128  tx.vin.resize(1);
129  tx.vin[0].prevout.n = 0;
130  tx.vin[0].prevout.hash = txPrev->GetHash();
131  tx.vout.resize(1);
132  tx.vout[0].nValue = 1*CENT;
133  tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
134  SignatureData empty;
135  BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
136 
137  orphanage.AddTx(MakeTransactionRef(tx), i);
138  }
139 
140  // This really-big orphan should be ignored:
141  for (int i = 0; i < 10; i++)
142  {
143  CTransactionRef txPrev = orphanage.RandomOrphan();
144 
146  tx.vout.resize(1);
147  tx.vout[0].nValue = 1*CENT;
148  tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
149  tx.vin.resize(2777);
150  for (unsigned int j = 0; j < tx.vin.size(); j++)
151  {
152  tx.vin[j].prevout.n = j;
153  tx.vin[j].prevout.hash = txPrev->GetHash();
154  }
155  SignatureData empty;
156  BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
157  // Reuse same signature for other inputs
158  // (they don't have to be valid for this test)
159  for (unsigned int j = 1; j < tx.vin.size(); j++)
160  tx.vin[j].scriptSig = tx.vin[0].scriptSig;
161 
162  BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
163  }
164 
165  // Test EraseOrphansFor:
166  for (NodeId i = 0; i < 3; i++)
167  {
168  size_t sizeBefore = orphanage.CountOrphans();
169  orphanage.EraseForPeer(i);
170  BOOST_CHECK(orphanage.CountOrphans() < sizeBefore);
171  }
172 
173  // Test LimitOrphanTxSize() function:
174  FastRandomContext rng{/*fDeterministic=*/true};
175  orphanage.LimitOrphans(40, rng);
176  BOOST_CHECK(orphanage.CountOrphans() <= 40);
177  orphanage.LimitOrphans(10, rng);
178  BOOST_CHECK(orphanage.CountOrphans() <= 10);
179  orphanage.LimitOrphans(0, rng);
180  BOOST_CHECK(orphanage.CountOrphans() == 0);
181 }
182 
183 BOOST_AUTO_TEST_CASE(get_children)
184 {
185  FastRandomContext det_rand{true};
186  std::vector<COutPoint> empty_outpoints;
187 
188  auto parent1 = MakeTransactionSpending(empty_outpoints, det_rand);
189  auto parent2 = MakeTransactionSpending(empty_outpoints, det_rand);
190 
191  // Make sure these parents have different txids otherwise this test won't make sense.
192  while (parent1->GetHash() == parent2->GetHash()) {
193  parent2 = MakeTransactionSpending(empty_outpoints, det_rand);
194  }
195 
196  // Create children to go into orphanage.
197  auto child_p1n0 = MakeTransactionSpending({{parent1->GetHash(), 0}}, det_rand);
198  auto child_p2n1 = MakeTransactionSpending({{parent2->GetHash(), 1}}, det_rand);
199  // Spends the same tx twice. Should not cause duplicates.
200  auto child_p1n0_p1n1 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent1->GetHash(), 1}}, det_rand);
201  // Spends the same outpoint as previous tx. Should still be returned; don't assume outpoints are unique.
202  auto child_p1n0_p2n0 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent2->GetHash(), 0}}, det_rand);
203 
204  const NodeId node1{1};
205  const NodeId node2{2};
206 
207  // All orphans provided by node1
208  {
209  TxOrphanage orphanage;
210  BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
211  BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
212  BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node1));
213  BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node1));
214 
215  std::set<CTransactionRef> expected_parent1_children{child_p1n0, child_p1n0_p2n0, child_p1n0_p1n1};
216  std::set<CTransactionRef> expected_parent2_children{child_p2n1, child_p1n0_p2n0};
217 
218  BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage.GetChildrenFromSamePeer(parent1, node1)));
219  BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromSamePeer(parent2, node1)));
220 
221  BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage.GetChildrenFromDifferentPeer(parent1, node2)));
222  BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromDifferentPeer(parent2, node2)));
223 
224  // The peer must match
225  BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent1, node2).empty());
226  BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent2, node2).empty());
227 
228  // There shouldn't be any children of this tx in the orphanage
229  BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node1).empty());
230  BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node2).empty());
231  BOOST_CHECK(orphanage.GetChildrenFromDifferentPeer(child_p1n0_p2n0, node1).empty());
232  BOOST_CHECK(orphanage.GetChildrenFromDifferentPeer(child_p1n0_p2n0, node2).empty());
233  }
234 
235  // Orphans provided by node1 and node2
236  {
237  TxOrphanage orphanage;
238  BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
239  BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
240  BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node2));
241  BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node2));
242 
243  // +----------------+---------------+----------------------------------+
244  // | | sender=node1 | sender=node2 |
245  // +----------------+---------------+----------------------------------+
246  // | spends parent1 | child_p1n0 | child_p1n0_p1n1, child_p1n0_p2n0 |
247  // | spends parent2 | child_p2n1 | child_p1n0_p2n0 |
248  // +----------------+---------------+----------------------------------+
249 
250  // Children of parent1 from node1:
251  {
252  std::set<CTransactionRef> expected_parent1_node1{child_p1n0};
253 
254  BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage.GetChildrenFromSamePeer(parent1, node1)));
255  BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage.GetChildrenFromDifferentPeer(parent1, node2)));
256  }
257 
258  // Children of parent2 from node1:
259  {
260  std::set<CTransactionRef> expected_parent2_node1{child_p2n1};
261 
262  BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage.GetChildrenFromSamePeer(parent2, node1)));
263  BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage.GetChildrenFromDifferentPeer(parent2, node2)));
264  }
265 
266  // Children of parent1 from node2:
267  {
268  std::set<CTransactionRef> expected_parent1_node2{child_p1n0_p1n1, child_p1n0_p2n0};
269 
270  BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage.GetChildrenFromSamePeer(parent1, node2)));
271  BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage.GetChildrenFromDifferentPeer(parent1, node1)));
272  }
273 
274  // Children of parent2 from node2:
275  {
276  std::set<CTransactionRef> expected_parent2_node2{child_p1n0_p2n0};
277 
278  BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage.GetChildrenFromSamePeer(parent2, node2)));
279  BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage.GetChildrenFromDifferentPeer(parent2, node1)));
280  }
281  }
282 }
283 
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
An encapsulated private key.
Definition: key.h:33
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:119
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:188
void Set(const T pbegin, const T pend, bool fCompressedIn)
Initialize using begin and end iterators to byte data.
Definition: key.h:99
Fast randomness source.
Definition: random.h:145
uint256 rand256() noexcept
generate a random uint256.
Definition: random.cpp:664
Fillable signing provider that keeps keys in an address->secret map.
virtual bool AddKey(const CKey &key)
A class to track orphan transactions (failed on TX_MISSING_INPUTS) Since we cannot distinguish orphan...
Definition: txorphanage.h:21
void LimitOrphans(unsigned int max_orphans, FastRandomContext &rng) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Limit the orphanage to the given maximum.
bool AddTx(const CTransactionRef &tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Add a new orphan transaction.
Definition: txorphanage.cpp:20
std::vector< std::pair< CTransactionRef, NodeId > > GetChildrenFromDifferentPeer(const CTransactionRef &parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Get all children that spend from this tx but were not received from nodeid.
void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Erase all orphans announced by a peer (eg, after that peer disconnects)
Definition: txorphanage.cpp:97
std::vector< CTransactionRef > GetChildrenFromSamePeer(const CTransactionRef &parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Get all children that spend from this tx and were received from nodeid.
size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition: uint256.h:106
BOOST_AUTO_TEST_SUITE_END()
@ SIGHASH_ALL
Definition: interpreter.h:30
int64_t NodeId
Definition: net.h:97
#define BOOST_CHECK(expr)
Definition: object.cpp:17
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)
Definition: transaction.h:424
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
@ OP_1
Definition: script.h:82
static constexpr CAmount CENT
Definition: setup_common.h:47
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:694
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:83
#define LOCK(cs)
Definition: sync.h:257
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:50
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
assert(!tx.IsCoinBase())