1 // Copyright (c) 2014-2019 Daniel Kraft
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <base58.h>
6 #include <coins.h>
7 #include <key_io.h>
8 #include <names/encoding.h>
9 #include <names/mempool.h>
10 #include <primitives/transaction.h>
11 #include <script/names.h>
12 #include <sync.h>
13 #include <txmempool.h>
14 #include <validation.h>
15 #include <validationinterface.h>
16
17 #include <test/util/setup_common.h>
18
19 #include <boost/test/unit_test.hpp>
20
21 /* No space between BOOST_FIXTURE_TEST_SUITE and '(', so that extraction of
22 the test-suite name works with grep as done in the Makefile. */
23 BOOST_AUTO_TEST_SUITE(name_mempool_tests)
24
25 namespace
26 {
27
28 class NameMempoolTestSetup : public TestingSetup
29 {
30
31 public:
32
33 CTxMemPool mempool;
34
35 CScript ADDR;
36 CScript OTHER_ADDR;
37
38 const LockPoints lp;
39
NameMempoolTestSetup()40 NameMempoolTestSetup ()
41 {
42 ADDR = CScript () << OP_TRUE;
43 OTHER_ADDR = CScript () << OP_TRUE << OP_RETURN;
44
45 ENTER_CRITICAL_SECTION (mempool.cs);
46 }
47
~NameMempoolTestSetup()48 ~NameMempoolTestSetup ()
49 {
50 LEAVE_CRITICAL_SECTION (mempool.cs);
51 }
52
53 /**
54 * Returns a valtype name based on the given string.
55 */
56 static valtype
Name(const std::string & str)57 Name (const std::string& str)
58 {
59 return DecodeName (str, NameEncoding::ASCII);
60 }
61
62 /**
63 * Returns the hash bytes for a name_new.
64 */
65 static valtype
NewHash(const std::string & nm,const char rand)66 NewHash (const std::string& nm, const char rand)
67 {
68 const valtype nameVal = Name (nm);
69 const valtype randVal(20, rand);
70
71 valtype toHash(randVal);
72 toHash.insert (toHash.end (), nameVal.begin (), nameVal.end ());
73 const uint160 hash = Hash160 (toHash);
74
75 return valtype (hash.begin (), hash.end ());
76 }
77
78 /**
79 * Builds a name new script for the given test name and rand based
80 * on a character (just to allow different rand's).
81 */
82 static CScript
NewScript(const CScript & addr,const std::string & nm,const char rand)83 NewScript (const CScript& addr, const std::string& nm, const char rand)
84 {
85 const valtype randVal(20, rand);
86
87 return CNameScript::buildNameNew (addr, Name (nm), randVal);
88 }
89
90 /**
91 * Builds a name_firstupdate script for the given name and rand value.
92 * The value we update to is just a fixed one.
93 */
94 static CScript
FirstScript(const CScript & addr,const std::string & nm,const char rand)95 FirstScript (const CScript& addr, const std::string& nm, const char rand)
96 {
97 const valtype randVal(20, rand);
98 const valtype value = DecodeName ("firstupdate value", NameEncoding::ASCII);
99
100 return CNameScript::buildNameFirstupdate (addr, Name (nm), value, randVal);
101 }
102
103 /**
104 * Builds a name_update script based on the given name and value.
105 */
106 static CScript
UpdateScript(const CScript & addr,const std::string & nm,const std::string & val)107 UpdateScript (const CScript& addr, const std::string& nm,
108 const std::string& val)
109 {
110 const valtype value = DecodeName (val, NameEncoding::ASCII);
111
112 return CNameScript::buildNameUpdate (addr, Name (nm), value);
113 }
114
115 /**
116 * Builds a transaction spending to a name-output script. The transaction
117 * is not valid, but it is "valid enough" for testing the name mempool
118 * rules with it.
119 */
120 static CTransaction
Tx(const CScript & out)121 Tx (const CScript& out)
122 {
123 CMutableTransaction mtx;
124 mtx.SetNamecoin ();
125 mtx.vout.push_back (CTxOut (COIN, out));
126
127 return CTransaction (mtx);
128 }
129
130 /**
131 * Builds a mempool entry for the given transaction.
132 */
133 CTxMemPoolEntry
Entry(const CTransaction & tx)134 Entry (const CTransaction& tx)
135 {
136 return CTxMemPoolEntry (MakeTransactionRef (tx), 0, 0, 100, false, 1, lp);
137 }
138
139 };
140
141 } // anonymous namespace
142
143 /* ************************************************************************** */
144
BOOST_FIXTURE_TEST_CASE(invalid_tx,NameMempoolTestSetup)145 BOOST_FIXTURE_TEST_CASE (invalid_tx, NameMempoolTestSetup)
146 {
147 /* Invalid transactions should not crash / assert fail the mempool check. */
148
149 CMutableTransaction mtx;
150 mtx.SetNamecoin ();
151 mempool.checkNameOps (CTransaction (mtx));
152
153 mtx.vout.push_back (CTxOut (COIN, NewScript (ADDR, "foo", 'a')));
154 mtx.vout.push_back (CTxOut (COIN, NewScript (ADDR, "bar", 'b')));
155 mtx.vout.push_back (CTxOut (COIN, FirstScript (ADDR, "foo", 'a')));
156 mtx.vout.push_back (CTxOut (COIN, FirstScript (ADDR, "bar", 'b')));
157 mtx.vout.push_back (CTxOut (COIN, UpdateScript (ADDR, "foo", "x")));
158 mtx.vout.push_back (CTxOut (COIN, UpdateScript (ADDR, "bar", "y")));
159 mempool.checkNameOps (CTransaction (mtx));
160 }
161
BOOST_FIXTURE_TEST_CASE(empty_mempool,NameMempoolTestSetup)162 BOOST_FIXTURE_TEST_CASE (empty_mempool, NameMempoolTestSetup)
163 {
164 /* While the mempool is empty (we do not add any transactions in this test),
165 all should be fine without respect to conflicts among the transactions. */
166
167 BOOST_CHECK (!mempool.registersName (Name ("foo")));
168 BOOST_CHECK (!mempool.updatesName (Name ("foo")));
169
170 BOOST_CHECK (mempool.checkNameOps (Tx (NewScript (ADDR, "foo", 'a'))));
171 BOOST_CHECK (mempool.checkNameOps (Tx (NewScript (ADDR, "foo", 'b'))));
172 BOOST_CHECK (mempool.checkNameOps (Tx (NewScript (OTHER_ADDR, "foo", 'a'))));
173
174 BOOST_CHECK (mempool.checkNameOps (Tx (FirstScript (ADDR, "foo", 'a'))));
175 BOOST_CHECK (mempool.checkNameOps (Tx (FirstScript (ADDR, "foo", 'b'))));
176
177 BOOST_CHECK (mempool.checkNameOps (Tx (UpdateScript (ADDR, "foo", "x"))));
178 BOOST_CHECK (mempool.checkNameOps (Tx (UpdateScript (ADDR, "foo", "y"))));
179 }
180
BOOST_FIXTURE_TEST_CASE(pendingChainLength_lastNameOutput,NameMempoolTestSetup)181 BOOST_FIXTURE_TEST_CASE (pendingChainLength_lastNameOutput,
182 NameMempoolTestSetup)
183 {
184 const auto txNew = Tx (NewScript (ADDR, "new", 'a'));
185 const auto txReg = Tx (FirstScript (ADDR, "reg", 'b'));
186 const auto txUpd = Tx (UpdateScript (ADDR, "upd", "x"));
187
188 mempool.addUnchecked (Entry (txNew));
189 mempool.addUnchecked (Entry (txReg));
190 mempool.addUnchecked (Entry (txUpd));
191
192 /* For testing chained name updates, we have to build a "real" chain of
193 transactions with matching inputs and outputs. */
194
195 CMutableTransaction mtx;
196 mtx.SetNamecoin ();
197 mtx.vout.push_back (CTxOut (COIN, FirstScript (ADDR, "chain", 'a')));
198 mtx.vout.push_back (CTxOut (COIN, ADDR));
199 mtx.vout.push_back (CTxOut (COIN, OTHER_ADDR));
200 const CTransaction chain1(mtx);
201 mempool.addUnchecked (Entry (chain1));
202
203 mtx.vout.clear ();
204 mtx.vout.push_back (CTxOut (COIN, ADDR));
205 mtx.vout.push_back (CTxOut (COIN, UpdateScript (ADDR, "chain", "x")));
206 mtx.vin.push_back (CTxIn (COutPoint (chain1.GetHash (), 0)));
207 const CTransaction chain2(mtx);
208 mempool.addUnchecked (Entry (chain2));
209
210 mtx.vout.clear ();
211 mtx.vout.push_back (CTxOut (COIN, OTHER_ADDR));
212 mtx.vout.push_back (CTxOut (COIN, UpdateScript (ADDR, "chain", "y")));
213 mtx.vin.push_back (CTxIn (COutPoint (chain2.GetHash (), 0)));
214 mtx.vin.push_back (CTxIn (COutPoint (chain1.GetHash (), 1)));
215 const CTransaction chain3(mtx);
216 mempool.addUnchecked (Entry (chain3));
217
218 CMutableTransaction mtxCurrency;
219 mtxCurrency.vin.push_back (CTxIn (COutPoint (chain1.GetHash (), 2)));
220 mtxCurrency.vin.push_back (CTxIn (COutPoint (chain3.GetHash (), 0)));
221 mempool.addUnchecked (Entry (CTransaction (mtxCurrency)));
222
223 BOOST_CHECK (mempool.lastNameOutput (Name ("new")).IsNull ());
224 BOOST_CHECK (mempool.lastNameOutput (Name ("reg"))
225 == COutPoint (txReg.GetHash (), 0));
226 BOOST_CHECK (mempool.lastNameOutput (Name ("upd"))
227 == COutPoint (txUpd.GetHash (), 0));
228 BOOST_CHECK (mempool.lastNameOutput (Name ("chain"))
229 == COutPoint (chain3.GetHash (), 1));
230
231 BOOST_CHECK_EQUAL (mempool.pendingNameChainLength (Name ("new")), 0);
232 BOOST_CHECK_EQUAL (mempool.pendingNameChainLength (Name ("reg")), 1);
233 BOOST_CHECK_EQUAL (mempool.pendingNameChainLength (Name ("upd")), 1);
234 BOOST_CHECK_EQUAL (mempool.pendingNameChainLength (Name ("chain")), 3);
235 }
236
BOOST_FIXTURE_TEST_CASE(name_new,NameMempoolTestSetup)237 BOOST_FIXTURE_TEST_CASE (name_new, NameMempoolTestSetup)
238 {
239 const auto tx1 = Tx (NewScript (ADDR, "foo", 'a'));
240 const auto tx1p = Tx (NewScript (OTHER_ADDR, "foo", 'a'));
241 const auto tx2 = Tx (NewScript (ADDR, "foo", 'b'));
242
243 const auto e1 = Entry (tx1);
244 const auto e2 = Entry (tx2);
245 BOOST_CHECK (e1.isNameNew () && e2.isNameNew ());
246 BOOST_CHECK (e1.getNameNewHash () == NewHash ("foo", 'a'));
247 BOOST_CHECK (e2.getNameNewHash () == NewHash ("foo", 'b'));
248
249 mempool.addUnchecked (e1);
250 mempool.addUnchecked (e2);
251 BOOST_CHECK (mempool.checkNameOps (tx1));
252 BOOST_CHECK (mempool.checkNameOps (tx2));
253 BOOST_CHECK (!mempool.checkNameOps (tx1p));
254
255 mempool.removeRecursive (tx1, MemPoolRemovalReason::EXPIRY);
256 mempool.removeRecursive (tx2, MemPoolRemovalReason::EXPIRY);
257 BOOST_CHECK (mempool.checkNameOps (tx1));
258 BOOST_CHECK (mempool.checkNameOps (tx2));
259 BOOST_CHECK (!mempool.checkNameOps (tx1p));
260 }
261
BOOST_FIXTURE_TEST_CASE(name_firstupdate,NameMempoolTestSetup)262 BOOST_FIXTURE_TEST_CASE (name_firstupdate, NameMempoolTestSetup)
263 {
264 const auto tx1 = Tx (FirstScript (ADDR, "foo", 'a'));
265 const auto tx2 = Tx (FirstScript (ADDR, "foo", 'b'));
266
267 const auto e = Entry (tx1);
268 BOOST_CHECK (e.isNameRegistration () && !e.isNameUpdate ());
269 BOOST_CHECK (e.getName () == Name ("foo"));
270
271 mempool.addUnchecked (e);
272 BOOST_CHECK (mempool.registersName (Name ("foo")));
273 BOOST_CHECK (!mempool.updatesName (Name ("foo")));
274 BOOST_CHECK (!mempool.checkNameOps (tx2));
275
276 mempool.removeRecursive (tx1, MemPoolRemovalReason::EXPIRY);
277 BOOST_CHECK (!mempool.registersName (Name ("foo")));
278 BOOST_CHECK (mempool.checkNameOps (tx1));
279 BOOST_CHECK (mempool.checkNameOps (tx2));
280 }
281
BOOST_FIXTURE_TEST_CASE(name_update,NameMempoolTestSetup)282 BOOST_FIXTURE_TEST_CASE (name_update, NameMempoolTestSetup)
283 {
284 const auto tx1 = Tx (UpdateScript (ADDR, "foo", "x"));
285 const auto tx2 = Tx (UpdateScript (ADDR, "foo", "y"));
286 const auto tx3 = Tx (UpdateScript (ADDR, "bar", "z"));
287
288 const auto e1 = Entry (tx1);
289 const auto e2 = Entry (tx2);
290 const auto e3 = Entry (tx3);
291 BOOST_CHECK (!e1.isNameRegistration () && e1.isNameUpdate ());
292 BOOST_CHECK (e1.getName () == Name ("foo"));
293
294 mempool.addUnchecked (e1);
295 mempool.addUnchecked (e2);
296 mempool.addUnchecked (e3);
297 BOOST_CHECK (!mempool.registersName (Name ("foo")));
298 BOOST_CHECK (mempool.updatesName (Name ("foo")));
299 BOOST_CHECK (mempool.updatesName (Name ("bar")));
300
301 mempool.removeRecursive (tx2, MemPoolRemovalReason::EXPIRY);
302 BOOST_CHECK (mempool.updatesName (Name ("foo")));
303 BOOST_CHECK (mempool.updatesName (Name ("bar")));
304
305 mempool.removeRecursive (tx1, MemPoolRemovalReason::EXPIRY);
306 BOOST_CHECK (!mempool.updatesName (Name ("foo")));
307 BOOST_CHECK (mempool.updatesName (Name ("bar")));
308
309 mempool.removeRecursive (tx3, MemPoolRemovalReason::EXPIRY);
310 BOOST_CHECK (!mempool.updatesName (Name ("foo")));
311 BOOST_CHECK (!mempool.updatesName (Name ("bar")));
312 }
313
BOOST_FIXTURE_TEST_CASE(mempool_sanity_check,NameMempoolTestSetup)314 BOOST_FIXTURE_TEST_CASE (mempool_sanity_check, NameMempoolTestSetup)
315 {
316 mempool.addUnchecked (Entry (Tx (NewScript (ADDR, "new", 'a'))));
317 mempool.addUnchecked (Entry (Tx (NewScript (ADDR, "new", 'b'))));
318
319 mempool.addUnchecked (Entry (Tx (FirstScript (ADDR, "reg", 'a'))));
320 mempool.addUnchecked (Entry (Tx (UpdateScript (ADDR, "reg", "n"))));
321
322 mempool.addUnchecked (Entry (Tx (UpdateScript (ADDR, "upd", "x"))));
323 mempool.addUnchecked (Entry (Tx (UpdateScript (ADDR, "upd", "y"))));
324
325 ChainstateManager& chainman = g_chainman;
326 CCoinsViewCache view(&chainman.ActiveChainstate ().CoinsTip ());
327
328 const CNameScript nameOp(UpdateScript (ADDR, "upd", "o"));
329 CNameData data;
330 data.fromScript (100, COutPoint (uint256 (), 0), nameOp);
331 view.SetName (Name ("upd"), data, false);
332
333 mempool.checkNames (chainman, &view);
334 }
335
336 namespace
337 {
338
339 /**
340 * Helper class that listens to TransactionRemovedFromMempool and records
341 * the txid's of all transactions removed.
342 */
343 class NameConflictTracker : public CValidationInterface
344 {
345
346 private:
347
348 /** The txids that have been removed according to our callback. */
349 std::vector<uint256> txids;
350
351 public:
352
NameConflictTracker()353 NameConflictTracker ()
354 {
355 RegisterValidationInterface (this);
356 }
357
~NameConflictTracker()358 ~NameConflictTracker ()
359 {
360 UnregisterValidationInterface (this);
361 }
362
363 void
TransactionRemovedFromMempool(const CTransactionRef & ptxn,const MemPoolRemovalReason reason,const uint64_t sequence)364 TransactionRemovedFromMempool (const CTransactionRef& ptxn,
365 const MemPoolRemovalReason reason,
366 const uint64_t sequence) override
367 {
368 txids.push_back (ptxn->GetHash ());
369 }
370
371 /**
372 * Expects the given list of txids to be removed. Waits for all outstanding
373 * callbacks to be processed as needed.
374 */
375 void
ExpectTxids(const std::vector<uint256> & expected) const376 ExpectTxids (const std::vector<uint256>& expected) const
377 {
378 while (GetMainSignals ().CallbacksPending () > 0)
379 UninterruptibleSleep (std::chrono::milliseconds (10));
380 BOOST_CHECK (txids == expected);
381 }
382
383 };
384
385 } // anonymous namespace
386
BOOST_FIXTURE_TEST_CASE(registration_conflicts,NameMempoolTestSetup)387 BOOST_FIXTURE_TEST_CASE (registration_conflicts, NameMempoolTestSetup)
388 {
389 const auto tx1 = Tx (FirstScript (ADDR, "foo", 'a'));
390 const auto tx2 = Tx (FirstScript (ADDR, "foo", 'b'));
391 const auto e = Entry (tx1);
392
393 mempool.addUnchecked (e);
394 BOOST_CHECK (mempool.registersName (Name ("foo")));
395 BOOST_CHECK (!mempool.checkNameOps (tx2));
396
397 NameConflictTracker tracker;
398 mempool.removeConflicts (tx2);
399 tracker.ExpectTxids ({tx1.GetHash ()});
400
401 BOOST_CHECK (!mempool.registersName (Name ("foo")));
402 BOOST_CHECK (mempool.checkNameOps (tx1));
403 BOOST_CHECK (mempool.checkNameOps (tx2));
404 BOOST_CHECK (mempool.mapTx.empty ());
405 }
406
BOOST_FIXTURE_TEST_CASE(expire_conflicts,NameMempoolTestSetup)407 BOOST_FIXTURE_TEST_CASE (expire_conflicts, NameMempoolTestSetup)
408 {
409 const auto tx1 = Tx (UpdateScript (ADDR, "foo", "x"));
410 const auto tx2 = Tx (UpdateScript (ADDR, "foo", "y"));
411 const auto e1 = Entry (tx1);
412 const auto e2 = Entry (tx2);
413
414 mempool.addUnchecked (e1);
415 mempool.addUnchecked (e2);
416 BOOST_CHECK (mempool.updatesName (Name ("foo")));
417
418 NameConflictTracker tracker;
419 mempool.removeExpireConflicts ({Name ("foo")});
420 tracker.ExpectTxids ({tx1.GetHash (), tx2.GetHash ()});
421
422 BOOST_CHECK (!mempool.updatesName (Name ("foo")));
423 BOOST_CHECK (mempool.mapTx.empty ());
424 }
425
BOOST_FIXTURE_TEST_CASE(unexpire_conflicts,NameMempoolTestSetup)426 BOOST_FIXTURE_TEST_CASE (unexpire_conflicts, NameMempoolTestSetup)
427 {
428 const auto tx = Tx (FirstScript (ADDR, "foo", 'a'));
429 const auto e = Entry (tx);
430
431 mempool.addUnchecked (e);
432 BOOST_CHECK (mempool.registersName (Name ("foo")));
433
434 NameConflictTracker tracker;
435 mempool.removeUnexpireConflicts ({Name ("foo")});
436 tracker.ExpectTxids ({tx.GetHash ()});
437
438 BOOST_CHECK (!mempool.registersName (Name ("foo")));
439 BOOST_CHECK (mempool.mapTx.empty ());
440 }
441
442 /* ************************************************************************** */
443
444 BOOST_AUTO_TEST_SUITE_END ()
445