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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
// 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