Skip to content

Commit 0e3a411

Browse files
committed
Merge #8498: Near-Bugfix: Optimization: Minimize the number of times it is checked that no money...
4e955c5 Near-Bugfix: Reestablish consensus check removed in 8d7849b (Jorge Timón) 3e8c916 Introduce CheckInputsAndUpdateCoins static wrapper in txmempool.cpp (Jorge Timón) 832e074 Optimization: Minimize the number of times it is checked that no money is created (Jorge Timón) 3f0ee3e Proper indentation for CheckTxInputs and other minor fixes (Jorge Timón) Pull request description: ...is created by individual transactions to 2 places (but call only once in each): - ConnectBlock ( before calculated fees per txs twice ) - AcceptToMemoryPoolWorker ( before called CheckTxInputs 4 times and calculated fees per tx one extra time ) Also call tx.GetValueOut() only once per call of CheckTxInputs (instead of 2) For more motivation: ~~https://github.com/bitcoin/bitcoin/blob/master/src/main.cpp#L1493~~ jtimon/bitcoin@0.13-consensus-inputs...jtimon:0.13-consensus-inputs-comments EDIT: partially replaces #6445 Near-Bugfix as pointed out in bitcoin/bitcoin#8498 (comment) Tree-SHA512: c71188e7c7c2425c9170ed7b803896755a92fd22f43b136eedaa6e554106696f0b10271d0ef0d0127c1eaafbc31d12eb19143df4f1b6882feecedf6ef05ea346
2 parents 5a9da37 + 4e955c5 commit 0e3a411

File tree

4 files changed

+69
-62
lines changed

4 files changed

+69
-62
lines changed

src/consensus/tx_verify.cpp

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include "chain.h"
1414
#include "coins.h"
1515
#include "utilmoneystr.h"
16-
16+
1717
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
1818
{
1919
if (tx.nLockTime == 0)
@@ -205,46 +205,46 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
205205
return true;
206206
}
207207

208-
bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight)
208+
bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
209209
{
210-
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
211-
// for an attacker to attempt to split the network.
212-
if (!inputs.HaveInputs(tx))
213-
return state.Invalid(false, 0, "", "Inputs unavailable");
214-
215-
CAmount nValueIn = 0;
216-
CAmount nFees = 0;
217-
for (unsigned int i = 0; i < tx.vin.size(); i++)
218-
{
219-
const COutPoint &prevout = tx.vin[i].prevout;
220-
const Coin& coin = inputs.AccessCoin(prevout);
221-
assert(!coin.IsSpent());
222-
223-
// If prev is coinbase, check that it's matured
224-
if (coin.IsCoinBase()) {
225-
if (nSpendHeight - coin.nHeight < COINBASE_MATURITY)
226-
return state.Invalid(false,
227-
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
228-
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
229-
}
230-
231-
// Check for negative or overflow input values
232-
nValueIn += coin.out.nValue;
233-
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn))
234-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
210+
// are the actual inputs available?
211+
if (!inputs.HaveInputs(tx)) {
212+
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false,
213+
strprintf("%s: inputs missing/spent", __func__));
214+
}
215+
216+
CAmount nValueIn = 0;
217+
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
218+
const COutPoint &prevout = tx.vin[i].prevout;
219+
const Coin& coin = inputs.AccessCoin(prevout);
220+
assert(!coin.IsSpent());
221+
222+
// If prev is coinbase, check that it's matured
223+
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
224+
return state.Invalid(false,
225+
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
226+
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
227+
}
235228

229+
// Check for negative or overflow input values
230+
nValueIn += coin.out.nValue;
231+
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
232+
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
236233
}
234+
}
235+
236+
const CAmount value_out = tx.GetValueOut();
237+
if (nValueIn < value_out) {
238+
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
239+
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
240+
}
241+
242+
// Tally transaction fees
243+
const CAmount txfee_aux = nValueIn - value_out;
244+
if (!MoneyRange(txfee_aux)) {
245+
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
246+
}
237247

238-
if (nValueIn < tx.GetValueOut())
239-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
240-
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())));
241-
242-
// Tally transaction fees
243-
CAmount nTxFee = nValueIn - tx.GetValueOut();
244-
if (nTxFee < 0)
245-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative");
246-
nFees += nTxFee;
247-
if (!MoneyRange(nFees))
248-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
248+
txfee = txfee_aux;
249249
return true;
250250
}

src/consensus/tx_verify.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef BITCOIN_CONSENSUS_TX_VERIFY_H
66
#define BITCOIN_CONSENSUS_TX_VERIFY_H
77

8+
#include "amount.h"
9+
810
#include <stdint.h>
911
#include <vector>
1012

@@ -22,9 +24,10 @@ namespace Consensus {
2224
/**
2325
* Check whether all inputs of this transaction are valid (no double spends and amounts)
2426
* This does not modify the UTXO set. This does not check scripts and sigs.
27+
* @param[out] txfee Set to the transaction fee if successful.
2528
* Preconditions: tx.IsCoinBase() is false.
2629
*/
27-
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight);
30+
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
2831
} // namespace Consensus
2932

3033
/** Auxiliary functions for transaction validation (ideally should not be exposed) */

src/txmempool.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,15 @@ void CTxMemPool::clear()
607607
_clear();
608608
}
609609

610+
static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight)
611+
{
612+
CValidationState state;
613+
CAmount txfee = 0;
614+
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee);
615+
assert(fCheckResult);
616+
UpdateCoins(tx, mempoolDuplicate, 1000000);
617+
}
618+
610619
void CTxMemPool::check(const CCoinsViewCache *pcoins) const
611620
{
612621
if (nCheckFrequency == 0)
@@ -621,7 +630,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
621630
uint64_t innerUsage = 0;
622631

623632
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins));
624-
const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate);
633+
const int64_t spendheight = GetSpendHeight(mempoolDuplicate);
625634

626635
LOCK(cs);
627636
std::list<const CTxMemPoolEntry*> waitingOnDependants;
@@ -700,11 +709,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
700709
if (fDependsWait)
701710
waitingOnDependants.push_back(&(*it));
702711
else {
703-
CValidationState state;
704-
bool fCheckResult = tx.IsCoinBase() ||
705-
Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight);
706-
assert(fCheckResult);
707-
UpdateCoins(tx, mempoolDuplicate, 1000000);
712+
CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight);
708713
}
709714
}
710715
unsigned int stepsSinceLastRemove = 0;
@@ -717,10 +722,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
717722
stepsSinceLastRemove++;
718723
assert(stepsSinceLastRemove < waitingOnDependants.size());
719724
} else {
720-
bool fCheckResult = entry->GetTx().IsCoinBase() ||
721-
Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight);
722-
assert(fCheckResult);
723-
UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000);
725+
CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight);
724726
stepsSinceLastRemove = 0;
725727
}
726728
}

src/validation.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
534534
CCoinsView dummy;
535535
CCoinsViewCache view(&dummy);
536536

537-
CAmount nValueIn = 0;
538537
LockPoints lp;
539538
{
540539
LOCK(pool.cs);
@@ -565,8 +564,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
565564
// Bring the best block into scope
566565
view.GetBestBlock();
567566

568-
nValueIn = view.GetValueIn(tx);
569-
570567
// we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
571568
view.SetBackend(dummy);
572569

@@ -577,6 +574,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
577574
// CoinsViewCache instead of create its own
578575
if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
579576
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
577+
578+
} // end LOCK(pool.cs)
579+
580+
CAmount nFees = 0;
581+
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) {
582+
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
580583
}
581584

582585
// Check for non-standard pay-to-script-hash in inputs
@@ -589,8 +592,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
589592

590593
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
591594

592-
CAmount nValueOut = tx.GetValueOut();
593-
CAmount nFees = nValueIn-nValueOut;
594595
// nModifiedFees includes any fee deltas from PrioritiseTransaction
595596
CAmount nModifiedFees = nFees;
596597
pool.ApplyDelta(hash, nModifiedFees);
@@ -1247,9 +1248,6 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
12471248
{
12481249
if (!tx.IsCoinBase())
12491250
{
1250-
if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs)))
1251-
return false;
1252-
12531251
if (pvChecks)
12541252
pvChecks->reserve(tx.vin.size());
12551253

@@ -1762,9 +1760,15 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
17621760

17631761
if (!tx.IsCoinBase())
17641762
{
1765-
if (!view.HaveInputs(tx))
1766-
return state.DoS(100, error("ConnectBlock(): inputs missing/spent"),
1767-
REJECT_INVALID, "bad-txns-inputs-missingorspent");
1763+
CAmount txfee = 0;
1764+
if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) {
1765+
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
1766+
}
1767+
nFees += txfee;
1768+
if (!MoneyRange(nFees)) {
1769+
return state.DoS(100, error("%s: accumulated fee in the block out of range.", __func__),
1770+
REJECT_INVALID, "bad-txns-accumulated-fee-outofrange");
1771+
}
17681772

17691773
// Check that transaction is BIP68 final
17701774
// BIP68 lock checks (as opposed to nLockTime checks) must
@@ -1792,8 +1796,6 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
17921796
txdata.emplace_back(tx);
17931797
if (!tx.IsCoinBase())
17941798
{
1795-
nFees += view.GetValueIn(tx)-tx.GetValueOut();
1796-
17971799
std::vector<CScriptCheck> vChecks;
17981800
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
17991801
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr))

0 commit comments

Comments
 (0)