|
10 | 10 | #include "txmempool.h"
|
11 | 11 | #include "random.h"
|
12 | 12 | #include "script/standard.h"
|
| 13 | +#include "script/sign.h" |
13 | 14 | #include "test/test_bitcoin.h"
|
14 | 15 | #include "utiltime.h"
|
| 16 | +#include "core_io.h" |
| 17 | +#include "keystore.h" |
| 18 | +#include "policy/policy.h" |
15 | 19 |
|
16 | 20 | #include <boost/test/unit_test.hpp>
|
17 | 21 |
|
| 22 | +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks); |
| 23 | + |
18 | 24 | BOOST_AUTO_TEST_SUITE(tx_validationcache_tests)
|
19 | 25 |
|
20 | 26 | static bool
|
@@ -84,4 +90,282 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
|
84 | 90 | BOOST_CHECK_EQUAL(mempool.size(), 0);
|
85 | 91 | }
|
86 | 92 |
|
| 93 | +// Run CheckInputs (using pcoinsTip) on the given transaction, for all script |
| 94 | +// flags. Test that CheckInputs passes for all flags that don't overlap with |
| 95 | +// the failing_flags argument, but otherwise fails. |
| 96 | +// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may |
| 97 | +// get reassigned) have an interaction with DISCOURAGE_UPGRADABLE_NOPS: if |
| 98 | +// the script flags used contain DISCOURAGE_UPGRADABLE_NOPS but don't contain |
| 99 | +// CHECKLOCKTIMEVERIFY (or CHECKSEQUENCEVERIFY), but the script does contain |
| 100 | +// OP_CHECKLOCKTIMEVERIFY (or OP_CHECKSEQUENCEVERIFY), then script execution |
| 101 | +// should fail. |
| 102 | +// Capture this interaction with the upgraded_nop argument: set it when evaluating |
| 103 | +// any script flag that is implemented as an upgraded NOP code. |
| 104 | +void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_flags, bool add_to_cache, bool upgraded_nop) |
| 105 | +{ |
| 106 | + PrecomputedTransactionData txdata(tx); |
| 107 | + // If we add many more flags, this loop can get too expensive, but we can |
| 108 | + // rewrite in the future to randomly pick a set of flags to evaluate. |
| 109 | + for (uint32_t test_flags=0; test_flags < (1U << 16); test_flags += 1) { |
| 110 | + CValidationState state; |
| 111 | + // Filter out incompatible flag choices |
| 112 | + if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) { |
| 113 | + // CLEANSTACK requires P2SH and WITNESS, see VerifyScript() in |
| 114 | + // script/interpreter.cpp |
| 115 | + test_flags |= SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS; |
| 116 | + } |
| 117 | + if ((test_flags & SCRIPT_VERIFY_WITNESS)) { |
| 118 | + // WITNESS requires P2SH |
| 119 | + test_flags |= SCRIPT_VERIFY_P2SH; |
| 120 | + } |
| 121 | + bool ret = CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, nullptr); |
| 122 | + // CheckInputs should succeed iff test_flags doesn't intersect with |
| 123 | + // failing_flags |
| 124 | + bool expected_return_value = !(test_flags & failing_flags); |
| 125 | + if (expected_return_value && upgraded_nop) { |
| 126 | + // If the script flag being tested corresponds to an upgraded NOP, |
| 127 | + // then script execution should fail if DISCOURAGE_UPGRADABLE_NOPS |
| 128 | + // is set. |
| 129 | + expected_return_value = !(test_flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS); |
| 130 | + } |
| 131 | + BOOST_CHECK_EQUAL(ret, expected_return_value); |
| 132 | + |
| 133 | + // Test the caching |
| 134 | + if (ret && add_to_cache) { |
| 135 | + // Check that we get a cache hit if the tx was valid |
| 136 | + std::vector<CScriptCheck> scriptchecks; |
| 137 | + BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks)); |
| 138 | + BOOST_CHECK(scriptchecks.empty()); |
| 139 | + } else { |
| 140 | + // Check that we get script executions to check, if the transaction |
| 141 | + // was invalid, or we didn't add to cache. |
| 142 | + std::vector<CScriptCheck> scriptchecks; |
| 143 | + BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, test_flags, true, add_to_cache, txdata, &scriptchecks)); |
| 144 | + BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size()); |
| 145 | + } |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | +BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) |
| 150 | +{ |
| 151 | + // Test that passing CheckInputs with one set of script flags doesn't imply |
| 152 | + // that we would pass again with a different set of flags. |
| 153 | + InitScriptExecutionCache(); |
| 154 | + |
| 155 | + CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; |
| 156 | + CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey)); |
| 157 | + CScript p2pkh_scriptPubKey = GetScriptForDestination(coinbaseKey.GetPubKey().GetID()); |
| 158 | + CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey); |
| 159 | + |
| 160 | + CBasicKeyStore keystore; |
| 161 | + keystore.AddKey(coinbaseKey); |
| 162 | + keystore.AddCScript(p2pk_scriptPubKey); |
| 163 | + |
| 164 | + // flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed pubkey thing |
| 165 | + |
| 166 | + // Create 2 outputs that match the three scripts above, spending the first |
| 167 | + // coinbase tx. |
| 168 | + CMutableTransaction spend_tx; |
| 169 | + |
| 170 | + spend_tx.nVersion = 1; |
| 171 | + spend_tx.vin.resize(1); |
| 172 | + spend_tx.vin[0].prevout.hash = coinbaseTxns[0].GetHash(); |
| 173 | + spend_tx.vin[0].prevout.n = 0; |
| 174 | + spend_tx.vout.resize(4); |
| 175 | + spend_tx.vout[0].nValue = 11*CENT; |
| 176 | + spend_tx.vout[0].scriptPubKey = p2sh_scriptPubKey; |
| 177 | + spend_tx.vout[1].nValue = 11*CENT; |
| 178 | + spend_tx.vout[1].scriptPubKey = p2wpkh_scriptPubKey; |
| 179 | + spend_tx.vout[2].nValue = 11*CENT; |
| 180 | + spend_tx.vout[2].scriptPubKey = CScript() << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; |
| 181 | + spend_tx.vout[3].nValue = 11*CENT; |
| 182 | + spend_tx.vout[3].scriptPubKey = CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; |
| 183 | + |
| 184 | + // Sign, with a non-DER signature |
| 185 | + { |
| 186 | + std::vector<unsigned char> vchSig; |
| 187 | + uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE); |
| 188 | + BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); |
| 189 | + vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER |
| 190 | + vchSig.push_back((unsigned char)SIGHASH_ALL); |
| 191 | + spend_tx.vin[0].scriptSig << vchSig; |
| 192 | + } |
| 193 | + |
| 194 | + LOCK(cs_main); |
| 195 | + |
| 196 | + // Test that invalidity under a set of flags doesn't preclude validity |
| 197 | + // under other (eg consensus) flags. |
| 198 | + // spend_tx is invalid according to DERSIG |
| 199 | + CValidationState state; |
| 200 | + { |
| 201 | + PrecomputedTransactionData ptd_spend_tx(spend_tx); |
| 202 | + |
| 203 | + BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); |
| 204 | + |
| 205 | + // If we call again asking for scriptchecks (as happens in |
| 206 | + // ConnectBlock), we should add a script check object for this -- we're |
| 207 | + // not caching invalidity (if that changes, delete this test case). |
| 208 | + std::vector<CScriptCheck> scriptchecks; |
| 209 | + BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); |
| 210 | + BOOST_CHECK_EQUAL(scriptchecks.size(), 1); |
| 211 | + |
| 212 | + // Test that CheckInputs returns true iff DERSIG-enforcing flags are |
| 213 | + // not present. Don't add these checks to the cache, so that we can |
| 214 | + // test later that block validation works fine in the absence of cached |
| 215 | + // successes. |
| 216 | + ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false, false); |
| 217 | + |
| 218 | + // And if we produce a block with this tx, it should be valid (DERSIG not |
| 219 | + // enabled yet), even though there's no cache entry. |
| 220 | + CBlock block; |
| 221 | + |
| 222 | + block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey); |
| 223 | + BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); |
| 224 | + BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash()); |
| 225 | + } |
| 226 | + |
| 227 | + // Test P2SH: construct a transaction that is valid without P2SH, and |
| 228 | + // then test validity with P2SH. |
| 229 | + { |
| 230 | + CMutableTransaction invalid_under_p2sh_tx; |
| 231 | + invalid_under_p2sh_tx.nVersion = 1; |
| 232 | + invalid_under_p2sh_tx.vin.resize(1); |
| 233 | + invalid_under_p2sh_tx.vin[0].prevout.hash = spend_tx.GetHash(); |
| 234 | + invalid_under_p2sh_tx.vin[0].prevout.n = 0; |
| 235 | + invalid_under_p2sh_tx.vout.resize(1); |
| 236 | + invalid_under_p2sh_tx.vout[0].nValue = 11*CENT; |
| 237 | + invalid_under_p2sh_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; |
| 238 | + std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end()); |
| 239 | + invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2; |
| 240 | + |
| 241 | + ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx, SCRIPT_VERIFY_P2SH, true, false); |
| 242 | + } |
| 243 | + |
| 244 | + // Test CHECKLOCKTIMEVERIFY |
| 245 | + { |
| 246 | + CMutableTransaction invalid_with_cltv_tx; |
| 247 | + invalid_with_cltv_tx.nVersion = 1; |
| 248 | + invalid_with_cltv_tx.nLockTime = 100; |
| 249 | + invalid_with_cltv_tx.vin.resize(1); |
| 250 | + invalid_with_cltv_tx.vin[0].prevout.hash = spend_tx.GetHash(); |
| 251 | + invalid_with_cltv_tx.vin[0].prevout.n = 2; |
| 252 | + invalid_with_cltv_tx.vin[0].nSequence = 0; |
| 253 | + invalid_with_cltv_tx.vout.resize(1); |
| 254 | + invalid_with_cltv_tx.vout[0].nValue = 11*CENT; |
| 255 | + invalid_with_cltv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; |
| 256 | + |
| 257 | + // Sign |
| 258 | + std::vector<unsigned char> vchSig; |
| 259 | + uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE); |
| 260 | + BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); |
| 261 | + vchSig.push_back((unsigned char)SIGHASH_ALL); |
| 262 | + invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; |
| 263 | + |
| 264 | + ValidateCheckInputsForAllFlags(invalid_with_cltv_tx, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true); |
| 265 | + |
| 266 | + // Make it valid, and check again |
| 267 | + invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; |
| 268 | + CValidationState state; |
| 269 | + PrecomputedTransactionData txdata(invalid_with_cltv_tx); |
| 270 | + BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip, true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); |
| 271 | + } |
| 272 | + |
| 273 | + // TEST CHECKSEQUENCEVERIFY |
| 274 | + { |
| 275 | + CMutableTransaction invalid_with_csv_tx; |
| 276 | + invalid_with_csv_tx.nVersion = 2; |
| 277 | + invalid_with_csv_tx.vin.resize(1); |
| 278 | + invalid_with_csv_tx.vin[0].prevout.hash = spend_tx.GetHash(); |
| 279 | + invalid_with_csv_tx.vin[0].prevout.n = 3; |
| 280 | + invalid_with_csv_tx.vin[0].nSequence = 100; |
| 281 | + invalid_with_csv_tx.vout.resize(1); |
| 282 | + invalid_with_csv_tx.vout[0].nValue = 11*CENT; |
| 283 | + invalid_with_csv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; |
| 284 | + |
| 285 | + // Sign |
| 286 | + std::vector<unsigned char> vchSig; |
| 287 | + uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SIGVERSION_BASE); |
| 288 | + BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); |
| 289 | + vchSig.push_back((unsigned char)SIGHASH_ALL); |
| 290 | + invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101; |
| 291 | + |
| 292 | + ValidateCheckInputsForAllFlags(invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true); |
| 293 | + |
| 294 | + // Make it valid, and check again |
| 295 | + invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; |
| 296 | + CValidationState state; |
| 297 | + PrecomputedTransactionData txdata(invalid_with_csv_tx); |
| 298 | + BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip, true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); |
| 299 | + } |
| 300 | + |
| 301 | + // TODO: add tests for remaining script flags |
| 302 | + |
| 303 | + // Test that passing CheckInputs with a valid witness doesn't imply success |
| 304 | + // for the same tx with a different witness. |
| 305 | + { |
| 306 | + CMutableTransaction valid_with_witness_tx; |
| 307 | + valid_with_witness_tx.nVersion = 1; |
| 308 | + valid_with_witness_tx.vin.resize(1); |
| 309 | + valid_with_witness_tx.vin[0].prevout.hash = spend_tx.GetHash(); |
| 310 | + valid_with_witness_tx.vin[0].prevout.n = 1; |
| 311 | + valid_with_witness_tx.vout.resize(1); |
| 312 | + valid_with_witness_tx.vout[0].nValue = 11*CENT; |
| 313 | + valid_with_witness_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; |
| 314 | + |
| 315 | + // Sign |
| 316 | + SignatureData sigdata; |
| 317 | + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata); |
| 318 | + UpdateTransaction(valid_with_witness_tx, 0, sigdata); |
| 319 | + |
| 320 | + // This should be valid under all script flags. |
| 321 | + ValidateCheckInputsForAllFlags(valid_with_witness_tx, 0, true, false); |
| 322 | + |
| 323 | + // Remove the witness, and check that it is now invalid. |
| 324 | + valid_with_witness_tx.vin[0].scriptWitness.SetNull(); |
| 325 | + ValidateCheckInputsForAllFlags(valid_with_witness_tx, SCRIPT_VERIFY_WITNESS, true, false); |
| 326 | + } |
| 327 | + |
| 328 | + { |
| 329 | + // Test a transaction with multiple inputs. |
| 330 | + CMutableTransaction tx; |
| 331 | + |
| 332 | + tx.nVersion = 1; |
| 333 | + tx.vin.resize(2); |
| 334 | + tx.vin[0].prevout.hash = spend_tx.GetHash(); |
| 335 | + tx.vin[0].prevout.n = 0; |
| 336 | + tx.vin[1].prevout.hash = spend_tx.GetHash(); |
| 337 | + tx.vin[1].prevout.n = 1; |
| 338 | + tx.vout.resize(1); |
| 339 | + tx.vout[0].nValue = 22*CENT; |
| 340 | + tx.vout[0].scriptPubKey = p2pk_scriptPubKey; |
| 341 | + |
| 342 | + // Sign |
| 343 | + for (int i=0; i<2; ++i) { |
| 344 | + SignatureData sigdata; |
| 345 | + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata); |
| 346 | + UpdateTransaction(tx, i, sigdata); |
| 347 | + } |
| 348 | + |
| 349 | + // This should be valid under all script flags |
| 350 | + ValidateCheckInputsForAllFlags(tx, 0, true, false); |
| 351 | + |
| 352 | + // Check that if the second input is invalid, but the first input is |
| 353 | + // valid, the transaction is not cached. |
| 354 | + // Invalidate vin[1] |
| 355 | + tx.vin[1].scriptWitness.SetNull(); |
| 356 | + |
| 357 | + CValidationState state; |
| 358 | + PrecomputedTransactionData txdata(tx); |
| 359 | + // This transaction is now invalid under segwit, because of the second input. |
| 360 | + BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr)); |
| 361 | + |
| 362 | + std::vector<CScriptCheck> scriptchecks; |
| 363 | + // Make sure this transaction was not cached (ie because the first |
| 364 | + // input was valid) |
| 365 | + BOOST_CHECK(CheckInputs(tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks)); |
| 366 | + // Should get 2 script checks back -- caching is on a whole-transaction basis. |
| 367 | + BOOST_CHECK_EQUAL(scriptchecks.size(), 2); |
| 368 | + } |
| 369 | +} |
| 370 | + |
87 | 371 | BOOST_AUTO_TEST_SUITE_END()
|
0 commit comments