mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-11 11:16:09 -05:00
![Suhas Daftuar](/assets/img/avatar_default.png)
Our policy checks for non-standard inputs depend only on the non-witness portion of a transaction: we look up the scriptPubKey of the input being spent from our UTXO set (which is covered by the input txid), and the p2sh checks only rely on the scriptSig portion of the input. Consequently it's safe to add txids of transactions that fail these checks to the reject filter, as the witness is irrelevant to the failure. This is helpful for any situation where we might request the transaction again via txid (either from txid-relay peers, or if we might fetch the transaction via txid due to parent-fetching of orphans). Further, in preparation for future witness versions being deployed on the network, ensure that WITNESS_UNKNOWN transactions are rejected in AreInputsStandard(), so that transactions spending v1 (or greater) witness outputs will fall into this category of having their txid added to the reject filter.
154 lines
6.7 KiB
C++
154 lines
6.7 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2019 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_VALIDATION_H
|
|
#define BITCOIN_CONSENSUS_VALIDATION_H
|
|
|
|
#include <string>
|
|
#include <version.h>
|
|
#include <consensus/consensus.h>
|
|
#include <primitives/transaction.h>
|
|
#include <primitives/block.h>
|
|
|
|
/** A "reason" why a transaction was invalid, suitable for determining whether the
|
|
* provider of the transaction should be banned/ignored/disconnected/etc.
|
|
*/
|
|
enum class TxValidationResult {
|
|
TX_RESULT_UNSET = 0, //!< initial value. Tx has not yet been rejected
|
|
TX_CONSENSUS, //!< invalid by consensus rules
|
|
/**
|
|
* Invalid by a change to consensus rules more recent than SegWit.
|
|
* Currently unused as there are no such consensus rule changes, and any download
|
|
* sources realistically need to support SegWit in order to provide useful data,
|
|
* so differentiating between always-invalid and invalid-by-pre-SegWit-soft-fork
|
|
* is uninteresting.
|
|
*/
|
|
TX_RECENT_CONSENSUS_CHANGE,
|
|
TX_INPUTS_NOT_STANDARD, //!< inputs (covered by txid) failed policy rules
|
|
TX_NOT_STANDARD, //!< otherwise didn't meet our local policy rules
|
|
TX_MISSING_INPUTS, //!< transaction was missing some of its inputs
|
|
TX_PREMATURE_SPEND, //!< transaction spends a coinbase too early, or violates locktime/sequence locks
|
|
/**
|
|
* Transaction might have a witness prior to SegWit
|
|
* activation, or witness may have been malleated (which includes
|
|
* non-standard witnesses).
|
|
*/
|
|
TX_WITNESS_MUTATED,
|
|
/**
|
|
* Transaction is missing a witness.
|
|
*/
|
|
TX_WITNESS_STRIPPED,
|
|
/**
|
|
* Tx already in mempool or conflicts with a tx in the chain
|
|
* (if it conflicts with another tx in mempool, we use MEMPOOL_POLICY as it failed to reach the RBF threshold)
|
|
* Currently this is only used if the transaction already exists in the mempool or on chain.
|
|
*/
|
|
TX_CONFLICT,
|
|
TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/RBF/etc limits
|
|
};
|
|
|
|
/** A "reason" why a block was invalid, suitable for determining whether the
|
|
* provider of the block should be banned/ignored/disconnected/etc.
|
|
* These are much more granular than the rejection codes, which may be more
|
|
* useful for some other use-cases.
|
|
*/
|
|
enum class BlockValidationResult {
|
|
BLOCK_RESULT_UNSET = 0, //!< initial value. Block has not yet been rejected
|
|
BLOCK_CONSENSUS, //!< invalid by consensus rules (excluding any below reasons)
|
|
/**
|
|
* Invalid by a change to consensus rules more recent than SegWit.
|
|
* Currently unused as there are no such consensus rule changes, and any download
|
|
* sources realistically need to support SegWit in order to provide useful data,
|
|
* so differentiating between always-invalid and invalid-by-pre-SegWit-soft-fork
|
|
* is uninteresting.
|
|
*/
|
|
BLOCK_RECENT_CONSENSUS_CHANGE,
|
|
BLOCK_CACHED_INVALID, //!< this block was cached as being invalid and we didn't store the reason why
|
|
BLOCK_INVALID_HEADER, //!< invalid proof of work or time too old
|
|
BLOCK_MUTATED, //!< the block's data didn't match the data committed to by the PoW
|
|
BLOCK_MISSING_PREV, //!< We don't have the previous block the checked one is built on
|
|
BLOCK_INVALID_PREV, //!< A block this one builds on is invalid
|
|
BLOCK_TIME_FUTURE, //!< block timestamp was > 2 hours in the future (or our clock is bad)
|
|
BLOCK_CHECKPOINT, //!< the block failed to meet one of our checkpoints
|
|
};
|
|
|
|
|
|
|
|
/** Template for capturing information about block/transaction validation. This is instantiated
|
|
* by TxValidationState and BlockValidationState for validation information on transactions
|
|
* and blocks respectively. */
|
|
template <typename Result>
|
|
class ValidationState
|
|
{
|
|
private:
|
|
enum class ModeState {
|
|
M_VALID, //!< everything ok
|
|
M_INVALID, //!< network rule violation (DoS value may be set)
|
|
M_ERROR, //!< run-time error
|
|
} m_mode{ModeState::M_VALID};
|
|
Result m_result{};
|
|
std::string m_reject_reason;
|
|
std::string m_debug_message;
|
|
|
|
public:
|
|
bool Invalid(Result result,
|
|
const std::string& reject_reason = "",
|
|
const std::string& debug_message = "")
|
|
{
|
|
m_result = result;
|
|
m_reject_reason = reject_reason;
|
|
m_debug_message = debug_message;
|
|
if (m_mode != ModeState::M_ERROR) m_mode = ModeState::M_INVALID;
|
|
return false;
|
|
}
|
|
bool Error(const std::string& reject_reason)
|
|
{
|
|
if (m_mode == ModeState::M_VALID)
|
|
m_reject_reason = reject_reason;
|
|
m_mode = ModeState::M_ERROR;
|
|
return false;
|
|
}
|
|
bool IsValid() const { return m_mode == ModeState::M_VALID; }
|
|
bool IsInvalid() const { return m_mode == ModeState::M_INVALID; }
|
|
bool IsError() const { return m_mode == ModeState::M_ERROR; }
|
|
Result GetResult() const { return m_result; }
|
|
std::string GetRejectReason() const { return m_reject_reason; }
|
|
std::string GetDebugMessage() const { return m_debug_message; }
|
|
std::string ToString() const
|
|
{
|
|
if (IsValid()) {
|
|
return "Valid";
|
|
}
|
|
|
|
if (!m_debug_message.empty()) {
|
|
return m_reject_reason + ", " + m_debug_message;
|
|
}
|
|
|
|
return m_reject_reason;
|
|
}
|
|
};
|
|
|
|
class TxValidationState : public ValidationState<TxValidationResult> {};
|
|
class BlockValidationState : public ValidationState<BlockValidationResult> {};
|
|
|
|
// These implement the weight = (stripped_size * 4) + witness_size formula,
|
|
// using only serialization with and without witness data. As witness_size
|
|
// is equal to total_size - stripped_size, this formula is identical to:
|
|
// weight = (stripped_size * 3) + total_size.
|
|
static inline int64_t GetTransactionWeight(const CTransaction& tx)
|
|
{
|
|
return ::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, PROTOCOL_VERSION);
|
|
}
|
|
static inline int64_t GetBlockWeight(const CBlock& block)
|
|
{
|
|
return ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, PROTOCOL_VERSION);
|
|
}
|
|
static inline int64_t GetTransactionInputWeight(const CTxIn& txin)
|
|
{
|
|
// scriptWitness size is added here because witnesses and txins are split up in segwit serialization.
|
|
return ::GetSerializeSize(txin, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, PROTOCOL_VERSION);
|
|
}
|
|
|
|
#endif // BITCOIN_CONSENSUS_VALIDATION_H
|