Line data Source code
1 : // Copyright (c) 2011-2014 The Bitcoin Core developers
2 : // Copyright (c) 2017-2021 The PIVX Core developers
3 : // Distributed under the MIT/X11 software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "test/test_pivx.h"
7 :
8 : #include "test/data/tx_invalid.json.h"
9 : #include "test/data/tx_valid.json.h"
10 :
11 : #include "consensus/tx_verify.h"
12 : #include "clientversion.h"
13 : #include "checkqueue.h"
14 : #include "core_io.h"
15 : #include "key.h"
16 : #include "keystore.h"
17 : #include "policy/policy.h"
18 : #include "script/script.h"
19 : #include "script/script_error.h"
20 : #include "script/sign.h"
21 : #include "validation.h"
22 :
23 : #include <boost/algorithm/string/classification.hpp>
24 : #include <boost/algorithm/string/split.hpp>
25 : #include <boost/test/unit_test.hpp>
26 :
27 : #include <univalue.h>
28 :
29 : // In script_tests.cpp
30 : extern UniValue read_json(const std::string& jsondata);
31 :
32 : // Helper
33 17 : bool IsStandardTx(const CTransaction& tx, int nBlockHeight, std::string& reason)
34 : {
35 34 : return IsStandardTx(MakeTransactionRef(tx), nBlockHeight, reason);
36 : }
37 :
38 : static std::map<std::string, unsigned int> mapFlagNames = {
39 : {std::string("NONE"), (unsigned int)SCRIPT_VERIFY_NONE},
40 : {std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
41 : {std::string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC},
42 : {std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
43 : {std::string("LOW_S"), (unsigned int)SCRIPT_VERIFY_LOW_S},
44 : {std::string("SIGPUSHONLY"), (unsigned int)SCRIPT_VERIFY_SIGPUSHONLY},
45 : {std::string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA},
46 : {std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
47 : {std::string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS},
48 : {std::string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK},
49 : {std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
50 : {std::string("EXCHANGEADDRVERIFY"), (unsigned int)SCRIPT_VERIFY_EXCHANGEADDR},
51 : };
52 :
53 1116 : unsigned int ParseScriptFlags(std::string strFlags)
54 : {
55 1116 : if (strFlags.empty()) {
56 : return 0;
57 : }
58 998 : unsigned int flags = 0;
59 998 : std::vector<std::string> words;
60 998 : boost::algorithm::split(words, strFlags, boost::algorithm::is_any_of(","));
61 :
62 2766 : for (std::string word : words)
63 : {
64 3536 : if (!mapFlagNames.count(word))
65 0 : BOOST_ERROR("Bad test: unknown verification flag '" << word << "'");
66 1768 : flags |= mapFlagNames[word];
67 : }
68 :
69 998 : return flags;
70 : }
71 :
72 82 : std::string FormatScriptFlags(unsigned int flags)
73 : {
74 82 : if (flags == 0) {
75 39 : return "";
76 : }
77 125 : std::string ret;
78 43 : std::map<std::string, unsigned int>::const_iterator it = mapFlagNames.begin();
79 559 : while (it != mapFlagNames.end()) {
80 516 : if (flags & it->second) {
81 92 : ret += it->first + ",";
82 : }
83 559 : it++;
84 : }
85 43 : return ret.substr(0, ret.size() - 1);
86 : }
87 :
88 : BOOST_FIXTURE_TEST_SUITE(transaction_tests, TestingSetup)
89 :
90 2 : BOOST_AUTO_TEST_CASE(tx_valid)
91 : {
92 : // Read tests from test/data/tx_valid.json
93 : // Format is an array of arrays
94 : // Inner arrays are either [ "comment" ]
95 : // or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, verifyFlags
96 : // ... where all scripts are stringified scripts.
97 : //
98 : // verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
99 3 : UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
100 :
101 1 : ScriptError err;
102 115 : for (unsigned int idx = 0; idx < tests.size(); idx++) {
103 114 : UniValue test = tests[idx];
104 228 : std::string strTest = test.write();
105 114 : if (test[0].isArray())
106 : {
107 50 : if (test.size() != 3 || !test[1].isStr() || !test[2].isStr())
108 : {
109 0 : BOOST_ERROR("Bad test: " << strTest);
110 0 : continue;
111 : }
112 :
113 100 : std::map<COutPoint, CScript> mapprevOutScriptPubKeys;
114 100 : UniValue inputs = test[0].get_array();
115 108 : bool fValid = true;
116 108 : for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
117 58 : const UniValue& input = inputs[inpIdx];
118 58 : if (!input.isArray())
119 : {
120 : fValid = false;
121 0 : break;
122 : }
123 58 : UniValue vinput = input.get_array();
124 58 : if (vinput.size() != 3)
125 : {
126 0 : fValid = false;
127 0 : break;
128 : }
129 :
130 228 : mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
131 : }
132 50 : if (!fValid)
133 : {
134 0 : BOOST_ERROR("Bad test: " << strTest);
135 0 : continue;
136 : }
137 :
138 100 : std::string transaction = test[1].get_str();
139 150 : CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION);
140 100 : CTransaction tx(deserialize, stream);
141 :
142 100 : CValidationState state;
143 100 : BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, false), strTest);
144 100 : BOOST_CHECK(state.IsValid());
145 :
146 50 : PrecomputedTransactionData precomTxData(tx);
147 108 : for (unsigned int i = 0; i < tx.vin.size(); i++)
148 : {
149 58 : if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
150 : {
151 0 : BOOST_ERROR("Bad test: " << strTest);
152 0 : break;
153 : }
154 :
155 58 : CAmount amount = 0;
156 116 : unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
157 174 : BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
158 : verify_flags, TransactionSignatureChecker(&tx, i, amount, precomTxData), tx.GetRequiredSigVersion(), &err),
159 : strTest);
160 116 : BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
161 : }
162 : }
163 : }
164 1 : }
165 :
166 2 : BOOST_AUTO_TEST_CASE(tx_invalid)
167 : {
168 : // Read tests from test/data/tx_invalid.json
169 : // Format is an array of arrays
170 : // Inner arrays are either [ "comment" ]
171 : // or [[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], serializedTransaction, verifyFlags
172 : // ... where all scripts are stringified scripts.
173 : //
174 : // verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
175 3 : UniValue tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
176 :
177 1 : ScriptError err;
178 87 : for (unsigned int idx = 0; idx < tests.size(); idx++) {
179 86 : UniValue test = tests[idx];
180 172 : std::string strTest = test.write();
181 86 : if (test[0].isArray())
182 : {
183 38 : if (test.size() != 3 || !test[1].isStr() || !test[2].isStr())
184 : {
185 0 : BOOST_ERROR("Bad test: " << strTest);
186 0 : continue;
187 : }
188 :
189 76 : std::map<COutPoint, CScript> mapprevOutScriptPubKeys;
190 76 : UniValue inputs = test[0].get_array();
191 78 : bool fValid = true;
192 78 : for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
193 40 : const UniValue& input = inputs[inpIdx];
194 40 : if (!input.isArray())
195 : {
196 : fValid = false;
197 0 : break;
198 : }
199 40 : UniValue vinput = input.get_array();
200 40 : if (vinput.size() != 3)
201 : {
202 0 : fValid = false;
203 0 : break;
204 : }
205 :
206 158 : mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
207 : }
208 38 : if (!fValid)
209 : {
210 0 : BOOST_ERROR("Bad test: " << strTest);
211 0 : continue;
212 : }
213 :
214 76 : std::string transaction = test[1].get_str();
215 114 : CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION);
216 76 : CTransaction tx(deserialize, stream);
217 :
218 76 : CValidationState state;
219 38 : fValid = CheckTransaction(tx, state, false) && state.IsValid();
220 :
221 38 : PrecomputedTransactionData precomTxData(tx);
222 52 : for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
223 : {
224 14 : if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
225 : {
226 0 : BOOST_ERROR("Bad test: " << strTest);
227 0 : break;
228 : }
229 :
230 14 : CAmount amount = 0;
231 28 : unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
232 28 : fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
233 28 : verify_flags, TransactionSignatureChecker(&tx, i, amount, precomTxData), tx.GetRequiredSigVersion(), &err);
234 : }
235 76 : BOOST_CHECK_MESSAGE(!fValid, strTest);
236 76 : BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
237 : }
238 : }
239 1 : }
240 :
241 2 : BOOST_AUTO_TEST_CASE(basic_transaction_tests)
242 : {
243 : // Random real transaction (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
244 1 : unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
245 1 : std::vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
246 2 : CDataStream stream(vch, SER_DISK, CLIENT_VERSION);
247 2 : CMutableTransaction tx;
248 1 : stream >> tx;
249 2 : CValidationState state;
250 2 : BOOST_CHECK_MESSAGE(CheckTransaction(tx, state, false) && state.IsValid(), "Simple deserialized transaction should be valid.");
251 :
252 : // Check that duplicate txins fail
253 1 : tx.vin.push_back(tx.vin[0]);
254 2 : BOOST_CHECK_MESSAGE(!CheckTransaction(tx, state, false) || !state.IsValid(), "Transaction with duplicate txins should be invalid.");
255 1 : }
256 :
257 : //
258 : // Helper: create two dummy transactions, each with
259 : // two outputs. The first has 11 and 50 CENT outputs
260 : // paid to a TX_PUBKEY, the second 21 and 22 CENT outputs
261 : // paid to a TX_PUBKEYHASH.
262 : //
263 : static std::vector<CMutableTransaction>
264 2 : SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
265 : {
266 2 : std::vector<CMutableTransaction> dummyTransactions;
267 2 : dummyTransactions.resize(2);
268 :
269 : // Add some keys to the keystore:
270 20 : CKey key[4];
271 10 : for (int i = 0; i < 4; i++)
272 : {
273 8 : key[i].MakeNewKey(i % 2);
274 8 : keystoreRet.AddKey(key[i]);
275 : }
276 :
277 : // Create some dummy input transactions
278 2 : dummyTransactions[0].vout.resize(2);
279 2 : dummyTransactions[0].vout[0].nValue = 11*CENT;
280 2 : dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
281 2 : dummyTransactions[0].vout[1].nValue = 50*CENT;
282 2 : dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
283 2 : AddCoins(coinsRet, dummyTransactions[0], 0);
284 :
285 2 : dummyTransactions[1].vout.resize(2);
286 2 : dummyTransactions[1].vout[0].nValue = 21*CENT;
287 2 : dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
288 2 : dummyTransactions[1].vout[1].nValue = 22*CENT;
289 2 : dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
290 2 : AddCoins(coinsRet, dummyTransactions[1], 0);
291 :
292 4 : return dummyTransactions;
293 : }
294 :
295 2 : BOOST_AUTO_TEST_CASE(test_Get)
296 : {
297 1 : CBasicKeyStore keystore;
298 1 : CCoinsView coinsDummy;
299 2 : CCoinsViewCache coins(&coinsDummy);
300 2 : std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
301 :
302 2 : CMutableTransaction t1;
303 1 : t1.vin.resize(3);
304 1 : t1.vin[0].prevout.hash = dummyTransactions[0].GetHash();
305 1 : t1.vin[0].prevout.n = 1;
306 1 : t1.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
307 1 : t1.vin[1].prevout.hash = dummyTransactions[1].GetHash();
308 1 : t1.vin[1].prevout.n = 0;
309 2 : t1.vin[1].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
310 1 : t1.vin[2].prevout.hash = dummyTransactions[1].GetHash();
311 1 : t1.vin[2].prevout.n = 1;
312 2 : t1.vin[2].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
313 1 : t1.vout.resize(2);
314 1 : t1.vout[0].nValue = 90*CENT;
315 1 : t1.vout[0].scriptPubKey << OP_1;
316 :
317 2 : BOOST_CHECK(AreInputsStandard(t1, coins));
318 1 : BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT);
319 1 : }
320 :
321 2 : BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
322 1 : CMutableTransaction mtx;
323 1 : mtx.nVersion = CTransaction::TxVersion::SAPLING;
324 :
325 2 : CKey key;
326 1 : key.MakeNewKey(false);
327 2 : CBasicKeyStore keystore;
328 1 : keystore.AddKeyPubKey(key, key.GetPubKey());
329 1 : CKeyID hash = key.GetPubKey().GetID();
330 2 : CScript scriptPubKey = GetScriptForDestination(hash);
331 :
332 2 : std::vector<int> sigHashes;
333 1 : sigHashes.push_back(SIGHASH_NONE | SIGHASH_ANYONECANPAY);
334 1 : sigHashes.push_back(SIGHASH_SINGLE | SIGHASH_ANYONECANPAY);
335 1 : sigHashes.push_back(SIGHASH_ALL | SIGHASH_ANYONECANPAY);
336 1 : sigHashes.push_back(SIGHASH_NONE);
337 1 : sigHashes.push_back(SIGHASH_SINGLE);
338 1 : sigHashes.push_back(SIGHASH_ALL);
339 :
340 : // create a big transaction of 4500 inputs signed by the same key
341 4501 : for(uint32_t ij = 0; ij < 4500; ij++) {
342 4500 : uint32_t i = mtx.vin.size();
343 4500 : uint256 prevId;
344 4500 : prevId.SetHex("0000000000000000000000000000000000000000000000000000000000000100");
345 4500 : COutPoint outpoint(prevId, i);
346 :
347 4500 : mtx.vin.resize(mtx.vin.size() + 1);
348 4500 : mtx.vin[i].prevout = outpoint;
349 4500 : mtx.vin[i].scriptSig = CScript();
350 :
351 4500 : mtx.vout.resize(mtx.vout.size() + 1);
352 4500 : mtx.vout[i].nValue = 1000;
353 4500 : mtx.vout[i].scriptPubKey = CScript() << OP_1;
354 : }
355 :
356 : // sign all inputs
357 4501 : for(uint32_t i = 0; i < mtx.vin.size(); i++) {
358 4500 : bool hashSigned = SignSignature(keystore, scriptPubKey, mtx, i, 1000, sigHashes.at(i % sigHashes.size()));
359 4500 : assert(hashSigned);
360 : }
361 :
362 2 : CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
363 1 : ssout << mtx;
364 2 : CTransaction tx(deserialize, ssout);
365 :
366 : // check all inputs concurrently, with the cache
367 1 : PrecomputedTransactionData precomTxData(tx);
368 2 : boost::thread_group threadGroup;
369 1 : CCheckQueue<CScriptCheck> scriptcheckqueue(128);
370 2 : CCheckQueueControl<CScriptCheck> control(&scriptcheckqueue);
371 :
372 21 : for (int i=0; i<20; i++)
373 20 : threadGroup.create_thread(std::bind(&CCheckQueue<CScriptCheck>::Thread, std::ref(scriptcheckqueue)));
374 :
375 2 : std::vector<Coin> coins;
376 4501 : for(uint32_t i = 0; i < mtx.vin.size(); i++) {
377 9000 : Coin coin;
378 4500 : coin.nHeight = 1;
379 4500 : coin.out.nValue = 1000;
380 4500 : coin.out.scriptPubKey = scriptPubKey;
381 4500 : coins.emplace_back(std::move(coin));
382 : }
383 :
384 4501 : for(uint32_t i = 0; i < mtx.vin.size(); i++) {
385 4500 : std::vector<CScriptCheck> vChecks;
386 9000 : CScriptCheck check(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH, false, &precomTxData);
387 4500 : vChecks.emplace_back();
388 4500 : check.swap(vChecks.back());
389 9000 : control.Add(vChecks);
390 : }
391 :
392 1 : bool controlCheck = control.Wait();
393 1 : assert(controlCheck);
394 :
395 1 : threadGroup.interrupt_all();
396 1 : threadGroup.join_all();
397 1 : }
398 :
399 2 : BOOST_AUTO_TEST_CASE(test_IsStandard)
400 : {
401 1 : LOCK(cs_main);
402 2 : CBasicKeyStore keystore;
403 1 : CCoinsView coinsDummy;
404 2 : CCoinsViewCache coins(&coinsDummy);
405 2 : std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
406 :
407 1 : CMutableTransaction t;
408 1 : t.vin.resize(1);
409 1 : t.vin[0].prevout.hash = dummyTransactions[0].GetHash();
410 1 : t.vin[0].prevout.n = 1;
411 1 : t.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
412 1 : t.vout.resize(1);
413 1 : t.vout[0].nValue = 90*CENT;
414 2 : CKey key;
415 1 : key.MakeNewKey(true);
416 1 : t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
417 :
418 2 : std::string reason;
419 2 : BOOST_CHECK(IsStandardTx(t, 0, reason));
420 :
421 : // Check dust with default relay fee:
422 1 : CAmount nDustThreshold = GetDustThreshold(dustRelayFee);
423 1 : BOOST_CHECK_EQUAL(nDustThreshold, 5460);
424 : // dust:
425 1 : t.vout[0].nValue = nDustThreshold - 1;
426 2 : BOOST_CHECK(!IsStandardTx(t, 0, reason));
427 : // not dust:
428 1 : t.vout[0].nValue = nDustThreshold;
429 2 : BOOST_CHECK(IsStandardTx(t, 0, reason));
430 :
431 : // Check dust with odd relay fee to verify rounding:
432 : // nDustThreshold = 182 * 3702 / 1000
433 1 : dustRelayFee = CFeeRate(3702);
434 : // dust:
435 1 : t.vout[0].nValue = 673 - 1;
436 2 : BOOST_CHECK(!IsStandardTx(t, 0, reason));
437 : // not dust:
438 1 : t.vout[0].nValue = 673;
439 2 : BOOST_CHECK(IsStandardTx(t, 0, reason));
440 1 : dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
441 :
442 1 : t.vout[0].scriptPubKey = CScript() << OP_1;
443 2 : BOOST_CHECK(!IsStandardTx(t, 0, reason));
444 :
445 : // MAX_OP_RETURN_RELAY-byte TX_NULL_DATA (standard)
446 2 : t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
447 2 : BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
448 2 : BOOST_CHECK(IsStandardTx(t, 0, reason));
449 :
450 : // MAX_OP_RETURN_RELAY+1-byte TX_NULL_DATA (non-standard)
451 2 : t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
452 2 : BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
453 2 : BOOST_CHECK(!IsStandardTx(t, 0, reason));
454 :
455 : // Data payload can be encoded in any way...
456 1 : t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("");
457 2 : BOOST_CHECK(IsStandardTx(t, 0, reason));
458 3 : t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("00") << ParseHex("01");
459 2 : BOOST_CHECK(IsStandardTx(t, 0, reason));
460 : // OP_RESERVED *is* considered to be a PUSHDATA type opcode by IsPushOnly()!
461 2 : t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RESERVED << -1 << 0 << ParseHex("01") << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12 << 13 << 14 << 15 << 16;
462 2 : BOOST_CHECK(IsStandardTx(t, 0, reason));
463 3 : t.vout[0].scriptPubKey = CScript() << OP_RETURN << 0 << ParseHex("01") << 2 << ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
464 2 : BOOST_CHECK(IsStandardTx(t, 0, reason));
465 :
466 : // ...so long as it only contains PUSHDATA's
467 1 : t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RETURN;
468 2 : BOOST_CHECK(!IsStandardTx(t, 0, reason));
469 :
470 : // TX_NULL_DATA w/o PUSHDATA
471 1 : t.vout.resize(1);
472 1 : t.vout[0].scriptPubKey = CScript() << OP_RETURN;
473 2 : BOOST_CHECK(IsStandardTx(t, 0, reason));
474 :
475 : // Only one TX_NULL_DATA permitted in all cases
476 1 : t.vout.resize(2);
477 2 : t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
478 2 : t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
479 2 : BOOST_CHECK(!IsStandardTx(t, 0, reason));
480 :
481 2 : t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
482 1 : t.vout[1].scriptPubKey = CScript() << OP_RETURN;
483 2 : BOOST_CHECK(!IsStandardTx(t, 0, reason));
484 :
485 1 : t.vout[0].scriptPubKey = CScript() << OP_RETURN;
486 1 : t.vout[1].scriptPubKey = CScript() << OP_RETURN;
487 2 : BOOST_CHECK(!IsStandardTx(t, 0, reason));
488 1 : }
489 :
490 : BOOST_AUTO_TEST_SUITE_END()
|