-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathtxvalidation_tests.cpp
More file actions
228 lines (195 loc) · 10.8 KB
/
txvalidation_tests.cpp
File metadata and controls
228 lines (195 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// Copyright (c) 2017-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/validation.h>
#include <key_io.h>
#include <policy/packages.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <script/standard.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
extern bool fRequireStandard;
BOOST_AUTO_TEST_SUITE(txvalidation_tests)
/**
* Ensure that the mempool won't accept coinbase transactions.
*/
BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
{
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
CMutableTransaction coinbaseTx;
coinbaseTx.nVersion = 1;
coinbaseTx.vin.resize(1);
coinbaseTx.vout.resize(1);
coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL;
coinbaseTx.vout[0].nValue = 1 * CENT;
coinbaseTx.vout[0].scriptPubKey = scriptPubKey;
BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(), *m_node.mempool, MakeTransactionRef(coinbaseTx),
true /* bypass_limits */);
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
// Check that the transaction hasn't been added to mempool.
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
// Check that the validation state reflects the unsuccessful attempt.
BOOST_CHECK(result.m_state.IsInvalid());
BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
}
/**
* Ensure that the mempool rejects version 2+ transactions with nTime=0.
*/
BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_zero_time, TestChain100Setup)
{
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
CMutableTransaction tx;
tx.nVersion = 2; // PoS transaction
tx.nTime = 0; // Invalid timestamp
tx.vin.resize(1);
tx.vout.resize(1);
tx.vin[0].prevout.hash = InsecureRand256();
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(65, 0);
tx.vout[0].nValue = 1 * CENT;
tx.vout[0].scriptPubKey = scriptPubKey;
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(),
*m_node.mempool,
MakeTransactionRef(tx),
false /* bypass_limits */);
BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
BOOST_CHECK(result.m_state.IsInvalid());
BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "bad-txns-time-zero");
BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
}
/**
* Ensure that the mempool accepts version 1 transactions with nTime=0 (PoW era).
*/
BOOST_FIXTURE_TEST_CASE(tx_mempool_accept_v1_zero_time, TestChain100Setup)
{
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
CMutableTransaction tx;
tx.nVersion = POW_TX_VERSION; // Version 1 - PoW transaction
tx.nTime = 0; // Valid for version 1
tx.vin.resize(1);
tx.vout.resize(1);
tx.vin[0].prevout.hash = m_coinbase_txns[0]->GetHash();
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(65, 0);
tx.vout[0].nValue = 1 * CENT;
tx.vout[0].scriptPubKey = scriptPubKey;
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
const MempoolAcceptResult result = AcceptToMemoryPool(m_node.chainman->ActiveChainstate(),
*m_node.mempool,
MakeTransactionRef(tx),
true /* bypass_limits */);
// Note: This test may fail due to other validation issues (signatures, etc.)
// but it should NOT fail due to nTime=0 for version 1 transactions
if (result.m_result_type == MempoolAcceptResult::ResultType::INVALID) {
BOOST_CHECK_NE(result.m_state.GetRejectReason(), "bad-txns-time-zero");
}
}
// Create placeholder transactions that have no meaning.
inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outputs)
{
CMutableTransaction mtx = CMutableTransaction();
mtx.nVersion = 2;
mtx.nTime = 1; // Reddcoin: version 2+ requires non-zero nTime
mtx.vin.resize(num_inputs);
mtx.vout.resize(num_outputs);
auto random_script = CScript() << ToByteVector(InsecureRand256()) << ToByteVector(InsecureRand256());
for (size_t i{0}; i < num_inputs; ++i) {
mtx.vin[i].prevout.hash = InsecureRand256();
mtx.vin[i].prevout.n = 0;
mtx.vin[i].scriptSig = random_script;
}
for (size_t o{0}; o < num_outputs; ++o) {
mtx.vout[o].nValue = 1 * CENT;
mtx.vout[o].scriptPubKey = random_script;
}
return MakeTransactionRef(mtx);
}
BOOST_FIXTURE_TEST_CASE(package_tests, TestChain100Setup)
{
LOCK(cs_main);
unsigned int initialPoolSize = m_node.mempool->size();
// Parent and Child Package
CKey parent_key;
parent_key.MakeNewKey(true);
CScript parent_locking_script = GetScriptForDestination(PKHash(parent_key.GetPubKey()));
auto mtx_parent = CreateValidMempoolTransaction(/* input_transaction */ m_coinbase_txns[0], /* vout */ 0,
/* input_height */ 0, /* input_signing_key */ coinbaseKey,
/* output_destination */ parent_locking_script,
/* output_amount */ CAmount(49 * COIN), /* submit */ false);
CTransactionRef tx_parent = MakeTransactionRef(mtx_parent);
CKey child_key;
child_key.MakeNewKey(true);
CScript child_locking_script = GetScriptForDestination(PKHash(child_key.GetPubKey()));
auto mtx_child = CreateValidMempoolTransaction(/* input_transaction */ tx_parent, /* vout */ 0,
/* input_height */ 101, /* input_signing_key */ parent_key,
/* output_destination */ child_locking_script,
/* output_amount */ CAmount(48 * COIN), /* submit */ false);
CTransactionRef tx_child = MakeTransactionRef(mtx_child);
const auto result_parent_child = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {tx_parent, tx_child}, /* test_accept */ true);
BOOST_CHECK_MESSAGE(result_parent_child.m_state.IsValid(),
"Package validation unexpectedly failed: " << result_parent_child.m_state.GetRejectReason());
auto it_parent = result_parent_child.m_tx_results.find(tx_parent->GetWitnessHash());
auto it_child = result_parent_child.m_tx_results.find(tx_child->GetWitnessHash());
BOOST_CHECK(it_parent != result_parent_child.m_tx_results.end());
BOOST_CHECK_MESSAGE(it_parent->second.m_state.IsValid(),
"Package validation unexpectedly failed: " << it_parent->second.m_state.GetRejectReason());
BOOST_CHECK(it_child != result_parent_child.m_tx_results.end());
BOOST_CHECK_MESSAGE(it_child->second.m_state.IsValid(),
"Package validation unexpectedly failed: " << it_child->second.m_state.GetRejectReason());
// Packages can't have more than 25 transactions.
Package package_too_many;
package_too_many.reserve(MAX_PACKAGE_COUNT + 1);
for (size_t i{0}; i < MAX_PACKAGE_COUNT + 1; ++i) {
package_too_many.emplace_back(create_placeholder_tx(1, 1));
}
auto result_too_many = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_too_many, /* test_accept */ true);
BOOST_CHECK(result_too_many.m_state.IsInvalid());
BOOST_CHECK_EQUAL(result_too_many.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
BOOST_CHECK_EQUAL(result_too_many.m_state.GetRejectReason(), "package-too-many-transactions");
// Packages can't have a total size of more than 101KvB.
CTransactionRef large_ptx = create_placeholder_tx(150, 150);
Package package_too_large;
auto size_large = GetVirtualTransactionSize(*large_ptx);
size_t total_size{0};
while (total_size <= MAX_PACKAGE_SIZE * 1000) {
package_too_large.push_back(large_ptx);
total_size += size_large;
}
BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT);
auto result_too_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, package_too_large, /* test_accept */ true);
BOOST_CHECK(result_too_large.m_state.IsInvalid());
BOOST_CHECK_EQUAL(result_too_large.m_state.GetResult(), PackageValidationResult::PCKG_POLICY);
BOOST_CHECK_EQUAL(result_too_large.m_state.GetRejectReason(), "package-too-large");
// A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.
// Reddcoin: Need more inputs/outputs than Bitcoin due to nTime field overhead
// With 2200 inputs/outputs, tx size ~400420 vbytes > MAX_STANDARD_TX_WEIGHT (400000)
// Enable standard checks for this test (regtest has fRequireStandard=false by default)
bool prev_require_standard = fRequireStandard;
fRequireStandard = true;
CTransactionRef giant_ptx = create_placeholder_tx(2200, 2200);
BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > MAX_STANDARD_TX_WEIGHT);
BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > MAX_PACKAGE_SIZE * 1000);
auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /* test_accept */ true);
BOOST_CHECK(result_single_large.m_state.IsInvalid());
BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX);
BOOST_CHECK_EQUAL(result_single_large.m_state.GetRejectReason(), "transaction failed");
auto it_giant_tx = result_single_large.m_tx_results.find(giant_ptx->GetWitnessHash());
BOOST_CHECK(it_giant_tx != result_single_large.m_tx_results.end());
BOOST_CHECK_EQUAL(it_giant_tx->second.m_state.GetRejectReason(), "tx-size");
// Restore previous fRequireStandard setting
fRequireStandard = prev_require_standard;
// Check that mempool size hasn't changed.
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
}
BOOST_AUTO_TEST_SUITE_END()