diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -102,6 +102,7 @@ std::vector coinbaseTxns; // private/public key needed to spend coinbase transactions. CKey coinbaseKey; + CScript coinbaseP2SHNullDummyScript; }; class CTxMemPoolEntry; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -20,6 +20,7 @@ #include "rpc/server.h" #include "script/scriptcache.h" #include "script/sigcache.h" +#include "script/standard.h" #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" @@ -137,12 +138,32 @@ TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) { - // Generate a 100-block chain: + // Generate a 102-block chain: coinbaseKey.MakeNewKey(true); CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + + // Create a p2pkh coinbase + std::vector noTxns; + CBlock b1 = CreateAndProcessBlock(noTxns, scriptPubKey); + coinbaseTxns.push_back(*b1.vtx[0]); + + // Create a coinbase that can fail NULLDUMMY checks. This is for testing + // consensus vs non-standard rules in `checkinputs_test`. We don't + // particularly want to sign things, so we'll pass a null signature and + // use OP_DROP OP_TRUE at the end. + CKey dummyKey; + dummyKey.MakeNewKey(true); + coinbaseP2SHNullDummyScript = + CScript() << OP_1 << ToByteVector(coinbaseKey.GetPubKey()) + << ToByteVector(dummyKey.GetPubKey()) << OP_2 + << OP_CHECKMULTISIG << OP_DROP << OP_1; + + CBlock b2 = CreateAndProcessBlock(noTxns, coinbaseP2SHNullDummyScript); + coinbaseTxns.push_back(*b2.vtx[0]); + + // Mature the coinbases for (int i = 0; i < COINBASE_MATURITY; i++) { - std::vector noTxns; CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); coinbaseTxns.push_back(*b.vtx[0]); } @@ -164,6 +185,14 @@ for (const CMutableTransaction &tx : txns) { block.vtx.push_back(MakeTransactionRef(tx)); } + + // Order transactions by canonical order + std::sort(std::begin(block.vtx) + 1, std::end(block.vtx), + [](const std::shared_ptr &txa, + const std::shared_ptr &txb) -> bool { + return txa->GetId() < txb->GetId(); + }); + // IncrementExtraNonce creates a valid coinbase and merkleRoot unsigned int extraNonce = 0; IncrementExtraNonce(config, &block, chainActive.Tip(), extraNonce); 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 @@ -114,6 +114,8 @@ bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, nullptr); + std::cout << "CheckInputs: " << ret + << " Failure: " << state.GetRejectReason(); // CheckInputs should succeed iff test_flags doesn't intersect with // failing_flags @@ -174,7 +176,7 @@ 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(coinbaseTxns[1].GetId(), 0); mutableSpend_tx.vout.resize(4); mutableSpend_tx.vout[0].nValue = 11 * CENT; mutableSpend_tx.vout[0].scriptPubKey = p2sh_scriptPubKey; @@ -194,10 +196,13 @@ std::vector vchSig; uint256 hash = SignatureHash( p2pk_scriptPubKey, CTransaction(mutableSpend_tx), 0, - SigHashType().withForkId(), coinbaseTxns[0].vout[0].nValue); + SigHashType().withForkId(), coinbaseTxns[1].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; + // First item will be dropped by CHECKMULTISIG This is to check + // nulldummy enforcement. It is OP_1 instead of OP_0. + // Also pass a null signature so we don't hit NULLFAIL + mutableSpend_tx.vin[0].scriptSig << OP_1 << OP_0; } const CTransaction spend_tx(mutableSpend_tx); @@ -213,25 +218,25 @@ 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.