diff --git a/src/main.cpp b/src/main.cpp index 09c357b01..72b3cabe0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1629,7 +1629,12 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c assert(coins); if (coins->IsCoinBase()) { - + // Ensure that coinbases cannot be spent to transparent outputs + if (!tx.vout.empty()) { + return state.Invalid( + error("CheckInputs(): tried to spend coinbase with transparent outputs"), + REJECT_INVALID, "bad-txns-coinbase-spend-has-transparent-outputs"); + } } // Check for negative or overflow input values diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index ce9b7eeb2..9edf432ea 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -6,6 +6,9 @@ #include "random.h" #include "uint256.h" #include "test/test_bitcoin.h" +#include "consensus/validation.h" +#include "main.h" +#include "undo.h" #include #include @@ -475,4 +478,48 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test) BOOST_CHECK(missed_an_entry); } +BOOST_AUTO_TEST_CASE(coins_coinbase_spends) +{ + CCoinsViewTest base; + CCoinsViewCacheTest cache(&base); + + // Create coinbase transaction + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].scriptSig = CScript() << OP_1; + mtx.vin[0].nSequence = 0; + mtx.vout.resize(1); + mtx.vout[0].nValue = 500; + mtx.vout[0].scriptPubKey = CScript() << OP_1; + + CTransaction tx(mtx); + + BOOST_CHECK(tx.IsCoinBase()); + + CValidationState state; + UpdateCoins(tx, state, cache, 100); + + // Create coinbase spend + CMutableTransaction mtx2; + mtx2.vin.resize(1); + mtx2.vin[0].prevout = COutPoint(tx.GetHash(), 0); + mtx2.vin[0].scriptSig = CScript() << OP_1; + mtx2.vin[0].nSequence = 0; + + { + CTransaction tx2(mtx2); + BOOST_CHECK(NonContextualCheckInputs(tx2, state, cache, false, SCRIPT_VERIFY_NONE, false, NULL)); + } + + mtx2.vout.resize(1); + mtx2.vout[0].nValue = 500; + mtx2.vout[0].scriptPubKey = CScript() << OP_1; + + { + CTransaction tx2(mtx2); + BOOST_CHECK(!NonContextualCheckInputs(tx2, state, cache, false, SCRIPT_VERIFY_NONE, false, NULL)); + BOOST_CHECK(state.GetRejectReason() == "bad-txns-coinbase-spend-has-transparent-outputs"); + } +} + BOOST_AUTO_TEST_SUITE_END()