Branch data Line data Source code
1 : : // Copyright (c) 2011-present 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 <consensus/validation.h>
6 : : #include <key.h>
7 : : #include <random.h>
8 : : #include <script/sigcache.h>
9 : : #include <script/sign.h>
10 : : #include <script/signingprovider.h>
11 : : #include <test/util/setup_common.h>
12 : : #include <txmempool.h>
13 : : #include <util/chaintype.h>
14 : : #include <validation.h>
15 : :
16 : : #include <boost/test/unit_test.hpp>
17 : :
18 : 4 : struct Dersig100Setup : public TestChain100Setup {
19 : 2 : Dersig100Setup()
20 [ + - ]: 4 : : TestChain100Setup{ChainType::REGTEST, {.extra_args = {"-testactivationheight=dersig@102"}}} {}
21 : : };
22 : :
23 : : BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
24 : :
25 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, Dersig100Setup)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
26 : : {
27 : : // Make sure skipping validation of transactions that were
28 : : // validated going into the memory pool does not allow
29 : : // double-spends in blocks to pass validation when they should not.
30 : :
31 [ + - + - : 2 : CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ - ]
32 : :
33 : 4 : const auto ToMemPool = [this](const CMutableTransaction& tx) {
34 : 3 : LOCK(cs_main);
35 : :
36 [ + - + - ]: 6 : const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(tx));
37 : 6 : return result.m_result_type == MempoolAcceptResult::ResultType::VALID;
38 [ + - ]: 7 : };
39 : :
40 : : // Create a double-spend of mature coinbase txn:
41 : 1 : std::vector<CMutableTransaction> spends;
42 [ + - ]: 1 : spends.resize(2);
43 [ + + ]: 3 : for (int i = 0; i < 2; i++)
44 : : {
45 [ + - ]: 2 : spends[i].version = 1;
46 [ + - - + : 4 : spends[i].vin = {CTxIn{m_coinbase_txns[0]->GetHash(), 0}};
+ + - - ]
47 [ - + + + : 6 : spends[i].vout = {CTxOut{11*CENT, scriptPubKey}};
- - ]
48 : :
49 : : // Sign:
50 : 2 : std::vector<unsigned char> vchSig;
51 [ + - ]: 2 : uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, SigVersion::BASE);
52 [ + - + - : 4 : BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
+ - + - ]
53 [ + - ]: 2 : vchSig.push_back((unsigned char)SIGHASH_ALL);
54 [ - + ]: 2 : spends[i].vin[0].scriptSig << vchSig;
55 : 2 : }
56 : :
57 : 1 : CBlock block;
58 : :
59 : : // Test 1: block with both of those transactions should be rejected.
60 [ + - ]: 1 : block = CreateAndProcessBlock(spends, scriptPubKey);
61 : 1 : {
62 [ + - ]: 1 : LOCK(cs_main);
63 [ + - + - : 4 : BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
+ - - + +
- + - +
- ]
64 : 0 : }
65 : :
66 : : // Test 2: ... and should be rejected if spend1 is in the memory pool
67 [ + - + - : 2 : BOOST_CHECK(ToMemPool(spends[0]));
+ - + - ]
68 [ + - ]: 1 : block = CreateAndProcessBlock(spends, scriptPubKey);
69 : 1 : {
70 [ + - ]: 1 : LOCK(cs_main);
71 [ + - + - : 4 : BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
+ - - + +
- + - +
- ]
72 : 0 : }
73 [ + - + - : 1 : BOOST_CHECK_EQUAL(m_node.mempool->size(), 1U);
+ - ]
74 [ + - + - : 4 : WITH_LOCK(m_node.mempool->cs, m_node.mempool->removeRecursive(CTransaction{spends[0]}, MemPoolRemovalReason::CONFLICT));
+ - ]
75 [ + - + - : 1 : BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U);
+ - ]
76 : :
77 : : // Test 3: ... and should be rejected if spend2 is in the memory pool
78 [ + - + - : 2 : BOOST_CHECK(ToMemPool(spends[1]));
+ - + - ]
79 [ + - ]: 1 : block = CreateAndProcessBlock(spends, scriptPubKey);
80 : 1 : {
81 [ + - ]: 1 : LOCK(cs_main);
82 [ + - + - : 4 : BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() != block.GetHash());
+ - - + +
- + - +
- ]
83 : 0 : }
84 [ + - + - : 1 : BOOST_CHECK_EQUAL(m_node.mempool->size(), 1U);
+ - ]
85 [ + - + - : 4 : WITH_LOCK(m_node.mempool->cs, m_node.mempool->removeRecursive(CTransaction{spends[1]}, MemPoolRemovalReason::CONFLICT));
+ - ]
86 [ + - + - : 1 : BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U);
+ - ]
87 : :
88 : : // Final sanity test: first spend in *m_node.mempool, second in block, that's OK:
89 : 1 : std::vector<CMutableTransaction> oneSpend;
90 [ + - ]: 1 : oneSpend.push_back(spends[0]);
91 [ + - + - : 2 : BOOST_CHECK(ToMemPool(spends[1]));
+ - + - ]
92 [ + - ]: 1 : block = CreateAndProcessBlock(oneSpend, scriptPubKey);
93 : 1 : {
94 [ + - ]: 1 : LOCK(cs_main);
95 [ + - + - : 3 : BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
+ - - + -
+ + - +
- ]
96 : 0 : }
97 : : // spends[1] should have been removed from the mempool when the
98 : : // block with spends[0] is accepted:
99 [ + - + - : 1 : BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U);
+ - ]
100 [ + - + - ]: 5 : }
101 : :
102 : : // Run CheckInputScripts (using CoinsTip()) on the given transaction, for all script
103 : : // flags. Test that CheckInputScripts passes for all flags that don't overlap with
104 : : // the failing_flags argument, but otherwise fails.
105 : : // CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may
106 : : // get reassigned) have an interaction with DISCOURAGE_UPGRADABLE_NOPS: if
107 : : // the script flags used contain DISCOURAGE_UPGRADABLE_NOPS but don't contain
108 : : // CHECKLOCKTIMEVERIFY (or CHECKSEQUENCEVERIFY), but the script does contain
109 : : // OP_CHECKLOCKTIMEVERIFY (or OP_CHECKSEQUENCEVERIFY), then script execution
110 : : // should fail.
111 : 9 : static void ValidateCheckInputsForAllFlags(const CTransaction &tx, script_verify_flags failing_flags, bool add_to_cache, CCoinsViewCache& active_coins_tip, ValidationCache& validation_cache) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
112 : : {
113 : 9 : PrecomputedTransactionData txdata;
114 : :
115 : 9 : FastRandomContext insecure_rand(true);
116 : :
117 [ + + ]: 90009 : for (int count = 0; count < 10000; ++count) {
118 : 90000 : TxValidationState state;
119 : :
120 : : // Randomly selects flag combinations
121 [ + + ]: 90000 : script_verify_flags test_flags = script_verify_flags::from_int(insecure_rand.randrange(MAX_SCRIPT_VERIFY_FLAGS));
122 : :
123 : : // Filter out incompatible flag choices
124 [ + + ]: 90000 : if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) {
125 : : // CLEANSTACK requires P2SH and WITNESS, see VerifyScript() in
126 : : // script/interpreter.cpp
127 : 45387 : test_flags |= SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS;
128 : : }
129 [ + + ]: 90000 : if ((test_flags & SCRIPT_VERIFY_TAPROOT)) {
130 : : // TAPROOT requires WITNESS
131 : 45099 : test_flags |= SCRIPT_VERIFY_WITNESS;
132 : : }
133 [ + + ]: 90000 : if ((test_flags & SCRIPT_VERIFY_WITNESS)) {
134 : : // WITNESS requires P2SH
135 : 78759 : test_flags |= SCRIPT_VERIFY_P2SH;
136 : : }
137 [ + - + - ]: 90000 : bool ret = CheckInputScripts(tx, state, &active_coins_tip, test_flags, CheckInputScriptsFor::Mempool, true, add_to_cache, txdata, validation_cache, nullptr);
138 : : // CheckInputScripts should succeed iff test_flags doesn't intersect with
139 : : // failing_flags
140 [ + - ]: 90000 : bool expected_return_value = !(test_flags & failing_flags);
141 [ + - + - ]: 90000 : BOOST_CHECK_EQUAL(ret, expected_return_value);
142 : :
143 : : // Test the caching
144 [ + + + + ]: 90000 : if (ret && add_to_cache) {
145 : : // Check that we get a cache hit if the tx was valid
146 : 46838 : std::vector<CScriptCheck> scriptchecks;
147 [ + - + - : 93676 : BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, CheckInputScriptsFor::Mempool, true, add_to_cache, txdata, validation_cache, &scriptchecks));
+ - + - +
- ]
148 [ + - + - ]: 93676 : BOOST_CHECK(scriptchecks.empty());
149 : 46838 : } else {
150 : : // Check that we get script executions to check, if the transaction
151 : : // was invalid, or we didn't add to cache.
152 : 43162 : std::vector<CScriptCheck> scriptchecks;
153 [ + - + - : 86324 : BOOST_CHECK(CheckInputScripts(tx, state, &active_coins_tip, test_flags, CheckInputScriptsFor::Mempool, true, add_to_cache, txdata, validation_cache, &scriptchecks));
+ - + - +
- ]
154 [ + - - + : 43162 : BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
- + + - ]
155 : 43162 : }
156 : 90000 : }
157 : 9 : }
158 : :
159 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(checkinputs_test, Dersig100Setup)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
160 : : {
161 : : // Test that passing CheckInputScripts with one set of script flags doesn't imply
162 : : // that we would pass again with a different set of flags.
163 [ + - + - : 2 : CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ - ]
164 [ + - + - ]: 1 : CScript p2sh_scriptPubKey = GetScriptForDestination(ScriptHash(p2pk_scriptPubKey));
165 [ + - + - : 1 : CScript p2pkh_scriptPubKey = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey()));
+ - ]
166 [ + - + - : 1 : CScript p2wpkh_scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(coinbaseKey.GetPubKey()));
+ - ]
167 [ + - + - ]: 1 : CScript p2tr_scriptPubKey = GetScriptForDestination(WitnessV1Taproot(XOnlyPubKey(coinbaseKey.GetPubKey())));
168 : :
169 : 1 : FillableSigningProvider keystore;
170 [ + - + - : 2 : BOOST_CHECK(keystore.AddKey(coinbaseKey));
+ - + - ]
171 [ + - + - : 2 : BOOST_CHECK(keystore.AddCScript(p2pk_scriptPubKey));
+ - + - ]
172 : :
173 : : // flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed pubkey thing
174 : :
175 : : // Create 2 outputs that match the three scripts above, spending the first
176 : : // coinbase tx.
177 [ + - ]: 1 : CMutableTransaction spend_tx;
178 : :
179 : 1 : spend_tx.version = 1;
180 [ + - - + : 2 : spend_tx.vin = {CTxIn{m_coinbase_txns[0]->GetHash(), 0}};
+ + - - ]
181 : 1 : spend_tx.vout = {
182 : 1 : CTxOut{11*CENT, p2sh_scriptPubKey},
183 : 2 : CTxOut{11*CENT, p2wpkh_scriptPubKey},
184 [ + - + - : 3 : CTxOut{11*CENT, CScript() << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG},
+ - + - +
- ]
185 [ + - + - : 3 : CTxOut{11*CENT, CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG},
+ - + - +
- ]
186 : 2 : CTxOut{11*CENT, p2tr_scriptPubKey},
187 [ - + + + : 6 : };
- - ]
188 : :
189 : : // Sign, with a non-DER signature
190 : 1 : {
191 : 1 : std::vector<unsigned char> vchSig;
192 [ + - ]: 1 : uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
193 [ + - + - : 2 : BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
+ - + - ]
194 [ + - ]: 1 : vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER
195 [ + - ]: 1 : vchSig.push_back((unsigned char)SIGHASH_ALL);
196 [ - + ]: 1 : spend_tx.vin[0].scriptSig << vchSig;
197 : 0 : }
198 : :
199 : : // Test that invalidity under a set of flags doesn't preclude validity
200 : : // under other (eg consensus) flags.
201 : : // spend_tx is invalid according to DERSIG
202 : 1 : {
203 [ + - ]: 1 : LOCK(cs_main);
204 : :
205 [ + - ]: 1 : TxValidationState state;
206 : 1 : PrecomputedTransactionData ptd_spend_tx;
207 : :
208 [ + - + - : 3 : BOOST_CHECK(!CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, CheckInputScriptsFor::Mempool, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, nullptr));
+ - + - +
- + - + -
+ - ]
209 : :
210 : : // If we call again asking for scriptchecks (as happens in
211 : : // ConnectBlock), we should add a script check object for this -- we're
212 : : // not caching invalidity (if that changes, delete this test case).
213 : 1 : std::vector<CScriptCheck> scriptchecks;
214 [ + - + - : 3 : BOOST_CHECK(CheckInputScripts(CTransaction(spend_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, CheckInputScriptsFor::Mempool, true, true, ptd_spend_tx, m_node.chainman->m_validation_cache, &scriptchecks));
+ - + - +
- + - + -
+ - ]
215 [ + - - + : 1 : BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
+ - ]
216 : :
217 : : // Test that CheckInputScripts returns true iff DERSIG-enforcing flags are
218 : : // not present. Don't add these checks to the cache, so that we can
219 : : // test later that block validation works fine in the absence of cached
220 : : // successes.
221 [ + - + - : 1 : ValidateCheckInputsForAllFlags(CTransaction(spend_tx), SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
+ - + - ]
222 [ + - ]: 2 : }
223 : :
224 : : // And if we produce a block with this tx, it should be valid (DERSIG not
225 : : // enabled yet), even though there's no cache entry.
226 : 1 : CBlock block;
227 : :
228 [ + - + - : 2 : block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
+ + - - ]
229 [ + - ]: 1 : LOCK(cs_main);
230 [ + - + - : 3 : BOOST_CHECK(m_node.chainman->ActiveChain().Tip()->GetBlockHash() == block.GetHash());
+ - - + -
+ + - +
- ]
231 [ + - + - : 3 : BOOST_CHECK(m_node.chainman->ActiveChainstate().CoinsTip().GetBestBlock() == block.GetHash());
+ - + - +
- + - +
- ]
232 : :
233 : : // Test P2SH: construct a transaction that is valid without P2SH, and
234 : : // then test validity with P2SH.
235 : 1 : {
236 [ + - ]: 1 : CMutableTransaction invalid_under_p2sh_tx;
237 : 1 : invalid_under_p2sh_tx.version = 1;
238 [ + - - + : 3 : invalid_under_p2sh_tx.vin = {CTxIn{spend_tx.GetHash(), 0}};
+ + - - ]
239 [ - + + + : 3 : invalid_under_p2sh_tx.vout = {CTxOut{11*CENT, p2pk_scriptPubKey}};
- - ]
240 [ - + + - ]: 1 : std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
241 [ - + ]: 1 : invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
242 : :
243 [ + - + - : 2 : ValidateCheckInputsForAllFlags(CTransaction(invalid_under_p2sh_tx), SCRIPT_VERIFY_P2SH, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
+ - + - ]
244 : 1 : }
245 : :
246 : : // Test CHECKLOCKTIMEVERIFY
247 : 1 : {
248 [ + - ]: 1 : CMutableTransaction invalid_with_cltv_tx;
249 : 1 : invalid_with_cltv_tx.version = 1;
250 : 1 : invalid_with_cltv_tx.nLockTime = 100;
251 [ + - - + : 3 : invalid_with_cltv_tx.vin = {CTxIn{spend_tx.GetHash(), 2, {}, /*nSequenceIn=*/0}};
+ + - - ]
252 [ - + + + : 3 : invalid_with_cltv_tx.vout = {CTxOut{11*CENT, p2pk_scriptPubKey}};
- - ]
253 : :
254 : : // Sign
255 : 1 : std::vector<unsigned char> vchSig;
256 [ + - ]: 1 : uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
257 [ + - + - : 2 : BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
+ - + - ]
258 [ + - ]: 1 : vchSig.push_back((unsigned char)SIGHASH_ALL);
259 [ - + + - ]: 1 : invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
260 : :
261 [ + - + - : 1 : ValidateCheckInputsForAllFlags(CTransaction(invalid_with_cltv_tx), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
+ - + - ]
262 : :
263 : : // Make it valid, and check again
264 [ - + + - ]: 1 : invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
265 [ + - ]: 1 : TxValidationState state;
266 : 1 : PrecomputedTransactionData txdata;
267 [ + - + - : 3 : BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_cltv_tx), state, m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, CheckInputScriptsFor::Mempool, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
+ - + - +
- + - ]
268 : 2 : }
269 : :
270 : : // TEST CHECKSEQUENCEVERIFY
271 : 1 : {
272 [ + - ]: 1 : CMutableTransaction invalid_with_csv_tx;
273 : 1 : invalid_with_csv_tx.version = 2;
274 [ + - - + : 3 : invalid_with_csv_tx.vin = {CTxIn{spend_tx.GetHash(), 3, {}, /*nSequenceIn=*/100}};
+ + - - ]
275 [ - + + + : 3 : invalid_with_csv_tx.vout = {CTxOut{11*CENT, p2pk_scriptPubKey}};
- - ]
276 : :
277 : : // Sign
278 : 1 : std::vector<unsigned char> vchSig;
279 [ + - ]: 1 : uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
280 [ + - + - : 2 : BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
+ - + - ]
281 [ + - ]: 1 : vchSig.push_back((unsigned char)SIGHASH_ALL);
282 [ - + + - ]: 1 : invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
283 : :
284 [ + - + - : 1 : ValidateCheckInputsForAllFlags(CTransaction(invalid_with_csv_tx), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
+ - + - ]
285 : :
286 : : // Make it valid, and check again
287 [ - + + - ]: 1 : invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
288 [ + - ]: 1 : TxValidationState state;
289 : 1 : PrecomputedTransactionData txdata;
290 [ + - + - : 3 : BOOST_CHECK(CheckInputScripts(CTransaction(invalid_with_csv_tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, CheckInputScriptsFor::Mempool, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
+ - + - +
- + - +
- ]
291 : 2 : }
292 : :
293 : : // TODO: add tests for remaining script flags
294 : :
295 : : // Test that passing CheckInputScripts with a valid witness doesn't imply success
296 : : // for the same tx with a different witness.
297 : 1 : {
298 [ + - ]: 1 : CMutableTransaction valid_with_witness_tx;
299 : 1 : valid_with_witness_tx.version = 1;
300 [ + - - + : 3 : valid_with_witness_tx.vin = {CTxIn{spend_tx.GetHash(), 1}};
+ + - - ]
301 [ - + + + : 3 : valid_with_witness_tx.vout = {CTxOut{11*CENT, p2pk_scriptPubKey}};
- - ]
302 : :
303 : : // Sign
304 : 1 : SignatureData sigdata;
305 [ + - + - : 2 : BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(valid_with_witness_tx, 0, 11 * CENT, {.sighash_type = SIGHASH_DEFAULT}), spend_tx.vout[1].scriptPubKey, sigdata));
+ - + - +
- ]
306 [ + - ]: 1 : UpdateInput(valid_with_witness_tx.vin[0], sigdata);
307 : :
308 : : // This should be valid under all script flags.
309 [ + - + - : 1 : ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
+ - + - ]
310 : :
311 : : // Remove the witness, and check that it is now invalid.
312 : 1 : valid_with_witness_tx.vin[0].scriptWitness.SetNull();
313 [ + - + - : 1 : ValidateCheckInputsForAllFlags(CTransaction(valid_with_witness_tx), SCRIPT_VERIFY_WITNESS, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
+ - + - ]
314 : 1 : }
315 : :
316 : : // Test a Taproot (witness v1) key-path spend, to exercise the Schnorr branch of the signature cache.
317 : 1 : {
318 [ + - ]: 1 : CMutableTransaction tr_tx;
319 [ + - - + : 3 : tr_tx.vin = {CTxIn{spend_tx.GetHash(), 4}};
+ + - - ]
320 [ - + + + : 3 : tr_tx.vout = {CTxOut{11*CENT, p2pk_scriptPubKey}};
- - ]
321 : :
322 : : // Sign P2TR output for key-path spending (i.e. add Schnorr signature to witness stack)
323 : 1 : FlatSigningProvider tr_keystore;
324 [ + - + - : 1 : tr_keystore.keys.emplace(coinbaseKey.GetPubKey().GetID(), coinbaseKey);
+ - ]
325 : 1 : const std::map<COutPoint, Coin> coins{
326 : 1 : {tr_tx.vin[0].prevout, Coin(spend_tx.vout[4], /*nHeightIn=*/0, /*fCoinBaseIn=*/false)}
327 [ - + + + : 2 : };
- - ]
328 [ + - ]: 1 : std::map<int, bilingual_str> input_errors;
329 [ + - + - : 2 : BOOST_REQUIRE(SignTransaction(tr_tx, &tr_keystore, coins, {.sighash_type = SIGHASH_DEFAULT}, input_errors));
+ - + - ]
330 [ + - ]: 1 : auto& witness_stack = tr_tx.vin[0].scriptWitness.stack;
331 [ + - - + : 2 : BOOST_REQUIRE(witness_stack.size() == 1 && witness_stack[0].size() == 64);
+ - - + -
+ + - +
- ]
332 : :
333 : : // Invalidate signature; an invalid Taproot key-path spend is only invalid if SCRIPT_VERIFY_TAPROOT is set
334 [ + - ]: 1 : witness_stack[0][63] ^= 0x01; // damage signature
335 [ + - + - : 1 : ValidateCheckInputsForAllFlags(CTransaction(tr_tx), SCRIPT_VERIFY_TAPROOT, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
+ - + - ]
336 [ + - ]: 1 : witness_stack[0][63] ^= 0x01; // repair signature
337 : :
338 : : // A valid Taproot key-path spend is valid under all flags
339 [ + - + - : 2 : ValidateCheckInputsForAllFlags(CTransaction(tr_tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
+ - + - ]
340 : 1 : }
341 : :
342 : 1 : {
343 : : // Test a transaction with multiple inputs.
344 [ + - ]: 1 : CMutableTransaction tx;
345 : :
346 : 1 : tx.version = 1;
347 : 1 : tx.vin = {
348 [ + - ]: 1 : CTxIn{spend_tx.GetHash(), 0},
349 [ + - ]: 2 : CTxIn{spend_tx.GetHash(), 1},
350 [ - + + + : 3 : };
- - ]
351 [ - + + + : 3 : tx.vout = {CTxOut{22*CENT, p2pk_scriptPubKey}};
- - ]
352 : :
353 : : // Sign
354 [ + + ]: 3 : for (int i = 0; i < 2; ++i) {
355 : 2 : SignatureData sigdata;
356 [ + - + - : 4 : BOOST_CHECK(ProduceSignature(keystore, MutableTransactionSignatureCreator(tx, i, 11 * CENT, {.sighash_type = SIGHASH_DEFAULT}), spend_tx.vout[i].scriptPubKey, sigdata));
+ - + - +
- ]
357 [ + - ]: 2 : UpdateInput(tx.vin[i], sigdata);
358 : 2 : }
359 : :
360 : : // This should be valid under all script flags
361 [ + - + - : 1 : ValidateCheckInputsForAllFlags(CTransaction(tx), 0, true, m_node.chainman->ActiveChainstate().CoinsTip(), m_node.chainman->m_validation_cache);
+ - + - ]
362 : :
363 : : // Check that if the second input is invalid, but the first input is
364 : : // valid, the transaction is not cached.
365 : : // Invalidate vin[1]
366 : 1 : tx.vin[1].scriptWitness.SetNull();
367 : :
368 [ + - ]: 1 : TxValidationState state;
369 : 1 : PrecomputedTransactionData txdata;
370 : : // This transaction is now invalid under segwit, because of the second input.
371 [ + - + - : 3 : BOOST_CHECK(!CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, CheckInputScriptsFor::Mempool, true, true, txdata, m_node.chainman->m_validation_cache, nullptr));
+ - + - +
- + - + -
+ - ]
372 : :
373 : 1 : std::vector<CScriptCheck> scriptchecks;
374 : : // Make sure this transaction was not cached (ie because the first
375 : : // input was valid)
376 [ + - + - : 3 : BOOST_CHECK(CheckInputScripts(CTransaction(tx), state, &m_node.chainman->ActiveChainstate().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, CheckInputScriptsFor::Mempool, true, true, txdata, m_node.chainman->m_validation_cache, &scriptchecks));
+ - + - +
- + - + -
+ - ]
377 : : // Should get 2 script checks back -- caching is on a whole-transaction basis.
378 [ + - - + : 1 : BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
+ - ]
379 [ + - ]: 3 : }
380 [ + - + - : 23 : }
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- - - -
- ]
381 : :
382 : : BOOST_AUTO_TEST_SUITE_END()
|