mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 06:49:38 -04:00
consensus: Use Coin span in CheckTxInputs
Move the responsibility of retrieving coins from CheckTxInputs to its caller. The missing inputs check will be moved in an upcoming commit. This is a part of a series of commits for removing access to the CCoinsViewCache in consensus verification functions. The goal is to allow calling verification functions with pre-fetched, or a user-defined set of coins. Define two explicit template specializations for both a span of references to coins and a span of coins. This allows using it for both Coin entries referenced from the CCoinsViewCache, and from contiguous memory, like the vector in CBlockUndo.
This commit is contained in:
parent
781668b6e8
commit
c1285a81de
5 changed files with 26 additions and 12 deletions
|
@ -186,7 +186,8 @@ template int64_t GetTransactionSigOpCost<std::span<const Coin>>(
|
|||
template int64_t GetTransactionSigOpCost<std::span<std::reference_wrapper<const Coin>>>(
|
||||
const CTransaction& tx, const std::span<std::reference_wrapper<const Coin>> coins, uint32_t flags);
|
||||
|
||||
bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
|
||||
template <typename T>
|
||||
bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, const T coins, int nSpendHeight, CAmount& txfee)
|
||||
{
|
||||
// are the actual inputs available?
|
||||
if (!inputs.HaveInputs(tx)) {
|
||||
|
@ -195,15 +196,16 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
|
|||
}
|
||||
|
||||
CAmount nValueIn = 0;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
|
||||
const COutPoint &prevout = tx.vin[i].prevout;
|
||||
const Coin& coin = inputs.AccessCoin(prevout);
|
||||
Assert(coins.size() == tx.vin.size());
|
||||
auto input_it = tx.vin.begin();
|
||||
for (auto it = coins.begin(); it != coins.end(); ++it, ++input_it) {
|
||||
const Coin& coin{GetCoin(*it)};
|
||||
assert(!coin.IsSpent());
|
||||
|
||||
// If prev is coinbase, check that it's matured
|
||||
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
|
||||
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase",
|
||||
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
||||
strprintf("tried to spend coinbase at depth %d", static_cast<int>(nSpendHeight - coin.nHeight)));
|
||||
}
|
||||
|
||||
// Check for negative or overflow input values
|
||||
|
@ -228,3 +230,9 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
|
|||
txfee = txfee_aux;
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool Consensus::CheckTxInputs<std::span<const Coin>>(
|
||||
const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, const std::span<const Coin> coins, int nSpendHeight, CAmount& txfee);
|
||||
|
||||
template bool Consensus::CheckTxInputs<std::span<std::reference_wrapper<const Coin>>>(
|
||||
const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, const std::span<std::reference_wrapper<const Coin>> coins, int nSpendHeight, CAmount& txfee);
|
||||
|
|
|
@ -25,7 +25,8 @@ namespace Consensus {
|
|||
* @param[out] txfee Set to the transaction fee if successful.
|
||||
* Preconditions: tx.IsCoinBase() is false.
|
||||
*/
|
||||
[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
|
||||
template <typename T>
|
||||
[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, const T coins, int nSpendHeight, CAmount& txfee);
|
||||
} // namespace Consensus
|
||||
|
||||
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
|
||||
|
|
|
@ -255,7 +255,10 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
|
|||
// It is not allowed to call CheckTxInputs if CheckTransaction failed
|
||||
return;
|
||||
}
|
||||
if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out)) {
|
||||
// are the actual inputs available?
|
||||
if (!coins_view_cache.HaveInputs(transaction)) return;
|
||||
auto coins{coins_view_cache.AccessCoins(transaction)};
|
||||
if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, std::span{coins}, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out)) {
|
||||
assert(MoneyRange(tx_fee_out));
|
||||
}
|
||||
},
|
||||
|
|
|
@ -777,7 +777,8 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
|
|||
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
|
||||
CAmount txfee = 0;
|
||||
assert(!tx.IsCoinBase());
|
||||
assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee));
|
||||
auto coins{mempoolDuplicate.AccessCoins(tx)};
|
||||
assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, std::span{coins}, spendheight, txfee));
|
||||
for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
|
||||
AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
|
||||
}
|
||||
|
|
|
@ -874,7 +874,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||
}
|
||||
|
||||
// The mempool holds txs for the next block, so pass height+1 to CheckTxInputs
|
||||
if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees)) {
|
||||
auto coins{m_view.AccessCoins(tx)};
|
||||
if (!Consensus::CheckTxInputs(tx, state, m_view, std::span{coins}, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees)) {
|
||||
return false; // state filled in by CheckTxInputs
|
||||
}
|
||||
|
||||
|
@ -887,7 +888,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|||
return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, "bad-witness-nonstandard");
|
||||
}
|
||||
|
||||
auto coins{m_view.AccessCoins(tx)};
|
||||
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, std::span{coins}, STANDARD_SCRIPT_VERIFY_FLAGS);
|
||||
|
||||
// Keep track of transactions that spend a coinbase, which we re-scan
|
||||
|
@ -2648,14 +2648,15 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|||
{
|
||||
if (!state.IsValid()) break;
|
||||
const CTransaction &tx = *(block.vtx[i]);
|
||||
|
||||
nInputs += tx.vin.size();
|
||||
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
CAmount txfee = 0;
|
||||
TxValidationState tx_state;
|
||||
if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, txfee)) {
|
||||
|
||||
auto coins{view.AccessCoins(tx)};
|
||||
if (!Consensus::CheckTxInputs(tx, tx_state, view, std::span{coins}, pindex->nHeight, txfee)) {
|
||||
// Any transaction validation failure in ConnectBlock is a block consensus failure
|
||||
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
|
||||
tx_state.GetRejectReason(),
|
||||
|
|
Loading…
Add table
Reference in a new issue