Browse Source
618d07f
MOVEONLY: tx functions to consensus/tx_verify.o (Jorge Timón)
Tree-SHA512: 63fa2777c070a344dbfe61974526a770d962e049881c6f371b0034b1682c1e6e24f47454f01ee35ded20ade34488e023d4467a05369662906b99a73bb5de8497
0.15
Wladimir J. van der Laan
8 years ago
13 changed files with 336 additions and 297 deletions
@ -0,0 +1,246 @@
@@ -0,0 +1,246 @@
|
||||
// Copyright (c) 2017-2017 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "tx_verify.h" |
||||
|
||||
#include "consensus.h" |
||||
#include "primitives/transaction.h" |
||||
#include "script/interpreter.h" |
||||
#include "validation.h" |
||||
|
||||
// TODO remove the following dependencies
|
||||
#include "chain.h" |
||||
#include "coins.h" |
||||
#include "utilmoneystr.h" |
||||
|
||||
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) |
||||
{ |
||||
if (tx.nLockTime == 0) |
||||
return true; |
||||
if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) |
||||
return true; |
||||
for (const auto& txin : tx.vin) { |
||||
if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL)) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block) |
||||
{ |
||||
assert(prevHeights->size() == tx.vin.size()); |
||||
|
||||
// Will be set to the equivalent height- and time-based nLockTime
|
||||
// values that would be necessary to satisfy all relative lock-
|
||||
// time constraints given our view of block chain history.
|
||||
// The semantics of nLockTime are the last invalid height/time, so
|
||||
// use -1 to have the effect of any height or time being valid.
|
||||
int nMinHeight = -1; |
||||
int64_t nMinTime = -1; |
||||
|
||||
// tx.nVersion is signed integer so requires cast to unsigned otherwise
|
||||
// we would be doing a signed comparison and half the range of nVersion
|
||||
// wouldn't support BIP 68.
|
||||
bool fEnforceBIP68 = static_cast<uint32_t>(tx.nVersion) >= 2 |
||||
&& flags & LOCKTIME_VERIFY_SEQUENCE; |
||||
|
||||
// Do not enforce sequence numbers as a relative lock time
|
||||
// unless we have been instructed to
|
||||
if (!fEnforceBIP68) { |
||||
return std::make_pair(nMinHeight, nMinTime); |
||||
} |
||||
|
||||
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { |
||||
const CTxIn& txin = tx.vin[txinIndex]; |
||||
|
||||
// Sequence numbers with the most significant bit set are not
|
||||
// treated as relative lock-times, nor are they given any
|
||||
// consensus-enforced meaning at this point.
|
||||
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) { |
||||
// The height of this input is not relevant for sequence locks
|
||||
(*prevHeights)[txinIndex] = 0; |
||||
continue; |
||||
} |
||||
|
||||
int nCoinHeight = (*prevHeights)[txinIndex]; |
||||
|
||||
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) { |
||||
int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast(); |
||||
// NOTE: Subtract 1 to maintain nLockTime semantics
|
||||
// BIP 68 relative lock times have the semantics of calculating
|
||||
// the first block or time at which the transaction would be
|
||||
// valid. When calculating the effective block time or height
|
||||
// for the entire transaction, we switch to using the
|
||||
// semantics of nLockTime which is the last invalid block
|
||||
// time or height. Thus we subtract 1 from the calculated
|
||||
// time or height.
|
||||
|
||||
// Time-based relative lock-times are measured from the
|
||||
// smallest allowed timestamp of the block containing the
|
||||
// txout being spent, which is the median time past of the
|
||||
// block prior.
|
||||
nMinTime = std::max(nMinTime, nCoinTime + (int64_t)((txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) << CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) - 1); |
||||
} else { |
||||
nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) - 1); |
||||
} |
||||
} |
||||
|
||||
return std::make_pair(nMinHeight, nMinTime); |
||||
} |
||||
|
||||
bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> lockPair) |
||||
{ |
||||
assert(block.pprev); |
||||
int64_t nBlockTime = block.pprev->GetMedianTimePast(); |
||||
if (lockPair.first >= block.nHeight || lockPair.second >= nBlockTime) |
||||
return false; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block) |
||||
{ |
||||
return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block)); |
||||
} |
||||
|
||||
unsigned int GetLegacySigOpCount(const CTransaction& tx) |
||||
{ |
||||
unsigned int nSigOps = 0; |
||||
for (const auto& txin : tx.vin) |
||||
{ |
||||
nSigOps += txin.scriptSig.GetSigOpCount(false); |
||||
} |
||||
for (const auto& txout : tx.vout) |
||||
{ |
||||
nSigOps += txout.scriptPubKey.GetSigOpCount(false); |
||||
} |
||||
return nSigOps; |
||||
} |
||||
|
||||
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs) |
||||
{ |
||||
if (tx.IsCoinBase()) |
||||
return 0; |
||||
|
||||
unsigned int nSigOps = 0; |
||||
for (unsigned int i = 0; i < tx.vin.size(); i++) |
||||
{ |
||||
const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); |
||||
if (prevout.scriptPubKey.IsPayToScriptHash()) |
||||
nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); |
||||
} |
||||
return nSigOps; |
||||
} |
||||
|
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags) |
||||
{ |
||||
int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR; |
||||
|
||||
if (tx.IsCoinBase()) |
||||
return nSigOps; |
||||
|
||||
if (flags & SCRIPT_VERIFY_P2SH) { |
||||
nSigOps += GetP2SHSigOpCount(tx, inputs) * WITNESS_SCALE_FACTOR; |
||||
} |
||||
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++) |
||||
{ |
||||
const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); |
||||
nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, &tx.vin[i].scriptWitness, flags); |
||||
} |
||||
return nSigOps; |
||||
} |
||||
|
||||
bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs) |
||||
{ |
||||
// Basic checks that don't depend on any context
|
||||
if (tx.vin.empty()) |
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); |
||||
if (tx.vout.empty()) |
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); |
||||
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
|
||||
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) > MAX_BLOCK_BASE_SIZE) |
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); |
||||
|
||||
// Check for negative or overflow output values
|
||||
CAmount nValueOut = 0; |
||||
for (const auto& txout : tx.vout) |
||||
{ |
||||
if (txout.nValue < 0) |
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); |
||||
if (txout.nValue > MAX_MONEY) |
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge"); |
||||
nValueOut += txout.nValue; |
||||
if (!MoneyRange(nValueOut)) |
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); |
||||
} |
||||
|
||||
// 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"); |
||||
} |
||||
} |
||||
|
||||
if (tx.IsCoinBase()) |
||||
{ |
||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) |
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); |
||||
} |
||||
else |
||||
{ |
||||
for (const auto& txin : tx.vin) |
||||
if (txin.prevout.IsNull()) |
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null"); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) |
||||
{ |
||||
// 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.
|
||||
if (!inputs.HaveInputs(tx)) |
||||
return state.Invalid(false, 0, "", "Inputs unavailable"); |
||||
|
||||
CAmount nValueIn = 0; |
||||
CAmount nFees = 0; |
||||
for (unsigned int i = 0; i < tx.vin.size(); i++) |
||||
{ |
||||
const COutPoint &prevout = tx.vin[i].prevout; |
||||
const CCoins *coins = inputs.AccessCoins(prevout.hash); |
||||
assert(coins); |
||||
|
||||
// If prev is coinbase, check that it's matured
|
||||
if (coins->IsCoinBase()) { |
||||
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) |
||||
return state.Invalid(false, |
||||
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", |
||||
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight)); |
||||
} |
||||
|
||||
// Check for negative or overflow input values
|
||||
nValueIn += coins->vout[prevout.n].nValue; |
||||
if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) |
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); |
||||
|
||||
} |
||||
|
||||
if (nValueIn < tx.GetValueOut()) |
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, |
||||
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); |
||||
|
||||
// Tally transaction fees
|
||||
CAmount nTxFee = nValueIn - tx.GetValueOut(); |
||||
if (nTxFee < 0) |
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); |
||||
nFees += nTxFee; |
||||
if (!MoneyRange(nFees)) |
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); |
||||
return true; |
||||
} |
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2017-2017 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_CONSENSUS_TX_VERIFY_H |
||||
#define BITCOIN_CONSENSUS_TX_VERIFY_H |
||||
|
||||
#include <stdint.h> |
||||
#include <vector> |
||||
|
||||
class CBlockIndex; |
||||
class CCoinsViewCache; |
||||
class CTransaction; |
||||
class CValidationState; |
||||
|
||||
/** Transaction validation functions */ |
||||
|
||||
/** Context-independent validity checks */ |
||||
bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true); |
||||
|
||||
namespace Consensus { |
||||
/**
|
||||
* Check whether all inputs of this transaction are valid (no double spends and amounts) |
||||
* This does not modify the UTXO set. This does not check scripts and sigs. |
||||
* Preconditions: tx.IsCoinBase() is false. |
||||
*/ |
||||
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight); |
||||
} // namespace Consensus
|
||||
|
||||
/** Auxiliary functions for transaction validation (ideally should not be exposed) */ |
||||
|
||||
/**
|
||||
* Count ECDSA signature operations the old-fashioned (pre-0.6) way |
||||
* @return number of sigops this transaction's outputs will produce when spent |
||||
* @see CTransaction::FetchInputs |
||||
*/ |
||||
unsigned int GetLegacySigOpCount(const CTransaction& tx); |
||||
|
||||
/**
|
||||
* Count ECDSA signature operations in pay-to-script-hash inputs. |
||||
* |
||||
* @param[in] mapInputs Map of previous transactions that have outputs we're spending |
||||
* @return maximum number of sigops required to validate this transaction's inputs |
||||
* @see CTransaction::FetchInputs |
||||
*/ |
||||
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& mapInputs); |
||||
|
||||
/**
|
||||
* Compute total signature operation cost of a transaction. |
||||
* @param[in] tx Transaction for which we are computing the cost |
||||
* @param[in] inputs Map of previous transactions that have outputs we're spending |
||||
* @param[out] flags Script verification flags |
||||
* @return Total signature operation cost of tx |
||||
*/ |
||||
int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& inputs, int flags); |
||||
|
||||
/**
|
||||
* Check if transaction is final and can be included in a block with the |
||||
* specified height and time. Consensus critical. |
||||
*/ |
||||
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime); |
||||
|
||||
/**
|
||||
* Calculates the block height and previous block's median time past at |
||||
* which the transaction will be considered final in the context of BIP 68. |
||||
* Also removes from the vector of input heights any entries which did not |
||||
* correspond to sequence locked inputs as they do not affect the calculation. |
||||
*/ |
||||
std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block); |
||||
|
||||
bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> lockPair); |
||||
/**
|
||||
* Check if transaction is final per BIP 68 sequence numbers and can be included in a block. |
||||
* Consensus critical. Takes as input a list of heights at which tx's inputs (in order) confirmed. |
||||
*/ |
||||
bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block); |
||||
|
||||
#endif // BITCOIN_CONSENSUS_TX_VERIFY_H
|
Loading…
Reference in new issue