mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -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>>>(
|
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);
|
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?
|
// are the actual inputs available?
|
||||||
if (!inputs.HaveInputs(tx)) {
|
if (!inputs.HaveInputs(tx)) {
|
||||||
|
@ -195,15 +196,16 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
|
||||||
}
|
}
|
||||||
|
|
||||||
CAmount nValueIn = 0;
|
CAmount nValueIn = 0;
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
|
Assert(coins.size() == tx.vin.size());
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
auto input_it = tx.vin.begin();
|
||||||
const Coin& coin = inputs.AccessCoin(prevout);
|
for (auto it = coins.begin(); it != coins.end(); ++it, ++input_it) {
|
||||||
|
const Coin& coin{GetCoin(*it)};
|
||||||
assert(!coin.IsSpent());
|
assert(!coin.IsSpent());
|
||||||
|
|
||||||
// If prev is coinbase, check that it's matured
|
// If prev is coinbase, check that it's matured
|
||||||
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
|
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
|
||||||
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase",
|
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
|
// Check for negative or overflow input values
|
||||||
|
@ -228,3 +230,9 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
|
||||||
txfee = txfee_aux;
|
txfee = txfee_aux;
|
||||||
return true;
|
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.
|
* @param[out] txfee Set to the transaction fee if successful.
|
||||||
* Preconditions: tx.IsCoinBase() is false.
|
* 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
|
} // namespace Consensus
|
||||||
|
|
||||||
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
|
/** 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
|
// It is not allowed to call CheckTxInputs if CheckTransaction failed
|
||||||
return;
|
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));
|
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
|
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
|
||||||
CAmount txfee = 0;
|
CAmount txfee = 0;
|
||||||
assert(!tx.IsCoinBase());
|
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);
|
for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
|
||||||
AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
|
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
|
// 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
|
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");
|
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);
|
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, std::span{coins}, STANDARD_SCRIPT_VERIFY_FLAGS);
|
||||||
|
|
||||||
// Keep track of transactions that spend a coinbase, which we re-scan
|
// 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;
|
if (!state.IsValid()) break;
|
||||||
const CTransaction &tx = *(block.vtx[i]);
|
const CTransaction &tx = *(block.vtx[i]);
|
||||||
|
|
||||||
nInputs += tx.vin.size();
|
nInputs += tx.vin.size();
|
||||||
|
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsCoinBase())
|
||||||
{
|
{
|
||||||
CAmount txfee = 0;
|
CAmount txfee = 0;
|
||||||
TxValidationState tx_state;
|
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
|
// Any transaction validation failure in ConnectBlock is a block consensus failure
|
||||||
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
|
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
|
||||||
tx_state.GetRejectReason(),
|
tx_state.GetRejectReason(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue