Changeset View
Changeset View
Standalone View
Standalone View
src/consensus/tx_verify.cpp
Show First 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | |||||
uint64_t GetTransactionSigOpCount(const CTransaction &tx, | uint64_t GetTransactionSigOpCount(const CTransaction &tx, | ||||
const CCoinsViewCache &view, uint32_t flags) { | const CCoinsViewCache &view, uint32_t flags) { | ||||
return GetSigOpCountWithoutP2SH(tx, flags) + | return GetSigOpCountWithoutP2SH(tx, flags) + | ||||
GetP2SHSigOpCount(tx, view, flags); | GetP2SHSigOpCount(tx, view, flags); | ||||
} | } | ||||
static bool CheckTransactionCommon(const CTransaction &tx, | static bool CheckTransactionCommon(const CTransaction &tx, | ||||
CValidationState &state, | CValidationState &state) { | ||||
bool fCheckDuplicateInputs) { | |||||
// Basic checks that don't depend on any context | // Basic checks that don't depend on any context | ||||
if (tx.vin.empty()) { | if (tx.vin.empty()) { | ||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); | return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); | ||||
} | } | ||||
if (tx.vout.empty()) { | if (tx.vout.empty()) { | ||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); | return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); | ||||
} | } | ||||
Show All 23 Lines | for (const auto &txout : tx.vout) { | ||||
} | } | ||||
} | } | ||||
if (GetSigOpCountWithoutP2SH(tx, SCRIPT_ENABLE_CHECKDATASIG) > | if (GetSigOpCountWithoutP2SH(tx, SCRIPT_ENABLE_CHECKDATASIG) > | ||||
MAX_TX_SIGOPS_COUNT) { | MAX_TX_SIGOPS_COUNT) { | ||||
return state.DoS(100, false, REJECT_INVALID, "bad-txn-sigops"); | return state.DoS(100, false, REJECT_INVALID, "bad-txn-sigops"); | ||||
} | } | ||||
// Check for duplicate inputs - note that this check is slow so we skip it | |||||
// in CheckBlock | |||||
if (fCheckDuplicateInputs) { | |||||
std::set<COutPoint> vInOutPoints; | |||||
for (const auto &txin : tx.vin) { | |||||
if (!vInOutPoints.insert(txin.prevout).second) { | |||||
return state.DoS(100, false, REJECT_INVALID, | |||||
"bad-txns-inputs-duplicate"); | |||||
} | |||||
} | |||||
} | |||||
return true; | return true; | ||||
} | } | ||||
bool CheckCoinbase(const CTransaction &tx, CValidationState &state, | bool CheckCoinbase(const CTransaction &tx, CValidationState &state) { | ||||
bool fCheckDuplicateInputs) { | |||||
if (!tx.IsCoinBase()) { | if (!tx.IsCoinBase()) { | ||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, | return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, | ||||
"first tx is not coinbase"); | "first tx is not coinbase"); | ||||
} | } | ||||
if (!CheckTransactionCommon(tx, state, fCheckDuplicateInputs)) { | if (!CheckTransactionCommon(tx, state)) { | ||||
// CheckTransactionCommon fill in the state. | // CheckTransactionCommon fill in the state. | ||||
return false; | return false; | ||||
} | } | ||||
if (tx.vin[0].scriptSig.size() < 2 || | if (tx.vin[0].scriptSig.size() < 2 || | ||||
tx.vin[0].scriptSig.size() > MAX_COINBASE_SCRIPTSIG_SIZE) { | tx.vin[0].scriptSig.size() > MAX_COINBASE_SCRIPTSIG_SIZE) { | ||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); | return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CheckRegularTransaction(const CTransaction &tx, CValidationState &state, | bool CheckRegularTransaction(const CTransaction &tx, CValidationState &state, | ||||
bool fCheckDuplicateInputs) { | bool fCheckDuplicateInputs) { | ||||
if (tx.IsCoinBase()) { | if (tx.IsCoinBase()) { | ||||
return state.DoS(100, false, REJECT_INVALID, "bad-tx-coinbase"); | return state.DoS(100, false, REJECT_INVALID, "bad-tx-coinbase"); | ||||
} | } | ||||
if (!CheckTransactionCommon(tx, state, fCheckDuplicateInputs)) { | if (!CheckTransactionCommon(tx, state)) { | ||||
// CheckTransactionCommon fill in the state. | // CheckTransactionCommon fill in the state. | ||||
return false; | return false; | ||||
} | } | ||||
for (const auto &txin : tx.vin) { | for (const auto &txin : tx.vin) { | ||||
if (txin.prevout.IsNull()) { | if (txin.prevout.IsNull()) { | ||||
return state.DoS(10, false, REJECT_INVALID, | return state.DoS(10, false, REJECT_INVALID, | ||||
"bad-txns-prevout-null"); | "bad-txns-prevout-null"); | ||||
} | } | ||||
} | } | ||||
// Check for duplicate inputs - note that this check is slow so we skip it | |||||
// in CheckBlock | |||||
if (fCheckDuplicateInputs) { | |||||
std::unordered_set<COutPoint, SaltedOutpointHasher> vInOutPoints; | |||||
for (const auto &txin : tx.vin) { | |||||
if (!vInOutPoints.insert(txin.prevout).second) { | |||||
return state.DoS(100, false, REJECT_INVALID, | |||||
"bad-txns-inputs-duplicate"); | |||||
} | |||||
} | |||||
} | |||||
return true; | return true; | ||||
} | } | ||||
namespace Consensus { | namespace Consensus { | ||||
bool CheckTxInputs(const CTransaction &tx, CValidationState &state, | bool CheckTxInputs(const CTransaction &tx, CValidationState &state, | ||||
const CCoinsViewCache &inputs, int nSpendHeight) { | const CCoinsViewCache &inputs, int nSpendHeight) { | ||||
// This doesn't trigger the DoS code on purpose; if it did, it would make it | // This doesn't trigger the DoS code on purpose; if it did, it would make it | ||||
// easier for an attacker to attempt to split the network. | // easier for an attacker to attempt to split the network. | ||||
if (!inputs.HaveInputs(tx)) { | if (!inputs.HaveInputs(tx)) { | ||||
return state.Invalid(false, 0, "", "Inputs unavailable"); | return state.Invalid(false, 0, "", "Inputs unavailable"); | ||||
} | } | ||||
// For duplicate input check. | |||||
std::unordered_set<COutPoint, SaltedOutpointHasher> vInOutPoints; | |||||
Amount nValueIn = Amount::zero(); | Amount nValueIn = Amount::zero(); | ||||
Amount nFees = Amount::zero(); | Amount nFees = Amount::zero(); | ||||
for (const auto &in : tx.vin) { | for (const auto &in : tx.vin) { | ||||
const COutPoint &prevout = in.prevout; | const COutPoint &prevout = in.prevout; | ||||
if (!vInOutPoints.insert(prevout).second) { | |||||
return state.DoS(100, false, REJECT_INVALID, | |||||
"bad-txns-inputs-duplicate"); | |||||
} | |||||
const Coin &coin = inputs.AccessCoin(prevout); | const Coin &coin = inputs.AccessCoin(prevout); | ||||
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()) { | if (coin.IsCoinBase()) { | ||||
if (nSpendHeight - coin.GetHeight() < COINBASE_MATURITY) { | if (nSpendHeight - coin.GetHeight() < COINBASE_MATURITY) { | ||||
return state.Invalid( | return state.Invalid( | ||||
false, REJECT_INVALID, | false, REJECT_INVALID, | ||||
Show All 35 Lines |