[refactor] Add useful-for-dos "reason" field to CValidationState
This is a first step towards cleaning up our DoS interface - make validation return *why* something is invalid, and let net_processing figure out what that implies in terms of banning/disconnection/etc. Behavior change: peers will now be banned for providing blocks with premature coinbase spends. Co-authored-by: Anthony Towns <aj@erisian.com.au> Suhas Daftuar <sdaftuar@gmail.com>
This commit is contained in:
parent
6a7f8777a0
commit
34477ccd39
7 changed files with 173 additions and 78 deletions
|
@ -11,24 +11,24 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
|
||||||
{
|
{
|
||||||
// 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(100, false, REJECT_INVALID, "bad-txns-vin-empty");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vin-empty");
|
||||||
if (tx.vout.empty())
|
if (tx.vout.empty())
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-empty");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, 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)
|
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
|
||||||
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
|
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-oversize");
|
||||||
|
|
||||||
// Check for negative or overflow output values
|
// Check for negative or overflow output values
|
||||||
CAmount nValueOut = 0;
|
CAmount nValueOut = 0;
|
||||||
for (const auto& txout : tx.vout)
|
for (const auto& txout : tx.vout)
|
||||||
{
|
{
|
||||||
if (txout.nValue < 0)
|
if (txout.nValue < 0)
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-negative");
|
||||||
if (txout.nValue > MAX_MONEY)
|
if (txout.nValue > MAX_MONEY)
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-toolarge");
|
||||||
nValueOut += txout.nValue;
|
nValueOut += txout.nValue;
|
||||||
if (!MoneyRange(nValueOut))
|
if (!MoneyRange(nValueOut))
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
|
// Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
|
||||||
|
@ -37,20 +37,20 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
|
||||||
for (const auto& txin : tx.vin)
|
for (const auto& txin : tx.vin)
|
||||||
{
|
{
|
||||||
if (!vInOutPoints.insert(txin.prevout).second)
|
if (!vInOutPoints.insert(txin.prevout).second)
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase())
|
||||||
{
|
{
|
||||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-length");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const auto& txin : tx.vin)
|
for (const auto& txin : tx.vin)
|
||||||
if (txin.prevout.IsNull())
|
if (txin.prevout.IsNull())
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-prevout-null");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-prevout-null");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -160,7 +160,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
|
||||||
{
|
{
|
||||||
// are the actual inputs available?
|
// are the actual inputs available?
|
||||||
if (!inputs.HaveInputs(tx)) {
|
if (!inputs.HaveInputs(tx)) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false,
|
return state.DoS(0, ValidationInvalidReason::TX_MISSING_INPUTS, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false,
|
||||||
strprintf("%s: inputs missing/spent", __func__));
|
strprintf("%s: inputs missing/spent", __func__));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
|
||||||
|
|
||||||
// If prev is coinbase, check that it's matured
|
// If prev is coinbase, check that it's matured
|
||||||
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
|
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
|
||||||
return state.DoS(0, false,
|
return state.DoS(0, ValidationInvalidReason::TX_MISSING_INPUTS, false,
|
||||||
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", false,
|
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", false,
|
||||||
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
||||||
}
|
}
|
||||||
|
@ -180,20 +180,20 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
|
||||||
// Check for negative or overflow input values
|
// Check for negative or overflow input values
|
||||||
nValueIn += coin.out.nValue;
|
nValueIn += coin.out.nValue;
|
||||||
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
|
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CAmount value_out = tx.GetValueOut();
|
const CAmount value_out = tx.GetValueOut();
|
||||||
if (nValueIn < value_out) {
|
if (nValueIn < value_out) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-in-belowout", false,
|
||||||
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tally transaction fees
|
// Tally transaction fees
|
||||||
const CAmount txfee_aux = nValueIn - value_out;
|
const CAmount txfee_aux = nValueIn - value_out;
|
||||||
if (!MoneyRange(txfee_aux)) {
|
if (!MoneyRange(txfee_aux)) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||||
}
|
}
|
||||||
|
|
||||||
txfee = txfee_aux;
|
txfee = txfee_aux;
|
||||||
|
|
|
@ -22,6 +22,50 @@ static const unsigned char REJECT_NONSTANDARD = 0x40;
|
||||||
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
|
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
|
||||||
static const unsigned char REJECT_CHECKPOINT = 0x43;
|
static const unsigned char REJECT_CHECKPOINT = 0x43;
|
||||||
|
|
||||||
|
/** A "reason" why something was invalid, suitable for determining whether the
|
||||||
|
* provider of the object 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 ValidationInvalidReason {
|
||||||
|
// txn and blocks:
|
||||||
|
NONE, //!< not actually invalid
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
RECENT_CONSENSUS_CHANGE,
|
||||||
|
// Only blocks (or headers):
|
||||||
|
CACHED_INVALID, //!< this object was cached as being invalid, but we don't know 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
|
||||||
|
// Only loose txn:
|
||||||
|
TX_NOT_STANDARD, //!< didn't meet our local policy rules
|
||||||
|
TX_MISSING_INPUTS, //!< a transaction was missing some of its inputs (or its inputs were spent at < coinbase maturity height)
|
||||||
|
/**
|
||||||
|
* Transaction might be missing a witness, have a witness prior to SegWit
|
||||||
|
* activation, or witness may have been malleated (which includes
|
||||||
|
* non-standard witnesses).
|
||||||
|
*/
|
||||||
|
TX_WITNESS_MUTATED,
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
* TODO: Currently this is only used if the transaction already exists in the mempool or on chain,
|
||||||
|
* TODO: ATMP's fMissingInputs and a valid CValidationState being used to indicate missing inputs
|
||||||
|
*/
|
||||||
|
TX_CONFLICT,
|
||||||
|
TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/RBF/etc limits
|
||||||
|
};
|
||||||
|
|
||||||
/** Capture information about block/transaction validation */
|
/** Capture information about block/transaction validation */
|
||||||
class CValidationState {
|
class CValidationState {
|
||||||
private:
|
private:
|
||||||
|
@ -30,31 +74,35 @@ private:
|
||||||
MODE_INVALID, //!< network rule violation (DoS value may be set)
|
MODE_INVALID, //!< network rule violation (DoS value may be set)
|
||||||
MODE_ERROR, //!< run-time error
|
MODE_ERROR, //!< run-time error
|
||||||
} mode;
|
} mode;
|
||||||
|
ValidationInvalidReason m_reason;
|
||||||
int nDoS;
|
int nDoS;
|
||||||
std::string strRejectReason;
|
std::string strRejectReason;
|
||||||
unsigned int chRejectCode;
|
unsigned int chRejectCode;
|
||||||
bool corruptionPossible;
|
bool corruptionPossible;
|
||||||
std::string strDebugMessage;
|
std::string strDebugMessage;
|
||||||
public:
|
public:
|
||||||
CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
|
CValidationState() : mode(MODE_VALID), m_reason(ValidationInvalidReason::NONE), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
|
||||||
bool DoS(int level, bool ret = false,
|
bool DoS(int level, ValidationInvalidReason reasonIn, bool ret = false,
|
||||||
unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="",
|
unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="",
|
||||||
bool corruptionIn=false,
|
bool corruptionIn=false,
|
||||||
const std::string &strDebugMessageIn="") {
|
const std::string &strDebugMessageIn="") {
|
||||||
|
m_reason = reasonIn;
|
||||||
chRejectCode = chRejectCodeIn;
|
chRejectCode = chRejectCodeIn;
|
||||||
strRejectReason = strRejectReasonIn;
|
strRejectReason = strRejectReasonIn;
|
||||||
corruptionPossible = corruptionIn;
|
corruptionPossible = corruptionIn;
|
||||||
strDebugMessage = strDebugMessageIn;
|
strDebugMessage = strDebugMessageIn;
|
||||||
|
nDoS += level;
|
||||||
|
assert(nDoS == GetDoSForReason());
|
||||||
|
assert(corruptionPossible == (m_reason == ValidationInvalidReason::BLOCK_MUTATED || m_reason == ValidationInvalidReason::TX_WITNESS_MUTATED));
|
||||||
if (mode == MODE_ERROR)
|
if (mode == MODE_ERROR)
|
||||||
return ret;
|
return ret;
|
||||||
nDoS += level;
|
|
||||||
mode = MODE_INVALID;
|
mode = MODE_INVALID;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
bool Invalid(bool ret = false,
|
bool Invalid(ValidationInvalidReason _reason, bool ret = false,
|
||||||
unsigned int _chRejectCode=0, const std::string &_strRejectReason="",
|
unsigned int _chRejectCode=0, const std::string &_strRejectReason="",
|
||||||
const std::string &_strDebugMessage="") {
|
const std::string &_strDebugMessage="") {
|
||||||
return DoS(0, ret, _chRejectCode, _strRejectReason, false, _strDebugMessage);
|
return DoS(0, _reason, ret, _chRejectCode, _strRejectReason, false, _strDebugMessage);
|
||||||
}
|
}
|
||||||
bool Error(const std::string& strRejectReasonIn) {
|
bool Error(const std::string& strRejectReasonIn) {
|
||||||
if (mode == MODE_VALID)
|
if (mode == MODE_VALID)
|
||||||
|
@ -72,12 +120,39 @@ public:
|
||||||
return mode == MODE_ERROR;
|
return mode == MODE_ERROR;
|
||||||
}
|
}
|
||||||
bool CorruptionPossible() const {
|
bool CorruptionPossible() const {
|
||||||
|
assert(corruptionPossible == (m_reason == ValidationInvalidReason::BLOCK_MUTATED || m_reason == ValidationInvalidReason::TX_WITNESS_MUTATED));
|
||||||
return corruptionPossible;
|
return corruptionPossible;
|
||||||
}
|
}
|
||||||
void SetCorruptionPossible() {
|
void SetCorruptionPossible() {
|
||||||
corruptionPossible = true;
|
corruptionPossible = true;
|
||||||
|
assert(corruptionPossible == (m_reason == ValidationInvalidReason::BLOCK_MUTATED || m_reason == ValidationInvalidReason::TX_WITNESS_MUTATED));
|
||||||
}
|
}
|
||||||
int GetDoS(void) const { return nDoS; }
|
int GetDoS(void) const { return nDoS; }
|
||||||
|
int GetDoSForReason() const {
|
||||||
|
switch (m_reason) {
|
||||||
|
case ValidationInvalidReason::NONE:
|
||||||
|
return 0;
|
||||||
|
case ValidationInvalidReason::CONSENSUS:
|
||||||
|
case ValidationInvalidReason::BLOCK_MUTATED:
|
||||||
|
case ValidationInvalidReason::BLOCK_INVALID_HEADER:
|
||||||
|
case ValidationInvalidReason::BLOCK_CHECKPOINT:
|
||||||
|
case ValidationInvalidReason::BLOCK_INVALID_PREV:
|
||||||
|
return 100;
|
||||||
|
case ValidationInvalidReason::BLOCK_MISSING_PREV:
|
||||||
|
return 10;
|
||||||
|
case ValidationInvalidReason::CACHED_INVALID:
|
||||||
|
case ValidationInvalidReason::RECENT_CONSENSUS_CHANGE:
|
||||||
|
case ValidationInvalidReason::BLOCK_TIME_FUTURE:
|
||||||
|
case ValidationInvalidReason::TX_NOT_STANDARD:
|
||||||
|
case ValidationInvalidReason::TX_MISSING_INPUTS:
|
||||||
|
case ValidationInvalidReason::TX_WITNESS_MUTATED:
|
||||||
|
case ValidationInvalidReason::TX_CONFLICT:
|
||||||
|
case ValidationInvalidReason::TX_MEMPOOL_POLICY:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ValidationInvalidReason GetReason() const { return m_reason; }
|
||||||
unsigned int GetRejectCode() const { return chRejectCode; }
|
unsigned int GetRejectCode() const { return chRejectCode; }
|
||||||
std::string GetRejectReason() const { return strRejectReason; }
|
std::string GetRejectReason() const { return strRejectReason; }
|
||||||
std::string GetDebugMessage() const { return strDebugMessage; }
|
std::string GetDebugMessage() const { return strDebugMessage; }
|
||||||
|
|
|
@ -961,6 +961,7 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
|
||||||
|
|
||||||
static bool TxRelayMayResultInDisconnect(const CValidationState& state)
|
static bool TxRelayMayResultInDisconnect(const CValidationState& state)
|
||||||
{
|
{
|
||||||
|
assert(state.GetDoS() == state.GetDoSForReason());
|
||||||
return (state.GetDoS() > 0);
|
return (state.GetDoS() > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -975,6 +976,7 @@ static bool TxRelayMayResultInDisconnect(const CValidationState& state)
|
||||||
* txs, the peer should not be punished. See BIP 152.
|
* txs, the peer should not be punished. See BIP 152.
|
||||||
*/
|
*/
|
||||||
static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool via_compact_block, const std::string& message = "") {
|
static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool via_compact_block, const std::string& message = "") {
|
||||||
|
assert(state.GetDoS() == state.GetDoSForReason());
|
||||||
int nDoS = state.GetDoS();
|
int nDoS = state.GetDoS();
|
||||||
if (nDoS > 0 && !via_compact_block) {
|
if (nDoS > 0 && !via_compact_block) {
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
|
@ -52,6 +52,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
|
||||||
// Check that the validation state reflects the unsuccessful attempt.
|
// Check that the validation state reflects the unsuccessful attempt.
|
||||||
BOOST_CHECK(state.IsInvalid());
|
BOOST_CHECK(state.IsInvalid());
|
||||||
BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase");
|
BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase");
|
||||||
|
BOOST_CHECK(state.GetReason() == ValidationInvalidReason::CONSENSUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
|
@ -579,28 +579,28 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
|
|
||||||
// Coinbase is only valid in a block, not as a loose transaction
|
// Coinbase is only valid in a block, not as a loose transaction
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase())
|
||||||
return state.DoS(100, false, REJECT_INVALID, "coinbase");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "coinbase");
|
||||||
|
|
||||||
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
||||||
std::string reason;
|
std::string reason;
|
||||||
if (fRequireStandard && !IsStandardTx(tx, reason))
|
if (fRequireStandard && !IsStandardTx(tx, reason))
|
||||||
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
|
return state.DoS(0, ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, reason);
|
||||||
|
|
||||||
// Do not work on transactions that are too small.
|
// Do not work on transactions that are too small.
|
||||||
// A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
|
// A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
|
||||||
// Transactions smaller than this are not relayed to reduce unnecessary malloc overhead.
|
// Transactions smaller than this are not relayed to reduce unnecessary malloc overhead.
|
||||||
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
|
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
|
||||||
return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small");
|
return state.DoS(0, ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "tx-size-small");
|
||||||
|
|
||||||
// Only accept nLockTime-using transactions that can be mined in the next
|
// Only accept nLockTime-using transactions that can be mined in the next
|
||||||
// block; we don't want our mempool filled up with transactions that can't
|
// block; we don't want our mempool filled up with transactions that can't
|
||||||
// be mined yet.
|
// be mined yet.
|
||||||
if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
|
if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
|
||||||
return state.DoS(0, false, REJECT_NONSTANDARD, "non-final");
|
return state.DoS(0, ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "non-final");
|
||||||
|
|
||||||
// is it already in the memory pool?
|
// is it already in the memory pool?
|
||||||
if (pool.exists(hash)) {
|
if (pool.exists(hash)) {
|
||||||
return state.Invalid(false, REJECT_DUPLICATE, "txn-already-in-mempool");
|
return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-in-mempool");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for conflicts with in-memory transactions
|
// Check for conflicts with in-memory transactions
|
||||||
|
@ -636,7 +636,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fReplacementOptOut) {
|
if (fReplacementOptOut) {
|
||||||
return state.Invalid(false, REJECT_DUPLICATE, "txn-mempool-conflict");
|
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_DUPLICATE, "txn-mempool-conflict");
|
||||||
}
|
}
|
||||||
|
|
||||||
setConflicts.insert(ptxConflicting->GetHash());
|
setConflicts.insert(ptxConflicting->GetHash());
|
||||||
|
@ -662,7 +662,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
for (size_t out = 0; out < tx.vout.size(); out++) {
|
for (size_t out = 0; out < tx.vout.size(); out++) {
|
||||||
// Optimistically just do efficient check of cache for outputs
|
// Optimistically just do efficient check of cache for outputs
|
||||||
if (pcoinsTip->HaveCoinInCache(COutPoint(hash, out))) {
|
if (pcoinsTip->HaveCoinInCache(COutPoint(hash, out))) {
|
||||||
return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known");
|
return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise assume this might be an orphan tx for which we just haven't seen parents yet
|
// Otherwise assume this might be an orphan tx for which we just haven't seen parents yet
|
||||||
|
@ -685,7 +685,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
// Must keep pool.cs for this unless we change CheckSequenceLocks to take a
|
// Must keep pool.cs for this unless we change CheckSequenceLocks to take a
|
||||||
// CoinsViewCache instead of create its own
|
// CoinsViewCache instead of create its own
|
||||||
if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
|
if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
|
||||||
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
|
return state.DoS(0, ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "non-BIP68-final");
|
||||||
|
|
||||||
CAmount nFees = 0;
|
CAmount nFees = 0;
|
||||||
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) {
|
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) {
|
||||||
|
@ -694,11 +694,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
|
|
||||||
// Check for non-standard pay-to-script-hash in inputs
|
// Check for non-standard pay-to-script-hash in inputs
|
||||||
if (fRequireStandard && !AreInputsStandard(tx, view))
|
if (fRequireStandard && !AreInputsStandard(tx, view))
|
||||||
return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
|
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
|
||||||
|
|
||||||
// Check for non-standard witness in P2WSH
|
// Check for non-standard witness in P2WSH
|
||||||
if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, view))
|
if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, view))
|
||||||
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true);
|
return state.DoS(0, ValidationInvalidReason::TX_WITNESS_MUTATED, false, REJECT_NONSTANDARD, "bad-witness-nonstandard", true);
|
||||||
|
|
||||||
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
|
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
|
||||||
|
|
||||||
|
@ -722,21 +722,21 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
unsigned int nSize = entry.GetTxSize();
|
unsigned int nSize = entry.GetTxSize();
|
||||||
|
|
||||||
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
|
if (nSigOpsCost > MAX_STANDARD_TX_SIGOPS_COST)
|
||||||
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
|
return state.DoS(0, ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
|
||||||
strprintf("%d", nSigOpsCost));
|
strprintf("%d", nSigOpsCost));
|
||||||
|
|
||||||
CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
|
CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
|
||||||
if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
|
if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
|
||||||
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
|
return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
|
||||||
}
|
}
|
||||||
|
|
||||||
// No transactions are allowed below minRelayTxFee except from disconnected blocks
|
// No transactions are allowed below minRelayTxFee except from disconnected blocks
|
||||||
if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
|
if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
|
||||||
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", false, strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
|
return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", false, strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nAbsurdFee && nFees > nAbsurdFee)
|
if (nAbsurdFee && nFees > nAbsurdFee)
|
||||||
return state.Invalid(false,
|
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false,
|
||||||
REJECT_HIGHFEE, "absurdly-high-fee",
|
REJECT_HIGHFEE, "absurdly-high-fee",
|
||||||
strprintf("%d > %d", nFees, nAbsurdFee));
|
strprintf("%d > %d", nFees, nAbsurdFee));
|
||||||
|
|
||||||
|
@ -748,7 +748,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
|
size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
|
||||||
std::string errString;
|
std::string errString;
|
||||||
if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
|
if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
|
||||||
return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString);
|
return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A transaction that spends outputs that would be replaced by it is invalid. Now
|
// A transaction that spends outputs that would be replaced by it is invalid. Now
|
||||||
|
@ -760,7 +760,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
|
const uint256 &hashAncestor = ancestorIt->GetTx().GetHash();
|
||||||
if (setConflicts.count(hashAncestor))
|
if (setConflicts.count(hashAncestor))
|
||||||
{
|
{
|
||||||
return state.DoS(100, false,
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false,
|
||||||
REJECT_INVALID, "bad-txns-spends-conflicting-tx", false,
|
REJECT_INVALID, "bad-txns-spends-conflicting-tx", false,
|
||||||
strprintf("%s spends conflicting transaction %s",
|
strprintf("%s spends conflicting transaction %s",
|
||||||
hash.ToString(),
|
hash.ToString(),
|
||||||
|
@ -803,7 +803,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
|
CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
|
||||||
if (newFeeRate <= oldFeeRate)
|
if (newFeeRate <= oldFeeRate)
|
||||||
{
|
{
|
||||||
return state.DoS(0, false,
|
return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false,
|
||||||
REJECT_INSUFFICIENTFEE, "insufficient fee", false,
|
REJECT_INSUFFICIENTFEE, "insufficient fee", false,
|
||||||
strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
|
strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
|
||||||
hash.ToString(),
|
hash.ToString(),
|
||||||
|
@ -832,7 +832,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
nConflictingSize += it->GetTxSize();
|
nConflictingSize += it->GetTxSize();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return state.DoS(0, false,
|
return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false,
|
||||||
REJECT_NONSTANDARD, "too many potential replacements", false,
|
REJECT_NONSTANDARD, "too many potential replacements", false,
|
||||||
strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
|
strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
|
||||||
hash.ToString(),
|
hash.ToString(),
|
||||||
|
@ -852,7 +852,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
// it's cheaper to just check if the new input refers to a
|
// it's cheaper to just check if the new input refers to a
|
||||||
// tx that's in the mempool.
|
// tx that's in the mempool.
|
||||||
if (pool.exists(tx.vin[j].prevout.hash)) {
|
if (pool.exists(tx.vin[j].prevout.hash)) {
|
||||||
return state.DoS(0, false,
|
return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false,
|
||||||
REJECT_NONSTANDARD, "replacement-adds-unconfirmed", false,
|
REJECT_NONSTANDARD, "replacement-adds-unconfirmed", false,
|
||||||
strprintf("replacement %s adds unconfirmed input, idx %d",
|
strprintf("replacement %s adds unconfirmed input, idx %d",
|
||||||
hash.ToString(), j));
|
hash.ToString(), j));
|
||||||
|
@ -865,7 +865,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
// transactions would not be paid for.
|
// transactions would not be paid for.
|
||||||
if (nModifiedFees < nConflictingFees)
|
if (nModifiedFees < nConflictingFees)
|
||||||
{
|
{
|
||||||
return state.DoS(0, false,
|
return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false,
|
||||||
REJECT_INSUFFICIENTFEE, "insufficient fee", false,
|
REJECT_INSUFFICIENTFEE, "insufficient fee", false,
|
||||||
strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
|
strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
|
||||||
hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees)));
|
hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees)));
|
||||||
|
@ -876,7 +876,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
CAmount nDeltaFees = nModifiedFees - nConflictingFees;
|
CAmount nDeltaFees = nModifiedFees - nConflictingFees;
|
||||||
if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize))
|
if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize))
|
||||||
{
|
{
|
||||||
return state.DoS(0, false,
|
return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false,
|
||||||
REJECT_INSUFFICIENTFEE, "insufficient fee", false,
|
REJECT_INSUFFICIENTFEE, "insufficient fee", false,
|
||||||
strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
|
strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
|
||||||
hash.ToString(),
|
hash.ToString(),
|
||||||
|
@ -898,7 +898,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
|
if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
|
||||||
!CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
|
!CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
|
||||||
// Only the witness is missing, so the transaction itself may be fine.
|
// Only the witness is missing, so the transaction itself may be fine.
|
||||||
state.SetCorruptionPossible();
|
state.DoS(0, ValidationInvalidReason::TX_WITNESS_MUTATED, false,
|
||||||
|
state.GetRejectCode(), state.GetRejectReason(), true, state.GetDebugMessage());
|
||||||
}
|
}
|
||||||
return false; // state filled in by CheckInputs
|
return false; // state filled in by CheckInputs
|
||||||
}
|
}
|
||||||
|
@ -956,7 +957,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
if (!bypass_limits) {
|
if (!bypass_limits) {
|
||||||
LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
|
LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
|
||||||
if (!pool.exists(hash))
|
if (!pool.exists(hash))
|
||||||
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full");
|
return state.DoS(0, ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1357,6 +1358,9 @@ void InitScriptExecutionCache() {
|
||||||
* which are matched. This is useful for checking blocks where we will likely never need the cache
|
* which are matched. This is useful for checking blocks where we will likely never need the cache
|
||||||
* entry again.
|
* entry again.
|
||||||
*
|
*
|
||||||
|
* Note that we may set state.reason to NOT_STANDARD for extra soft-fork flags in flags, block-checking
|
||||||
|
* callers should probably reset it to CONSENSUS in such cases.
|
||||||
|
*
|
||||||
* Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
|
* Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
|
||||||
*/
|
*/
|
||||||
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
|
@ -1418,7 +1422,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
||||||
CScriptCheck check2(coin.out, tx, i,
|
CScriptCheck check2(coin.out, tx, i,
|
||||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
|
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
|
||||||
if (check2())
|
if (check2())
|
||||||
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
|
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
|
||||||
}
|
}
|
||||||
// Failures of other flags indicate a transaction that is
|
// Failures of other flags indicate a transaction that is
|
||||||
// invalid in new blocks, e.g. an invalid P2SH. We DoS ban
|
// invalid in new blocks, e.g. an invalid P2SH. We DoS ban
|
||||||
|
@ -1427,7 +1431,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
||||||
// as to the correct behavior - we may want to continue
|
// as to the correct behavior - we may want to continue
|
||||||
// peering with non-upgraded nodes even after soft-fork
|
// peering with non-upgraded nodes even after soft-fork
|
||||||
// super-majority signaling has occurred.
|
// super-majority signaling has occurred.
|
||||||
return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1922,7 +1926,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||||
for (const auto& tx : block.vtx) {
|
for (const auto& tx : block.vtx) {
|
||||||
for (size_t o = 0; o < tx->vout.size(); o++) {
|
for (size_t o = 0; o < tx->vout.size(); o++) {
|
||||||
if (view.HaveCoin(COutPoint(tx->GetHash(), o))) {
|
if (view.HaveCoin(COutPoint(tx->GetHash(), o))) {
|
||||||
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): tried to overwrite transaction"),
|
||||||
REJECT_INVALID, "bad-txns-BIP30");
|
REJECT_INVALID, "bad-txns-BIP30");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1962,11 +1966,17 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||||
{
|
{
|
||||||
CAmount txfee = 0;
|
CAmount txfee = 0;
|
||||||
if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) {
|
if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) {
|
||||||
|
if (state.GetReason() == ValidationInvalidReason::TX_MISSING_INPUTS) {
|
||||||
|
// CheckTxInputs may return MISSING_INPUTS but we can't return that, as
|
||||||
|
// it's not defined for a block, so we reset the reason flag to CONSENSUS here.
|
||||||
|
state.DoS(100, ValidationInvalidReason::CONSENSUS, false,
|
||||||
|
state.GetRejectCode(), state.GetRejectReason(), state.CorruptionPossible(), state.GetDebugMessage());
|
||||||
|
}
|
||||||
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
|
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
|
||||||
}
|
}
|
||||||
nFees += txfee;
|
nFees += txfee;
|
||||||
if (!MoneyRange(nFees)) {
|
if (!MoneyRange(nFees)) {
|
||||||
return state.DoS(100, error("%s: accumulated fee in the block out of range.", __func__),
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, error("%s: accumulated fee in the block out of range.", __func__),
|
||||||
REJECT_INVALID, "bad-txns-accumulated-fee-outofrange");
|
REJECT_INVALID, "bad-txns-accumulated-fee-outofrange");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1979,7 +1989,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
|
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
|
||||||
return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__),
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, error("%s: contains a non-BIP68-final transaction", __func__),
|
||||||
REJECT_INVALID, "bad-txns-nonfinal");
|
REJECT_INVALID, "bad-txns-nonfinal");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1990,7 +2000,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||||
// * witness (when witness enabled in flags and excludes coinbase)
|
// * witness (when witness enabled in flags and excludes coinbase)
|
||||||
nSigOpsCost += GetTransactionSigOpCost(tx, view, flags);
|
nSigOpsCost += GetTransactionSigOpCost(tx, view, flags);
|
||||||
if (nSigOpsCost > MAX_BLOCK_SIGOPS_COST)
|
if (nSigOpsCost > MAX_BLOCK_SIGOPS_COST)
|
||||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): too many sigops"),
|
||||||
REJECT_INVALID, "bad-blk-sigops");
|
REJECT_INVALID, "bad-blk-sigops");
|
||||||
|
|
||||||
txdata.emplace_back(tx);
|
txdata.emplace_back(tx);
|
||||||
|
@ -1999,9 +2009,16 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||||
std::vector<CScriptCheck> vChecks;
|
std::vector<CScriptCheck> vChecks;
|
||||||
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
||||||
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) {
|
if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) {
|
||||||
// With parallel script checks, we always set DoS to 100; do
|
if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) {
|
||||||
// that here as well for simplicity (for now).
|
// CheckInputs may return NOT_STANDARD for extra flags we passed,
|
||||||
state.DoS(100, false, state.GetRejectCode(), state.GetRejectReason(), state.CorruptionPossible(), state.GetDebugMessage());
|
// but we can't return that, as it's not defined for a block, so
|
||||||
|
// we reset the reason flag to CONSENSUS here.
|
||||||
|
// In the event of a future soft-fork, we may need to
|
||||||
|
// consider whether rewriting to CONSENSUS or
|
||||||
|
// RECENT_CONSENSUS_CHANGE would be more appropriate.
|
||||||
|
state.DoS(100, ValidationInvalidReason::CONSENSUS, false,
|
||||||
|
state.GetRejectCode(), state.GetRejectReason(), state.CorruptionPossible(), state.GetDebugMessage());
|
||||||
|
}
|
||||||
return error("ConnectBlock(): CheckInputs on %s failed with %s",
|
return error("ConnectBlock(): CheckInputs on %s failed with %s",
|
||||||
tx.GetHash().ToString(), FormatStateMessage(state));
|
tx.GetHash().ToString(), FormatStateMessage(state));
|
||||||
}
|
}
|
||||||
|
@ -2019,13 +2036,13 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||||
|
|
||||||
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
|
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
|
||||||
if (block.vtx[0]->GetValueOut() > blockReward)
|
if (block.vtx[0]->GetValueOut() > blockReward)
|
||||||
return state.DoS(100,
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS,
|
||||||
error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)",
|
error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)",
|
||||||
block.vtx[0]->GetValueOut(), blockReward),
|
block.vtx[0]->GetValueOut(), blockReward),
|
||||||
REJECT_INVALID, "bad-cb-amount");
|
REJECT_INVALID, "bad-cb-amount");
|
||||||
|
|
||||||
if (!control.Wait())
|
if (!control.Wait())
|
||||||
return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed");
|
||||||
int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2;
|
int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2;
|
||||||
LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, MILLI * (nTime4 - nTime2), nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal);
|
LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, MILLI * (nTime4 - nTime2), nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal);
|
||||||
|
|
||||||
|
@ -3051,7 +3068,7 @@ static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state,
|
||||||
{
|
{
|
||||||
// Check proof of work matches claimed amount
|
// Check proof of work matches claimed amount
|
||||||
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
|
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
|
||||||
return state.DoS(100, false, REJECT_INVALID, "high-hash", false, "proof of work failed");
|
return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_INVALID, "high-hash", false, "proof of work failed");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3073,13 +3090,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
|
||||||
bool mutated;
|
bool mutated;
|
||||||
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
|
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
|
||||||
if (block.hashMerkleRoot != hashMerkleRoot2)
|
if (block.hashMerkleRoot != hashMerkleRoot2)
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", true, "hashMerkleRoot mismatch");
|
return state.DoS(100, ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-txnmrklroot", true, "hashMerkleRoot mismatch");
|
||||||
|
|
||||||
// Check for merkle tree malleability (CVE-2012-2459): repeating sequences
|
// Check for merkle tree malleability (CVE-2012-2459): repeating sequences
|
||||||
// of transactions in a block without affecting the merkle root of a block,
|
// of transactions in a block without affecting the merkle root of a block,
|
||||||
// while still invalidating it.
|
// while still invalidating it.
|
||||||
if (mutated)
|
if (mutated)
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction");
|
return state.DoS(100, ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
// All potential-corruption validation must be done before we do any
|
// All potential-corruption validation must be done before we do any
|
||||||
|
@ -3090,19 +3107,19 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
|
||||||
|
|
||||||
// Size limits
|
// Size limits
|
||||||
if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
|
if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
|
||||||
|
|
||||||
// First transaction must be coinbase, the rest must not be
|
// First transaction must be coinbase, the rest must not be
|
||||||
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase())
|
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase())
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase");
|
||||||
for (unsigned int i = 1; i < block.vtx.size(); i++)
|
for (unsigned int i = 1; i < block.vtx.size(); i++)
|
||||||
if (block.vtx[i]->IsCoinBase())
|
if (block.vtx[i]->IsCoinBase())
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
|
||||||
|
|
||||||
// Check transactions
|
// Check transactions
|
||||||
for (const auto& tx : block.vtx)
|
for (const auto& tx : block.vtx)
|
||||||
if (!CheckTransaction(*tx, state, true))
|
if (!CheckTransaction(*tx, state, true))
|
||||||
return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(),
|
return state.Invalid(state.GetReason(), false, state.GetRejectCode(), state.GetRejectReason(),
|
||||||
strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage()));
|
strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage()));
|
||||||
|
|
||||||
unsigned int nSigOps = 0;
|
unsigned int nSigOps = 0;
|
||||||
|
@ -3111,7 +3128,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
|
||||||
nSigOps += GetLegacySigOpCount(*tx);
|
nSigOps += GetLegacySigOpCount(*tx);
|
||||||
}
|
}
|
||||||
if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST)
|
if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST)
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");
|
||||||
|
|
||||||
if (fCheckPOW && fCheckMerkleRoot)
|
if (fCheckPOW && fCheckMerkleRoot)
|
||||||
block.fChecked = true;
|
block.fChecked = true;
|
||||||
|
@ -3204,7 +3221,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
|
||||||
// Check proof of work
|
// Check proof of work
|
||||||
const Consensus::Params& consensusParams = params.GetConsensus();
|
const Consensus::Params& consensusParams = params.GetConsensus();
|
||||||
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
|
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work");
|
return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work");
|
||||||
|
|
||||||
// Check against checkpoints
|
// Check against checkpoints
|
||||||
if (fCheckpointsEnabled) {
|
if (fCheckpointsEnabled) {
|
||||||
|
@ -3213,23 +3230,23 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
|
||||||
// MapBlockIndex.
|
// MapBlockIndex.
|
||||||
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(params.Checkpoints());
|
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(params.Checkpoints());
|
||||||
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
|
if (pcheckpoint && nHeight < pcheckpoint->nHeight)
|
||||||
return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
|
return state.DoS(100, ValidationInvalidReason::BLOCK_CHECKPOINT, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check timestamp against prev
|
// Check timestamp against prev
|
||||||
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
|
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
|
||||||
return state.DoS(100, false, REJECT_INVALID, "time-too-old", false, "block's timestamp is too early");
|
return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_INVALID, "time-too-old", false, "block's timestamp is too early");
|
||||||
|
|
||||||
// Check timestamp
|
// Check timestamp
|
||||||
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
|
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
|
||||||
return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future");
|
return state.Invalid(ValidationInvalidReason::BLOCK_TIME_FUTURE, false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future");
|
||||||
|
|
||||||
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
|
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
|
||||||
// check for version 2, 3 and 4 upgrades
|
// check for version 2, 3 and 4 upgrades
|
||||||
if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) ||
|
if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) ||
|
||||||
(block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) ||
|
(block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) ||
|
||||||
(block.nVersion < 4 && nHeight >= consensusParams.BIP65Height))
|
(block.nVersion < 4 && nHeight >= consensusParams.BIP65Height))
|
||||||
return state.DoS(100, false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion), false,
|
return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion), false,
|
||||||
strprintf("rejected nVersion=0x%08x block", block.nVersion));
|
strprintf("rejected nVersion=0x%08x block", block.nVersion));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -3259,7 +3276,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
||||||
// Check that all transactions are finalized
|
// Check that all transactions are finalized
|
||||||
for (const auto& tx : block.vtx) {
|
for (const auto& tx : block.vtx) {
|
||||||
if (!IsFinalTx(*tx, nHeight, nLockTimeCutoff)) {
|
if (!IsFinalTx(*tx, nHeight, nLockTimeCutoff)) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3269,7 +3286,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
||||||
CScript expect = CScript() << nHeight;
|
CScript expect = CScript() << nHeight;
|
||||||
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
|
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
|
||||||
!std::equal(expect.begin(), expect.end(), block.vtx[0]->vin[0].scriptSig.begin())) {
|
!std::equal(expect.begin(), expect.end(), block.vtx[0]->vin[0].scriptSig.begin())) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase");
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3291,11 +3308,11 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
||||||
// already does not permit it, it is impossible to trigger in the
|
// already does not permit it, it is impossible to trigger in the
|
||||||
// witness tree.
|
// witness tree.
|
||||||
if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
|
if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness reserved value size", __func__));
|
return state.DoS(100, ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness reserved value size", __func__));
|
||||||
}
|
}
|
||||||
CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->vin[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
|
CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->vin[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
|
||||||
if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
|
if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__));
|
return state.DoS(100, ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__));
|
||||||
}
|
}
|
||||||
fHaveWitness = true;
|
fHaveWitness = true;
|
||||||
}
|
}
|
||||||
|
@ -3305,7 +3322,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
||||||
if (!fHaveWitness) {
|
if (!fHaveWitness) {
|
||||||
for (const auto& tx : block.vtx) {
|
for (const auto& tx : block.vtx) {
|
||||||
if (tx->HasWitness()) {
|
if (tx->HasWitness()) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__));
|
return state.DoS(100, ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3317,7 +3334,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
||||||
// the block hash, so we couldn't mark the block as permanently
|
// the block hash, so we couldn't mark the block as permanently
|
||||||
// failed).
|
// failed).
|
||||||
if (GetBlockWeight(block) > MAX_BLOCK_WEIGHT) {
|
if (GetBlockWeight(block) > MAX_BLOCK_WEIGHT) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-weight", false, strprintf("%s : weight limit failed", __func__));
|
return state.DoS(100, ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-weight", false, strprintf("%s : weight limit failed", __func__));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -3337,7 +3354,7 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
|
||||||
if (ppindex)
|
if (ppindex)
|
||||||
*ppindex = pindex;
|
*ppindex = pindex;
|
||||||
if (pindex->nStatus & BLOCK_FAILED_MASK)
|
if (pindex->nStatus & BLOCK_FAILED_MASK)
|
||||||
return state.Invalid(error("%s: block %s is marked invalid", __func__, hash.ToString()), 0, "duplicate");
|
return state.Invalid(ValidationInvalidReason::CACHED_INVALID, error("%s: block %s is marked invalid", __func__, hash.ToString()), 0, "duplicate");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3348,10 +3365,10 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
|
||||||
CBlockIndex* pindexPrev = nullptr;
|
CBlockIndex* pindexPrev = nullptr;
|
||||||
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
|
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
|
||||||
if (mi == mapBlockIndex.end())
|
if (mi == mapBlockIndex.end())
|
||||||
return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
|
return state.DoS(10, ValidationInvalidReason::BLOCK_MISSING_PREV, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
|
||||||
pindexPrev = (*mi).second;
|
pindexPrev = (*mi).second;
|
||||||
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
|
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
|
||||||
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
|
return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
|
||||||
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
|
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
|
||||||
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
|
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
|
||||||
|
|
||||||
|
@ -3388,7 +3405,7 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
|
||||||
setDirtyBlockIndex.insert(invalid_walk);
|
setDirtyBlockIndex.insert(invalid_walk);
|
||||||
invalid_walk = invalid_walk->pprev;
|
invalid_walk = invalid_walk->pprev;
|
||||||
}
|
}
|
||||||
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
|
return state.DoS(100, ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,7 +295,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||||
self.log.info("Reject a block spending an immature coinbase.")
|
self.log.info("Reject a block spending an immature coinbase.")
|
||||||
self.move_tip(15)
|
self.move_tip(15)
|
||||||
b20 = self.next_block(20, spend=out[7])
|
b20 = self.next_block(20, spend=out[7])
|
||||||
self.send_blocks([b20], success=False, reject_reason='bad-txns-premature-spend-of-coinbase')
|
self.send_blocks([b20], success=False, reject_reason='bad-txns-premature-spend-of-coinbase', reconnect=True)
|
||||||
|
|
||||||
# Attempt to spend a coinbase at depth too low (on a fork this time)
|
# Attempt to spend a coinbase at depth too low (on a fork this time)
|
||||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
|
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
|
||||||
|
@ -308,7 +308,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||||
self.send_blocks([b21], False)
|
self.send_blocks([b21], False)
|
||||||
|
|
||||||
b22 = self.next_block(22, spend=out[5])
|
b22 = self.next_block(22, spend=out[5])
|
||||||
self.send_blocks([b22], success=False, reject_reason='bad-txns-premature-spend-of-coinbase')
|
self.send_blocks([b22], success=False, reject_reason='bad-txns-premature-spend-of-coinbase', reconnect=True)
|
||||||
|
|
||||||
# Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected
|
# Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected
|
||||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
|
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
|
||||||
|
|
Loading…
Reference in a new issue