diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -164,6 +164,44 @@ keystore.AddKey(coinbaseKey); keystore.AddCScript(p2pk_scriptPubKey); + // Create a funding transaction that can fail NULLDUMMY checks. This is for + // testing consensus vs non-standard rules in `checkinputs_test`. + CMutableTransaction mutableFunding_tx; + mutableFunding_tx.nVersion = 1; + mutableFunding_tx.vin.resize(1); + mutableFunding_tx.vin[0].prevout = COutPoint(coinbaseTxns[0].GetId(), 0); + mutableFunding_tx.vout.resize(1); + mutableFunding_tx.vout[0].nValue = 50 * COIN; + + CKey dummyKey; + dummyKey.MakeNewKey(true); + CScript nulldummyRedeemScript = + CScript() << OP_1 << ToByteVector(coinbaseKey.GetPubKey()) + << ToByteVector(dummyKey.GetPubKey()) << OP_2 + << OP_CHECKMULTISIG; + // Add our CScript to the keystore + keystore.AddCScript(nulldummyRedeemScript); + + CScript p2sh_nulldummyScriptPubKey = + GetScriptForDestination(CScriptID(nulldummyRedeemScript)); + mutableFunding_tx.vout[0].scriptPubKey = p2sh_nulldummyScriptPubKey; + std::vector nullDummyVchSig; + uint256 p2shNulldummyHash = SignatureHash( + p2pk_scriptPubKey, CTransaction(mutableFunding_tx), 0, + SigHashType().withForkId(), coinbaseTxns[0].vout[0].nValue); + BOOST_CHECK(coinbaseKey.Sign(p2shNulldummyHash, nullDummyVchSig)); + nullDummyVchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); + mutableFunding_tx.vin[0].scriptSig << nullDummyVchSig; + + // Spend the funding transaction + const CTransaction funding_tx(mutableFunding_tx); + { + LOCK(cs_main); + CBlock block = CreateAndProcessBlock({funding_tx}, p2pk_scriptPubKey); + BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); + BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash()); + } + // flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, // SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed // pubkey thing @@ -171,10 +209,9 @@ // Create 2 outputs that match the three scripts above, spending the first // coinbase tx. CMutableTransaction mutableSpend_tx; - mutableSpend_tx.nVersion = 1; mutableSpend_tx.vin.resize(1); - mutableSpend_tx.vin[0].prevout = COutPoint(coinbaseTxns[0].GetId(), 0); + mutableSpend_tx.vin[0].prevout = COutPoint(funding_tx.GetId(), 0); mutableSpend_tx.vout.resize(4); mutableSpend_tx.vout[0].nValue = 11 * CENT; mutableSpend_tx.vout[0].scriptPubKey = p2sh_scriptPubKey; @@ -189,15 +226,16 @@ mutableSpend_tx.vout[3].nValue = 11 * CENT; mutableSpend_tx.vout[3].scriptPubKey = p2sh_scriptPubKey; - // Sign, and push an extra element on the stack. + // Sign the main transaction that we spend from and make it fail NULLDUMMY + // checks. { std::vector vchSig; - uint256 hash = SignatureHash( - p2pk_scriptPubKey, CTransaction(mutableSpend_tx), 0, - SigHashType().withForkId(), coinbaseTxns[0].vout[0].nValue); - BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); - vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID)); - mutableSpend_tx.vin[0].scriptSig << OP_TRUE << vchSig; + BOOST_CHECK(SignSignature(keystore, p2sh_nulldummyScriptPubKey, + mutableSpend_tx, 0, funding_tx.vout[0].nValue, + SigHashType(SIGHASH_ALL | SIGHASH_FORKID))); + // Insert a non-nulldummy element into CSCript (SignSignature puts a + // NULL in the appropriate place) + mutableSpend_tx.vin[0].scriptSig[0] = OP_1; } const CTransaction spend_tx(mutableSpend_tx); @@ -206,32 +244,32 @@ // Test that invalidity under a set of flags doesn't preclude validity under // other (eg consensus) flags. - // spend_tx is invalid according to DERSIG + // spend_tx is invalid according to NULLDUMMY { CValidationState state; PrecomputedTransactionData ptd_spend_tx(spend_tx); BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true, MANDATORY_SCRIPT_VERIFY_FLAGS | - SCRIPT_VERIFY_CLEANSTACK, + SCRIPT_VERIFY_NULLDUMMY, true, true, ptd_spend_tx, nullptr)); // If we call again asking for scriptchecks (as happens in // ConnectBlock), we should add a script check object for this -- we're // not caching invalidity (if that changes, delete this test case). std::vector scriptchecks; - BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, - MANDATORY_SCRIPT_VERIFY_FLAGS | - SCRIPT_VERIFY_CLEANSTACK, - true, true, ptd_spend_tx, &scriptchecks)); + BOOST_CHECK( + CheckInputs(spend_tx, state, pcoinsTip.get(), true, + MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_NULLDUMMY, + true, true, ptd_spend_tx, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), 1); // Test that CheckInputs returns true iff cleanstack-enforcing flags are // not present. Don't add these checks to the cache, so that we can test // later that block validation works fine in the absence of cached // successes. - ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_CLEANSTACK, - false, false); + ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_NULLDUMMY, false, + false); // And if we produce a block with this tx, it should be valid (LOW_S not // enabled yet), even though there's no cache entry.