diff --git a/src/main.cpp b/src/main.cpp index cb6cef5bb..1569ff672 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1171,6 +1171,14 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(100, error("CheckTransaction(): coinbase has joinsplits"), REJECT_INVALID, "bad-cb-has-joinsplits"); + // A coinbase transaction cannot have spend descriptions or output descriptions + if (tx.vShieldedSpend.size() > 0) + return state.DoS(100, error("CheckTransaction(): coinbase has spend descriptions"), + REJECT_INVALID, "bad-cb-has-spend-description"); + if (tx.vShieldedOutput.size() > 0) + return state.DoS(100, error("CheckTransaction(): coinbase has output descriptions"), + REJECT_INVALID, "bad-cb-has-output-description"); + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) return state.DoS(100, error("CheckTransaction(): coinbase script size"), REJECT_INVALID, "bad-cb-length"); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 30c69882a..767f9f75f 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -438,6 +438,28 @@ void test_simple_sapling_invalidity(uint32_t consensusBranchId, CMutableTransact BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state)); } + { + CMutableTransaction newTx(tx); + CValidationState state; + + // Create a coinbase transaction + CTxIn vin; + vin.prevout = COutPoint(); + newTx.vin.push_back(vin); + CTxOut vout; + vout.nValue = 1; + newTx.vout.push_back(vout); + + newTx.vShieldedOutput.push_back(OutputDescription()); + + BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state)); + BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-output-description"); + + newTx.vShieldedSpend.push_back(SpendDescription()); + + BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state)); + BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-spend-description"); + } } void test_simple_joinsplit_invalidity(uint32_t consensusBranchId, CMutableTransaction tx)