mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +01:00
merge bitcoin#15141: Rewrite DoS interface between validation and net_processing
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
parent
ef3f738f6f
commit
f14bf83a9d
@ -204,7 +204,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
|
||||
// but that is expensive, and CheckBlock caches a block's
|
||||
// "checked-status" (in the CBlock?). CBlock should be able to
|
||||
// check its own merkle root and cache that check.
|
||||
if (state.CorruptionPossible())
|
||||
if (state.GetReason() == ValidationInvalidReason::BLOCK_MUTATED)
|
||||
return READ_STATUS_FAILED; // Possible Short ID collision
|
||||
return READ_STATUS_CHECKBLOCK_FAILED;
|
||||
}
|
||||
|
@ -19,25 +19,25 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
|
||||
|
||||
// Basic checks that don't depend on any context
|
||||
if (!allowEmptyTxInOut && tx.vin.empty())
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vin-empty");
|
||||
if (!allowEmptyTxInOut && tx.vout.empty())
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-empty");
|
||||
// Size limits
|
||||
if (::GetSerializeSize(tx, PROTOCOL_VERSION) > MAX_LEGACY_BLOCK_SIZE)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-oversize");
|
||||
if (tx.vExtraPayload.size() > MAX_TX_EXTRA_PAYLOAD)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-payload-oversize");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-payload-oversize");
|
||||
|
||||
// Check for negative or overflow output values (see CVE-2010-5139)
|
||||
CAmount nValueOut = 0;
|
||||
for (const auto& txout : tx.vout) {
|
||||
if (txout.nValue < 0)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-negative");
|
||||
if (txout.nValue > MAX_MONEY)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-toolarge");
|
||||
nValueOut += txout.nValue;
|
||||
if (!MoneyRange(nValueOut))
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
}
|
||||
|
||||
// Check for duplicate inputs (see CVE-2018-17144)
|
||||
@ -48,7 +48,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
|
||||
std::set<COutPoint> vInOutPoints;
|
||||
for (const auto& txin : tx.vin) {
|
||||
if (!vInOutPoints.insert(txin.prevout).second)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
|
||||
}
|
||||
|
||||
if (tx.IsCoinBase()) {
|
||||
@ -58,11 +58,11 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state)
|
||||
minCbSize = 1;
|
||||
}
|
||||
if (tx.vin[0].scriptSig.size() < minCbSize || tx.vin[0].scriptSig.size() > 100)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, 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 state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-prevout-null");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -162,8 +162,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
|
||||
{
|
||||
// are the actual inputs available?
|
||||
if (!inputs.HaveInputs(tx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false,
|
||||
strprintf("%s: inputs missing/spent", __func__));
|
||||
return state.Invalid(ValidationInvalidReason::TX_MISSING_INPUTS, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", strprintf("%s: inputs missing/spent", __func__));
|
||||
}
|
||||
|
||||
CAmount nValueIn = 0;
|
||||
@ -174,28 +173,25 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
|
||||
|
||||
// If prev is coinbase, check that it's matured
|
||||
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
|
||||
return state.Invalid(false,
|
||||
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
|
||||
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
||||
return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
||||
}
|
||||
|
||||
// Check for negative or overflow input values
|
||||
nValueIn += coin.out.nValue;
|
||||
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
||||
}
|
||||
}
|
||||
|
||||
const CAmount value_out = tx.GetValueOut();
|
||||
if (nValueIn < value_out) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
|
||||
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-in-belowout", strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
||||
}
|
||||
|
||||
// Tally transaction fees
|
||||
const CAmount txfee_aux = nValueIn - value_out;
|
||||
if (!MoneyRange(txfee_aux)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||
}
|
||||
|
||||
txfee = txfee_aux;
|
||||
|
@ -18,6 +18,72 @@ static const unsigned char REJECT_NONSTANDARD = 0x40;
|
||||
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
|
||||
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 some major deployment.
|
||||
*/
|
||||
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
|
||||
BLOCK_CHAINLOCK, //!< the block conflicts with the ChainLock
|
||||
// Only loose txn:
|
||||
TX_NOT_STANDARD, //!< didn't meet our local policy rules
|
||||
TX_MISSING_INPUTS, //!< a transaction was missing some of its inputs
|
||||
TX_PREMATURE_SPEND, //!< transaction spends a coinbase too early, or violates locktime/sequence locks
|
||||
TX_BAD_SPECIAL, //!< special transaction violates some rules that are not enough for insta-ban
|
||||
/**
|
||||
* Tx already in mempool or conflicts with a tx in the chain
|
||||
* 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_CONFLICT_LOCK, //!< conflicts with InstantSend lock
|
||||
TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/etc limits
|
||||
};
|
||||
|
||||
inline bool IsTransactionReason(ValidationInvalidReason r)
|
||||
{
|
||||
return r == ValidationInvalidReason::NONE ||
|
||||
r == ValidationInvalidReason::CONSENSUS ||
|
||||
r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE ||
|
||||
r == ValidationInvalidReason::TX_NOT_STANDARD ||
|
||||
r == ValidationInvalidReason::TX_PREMATURE_SPEND ||
|
||||
r == ValidationInvalidReason::TX_MISSING_INPUTS ||
|
||||
r == ValidationInvalidReason::TX_BAD_SPECIAL ||
|
||||
r == ValidationInvalidReason::TX_CONFLICT ||
|
||||
r == ValidationInvalidReason::TX_CONFLICT_LOCK ||
|
||||
r == ValidationInvalidReason::TX_MEMPOOL_POLICY;
|
||||
}
|
||||
|
||||
inline bool IsBlockReason(ValidationInvalidReason r)
|
||||
{
|
||||
return r == ValidationInvalidReason::NONE ||
|
||||
r == ValidationInvalidReason::CONSENSUS ||
|
||||
r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE ||
|
||||
r == ValidationInvalidReason::CACHED_INVALID ||
|
||||
r == ValidationInvalidReason::BLOCK_INVALID_HEADER ||
|
||||
r == ValidationInvalidReason::BLOCK_MUTATED ||
|
||||
r == ValidationInvalidReason::BLOCK_MISSING_PREV ||
|
||||
r == ValidationInvalidReason::BLOCK_INVALID_PREV ||
|
||||
r == ValidationInvalidReason::BLOCK_TIME_FUTURE ||
|
||||
r == ValidationInvalidReason::BLOCK_CHECKPOINT ||
|
||||
r == ValidationInvalidReason::BLOCK_CHAINLOCK;
|
||||
}
|
||||
|
||||
/** Capture information about block/transaction validation */
|
||||
class CValidationState {
|
||||
private:
|
||||
@ -26,32 +92,24 @@ private:
|
||||
MODE_INVALID, //!< network rule violation (DoS value may be set)
|
||||
MODE_ERROR, //!< run-time error
|
||||
} mode;
|
||||
int nDoS;
|
||||
ValidationInvalidReason m_reason;
|
||||
std::string strRejectReason;
|
||||
unsigned int chRejectCode;
|
||||
bool corruptionPossible;
|
||||
std::string strDebugMessage;
|
||||
public:
|
||||
CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
|
||||
bool DoS(int level, bool ret = false,
|
||||
unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="",
|
||||
bool corruptionIn=false,
|
||||
const std::string &strDebugMessageIn="") {
|
||||
CValidationState() : mode(MODE_VALID), m_reason(ValidationInvalidReason::NONE), chRejectCode(0) {}
|
||||
bool Invalid(ValidationInvalidReason reasonIn, bool ret = false,
|
||||
unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="",
|
||||
const std::string &strDebugMessageIn="") {
|
||||
m_reason = reasonIn;
|
||||
chRejectCode = chRejectCodeIn;
|
||||
strRejectReason = strRejectReasonIn;
|
||||
corruptionPossible = corruptionIn;
|
||||
strDebugMessage = strDebugMessageIn;
|
||||
if (mode == MODE_ERROR)
|
||||
return ret;
|
||||
nDoS += level;
|
||||
mode = MODE_INVALID;
|
||||
return ret;
|
||||
}
|
||||
bool Invalid(bool ret = false,
|
||||
unsigned int _chRejectCode=0, const std::string &_strRejectReason="",
|
||||
const std::string &_strDebugMessage="") {
|
||||
return DoS(0, ret, _chRejectCode, _strRejectReason, false, _strDebugMessage);
|
||||
}
|
||||
bool Error(const std::string& strRejectReasonIn) {
|
||||
if (mode == MODE_VALID)
|
||||
strRejectReason = strRejectReasonIn;
|
||||
@ -67,16 +125,7 @@ public:
|
||||
bool IsError() const {
|
||||
return mode == MODE_ERROR;
|
||||
}
|
||||
bool IsInvalid(int &nDoSOut) const {
|
||||
if (IsInvalid()) {
|
||||
nDoSOut = nDoS;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool CorruptionPossible() const {
|
||||
return corruptionPossible;
|
||||
}
|
||||
ValidationInvalidReason GetReason() const { return m_reason; }
|
||||
unsigned int GetRejectCode() const { return chRejectCode; }
|
||||
std::string GetRejectReason() const { return strRejectReason; }
|
||||
std::string GetDebugMessage() const { return strDebugMessage; }
|
||||
|
@ -17,30 +17,30 @@
|
||||
bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state)
|
||||
{
|
||||
if (tx.nType != TRANSACTION_COINBASE) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cbtx-type");
|
||||
}
|
||||
|
||||
if (!tx.IsCoinBase()) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-invalid");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cbtx-invalid");
|
||||
}
|
||||
|
||||
CCbTx cbTx;
|
||||
if (!GetTxPayload(tx, cbTx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cbtx-payload");
|
||||
}
|
||||
|
||||
if (cbTx.nVersion == 0 || cbTx.nVersion > CCbTx::CURRENT_VERSION) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-version");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cbtx-version");
|
||||
}
|
||||
|
||||
if (pindexPrev && pindexPrev->nHeight + 1 != cbTx.nHeight) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-height");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cbtx-height");
|
||||
}
|
||||
|
||||
if (pindexPrev) {
|
||||
bool fDIP0008Active = pindexPrev->nHeight >= Params().GetConsensus().DIP0008Height;
|
||||
if (fDIP0008Active && cbTx.nVersion < 2) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-version");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cbtx-version");
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CValid
|
||||
|
||||
CCbTx cbTx;
|
||||
if (!GetTxPayload(*block.vtx[0], cbTx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cbtx-payload");
|
||||
}
|
||||
|
||||
int64_t nTime2 = GetTimeMicros(); nTimePayload += nTime2 - nTime1;
|
||||
@ -76,7 +76,7 @@ bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CValid
|
||||
return false;
|
||||
}
|
||||
if (calculatedMerkleRoot != cbTx.merkleRootMNList) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-mnmerkleroot");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cbtx-mnmerkleroot");
|
||||
}
|
||||
|
||||
int64_t nTime3 = GetTimeMicros(); nTimeMerkleMNL += nTime3 - nTime2;
|
||||
@ -88,7 +88,7 @@ bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, CValid
|
||||
return false;
|
||||
}
|
||||
if (calculatedMerkleRoot != cbTx.merkleRootQuorums) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-quorummerkleroot");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cbtx-quorummerkleroot");
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev
|
||||
if (sml.mnList == smlCached.mnList) {
|
||||
merkleRootRet = merkleRootCached;
|
||||
if (mutatedCached) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "mutated-cached-calc-cb-mnmerkleroot");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "mutated-cached-calc-cb-mnmerkleroot");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -148,13 +148,13 @@ bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev
|
||||
mutatedCached = mutated;
|
||||
|
||||
if (mutated) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "mutated-calc-cb-mnmerkleroot");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "mutated-calc-cb-mnmerkleroot");
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("%s -- failed: %s\n", __func__, e.what());
|
||||
return state.DoS(100, false, REJECT_INVALID, "failed-calc-cb-mnmerkleroot");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "failed-calc-cb-mnmerkleroot");
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +182,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre
|
||||
for (const auto& p2 : p.second) {
|
||||
uint256 minedBlockHash;
|
||||
llmq::CFinalCommitmentPtr qc = llmq::quorumBlockProcessor->GetMinedCommitment(p.first, p2->GetBlockHash(), minedBlockHash);
|
||||
if (qc == nullptr) return state.DoS(100, false, REJECT_INVALID, "commitment-not-found");
|
||||
if (qc == nullptr) return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "commitment-not-found");
|
||||
if (llmq::CLLMQUtils::IsQuorumRotationEnabled(qc->llmqType, pindexPrev)) {
|
||||
auto& qi = qcIndexedHashes[p.first];
|
||||
qi.insert(std::make_pair(qc->quorumIndex, ::SerializeHash(*qc)));
|
||||
@ -205,7 +205,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre
|
||||
if (tx->nVersion == 3 && tx->nType == TRANSACTION_QUORUM_COMMITMENT) {
|
||||
llmq::CFinalCommitmentTxPayload qc;
|
||||
if (!GetTxPayload(*tx, qc)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-payload-calc-cbtx-quorummerkleroot");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-payload-calc-cbtx-quorummerkleroot");
|
||||
}
|
||||
if (qc.commitment.IsNull()) {
|
||||
continue;
|
||||
@ -227,7 +227,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre
|
||||
v.emplace_back(qcHash);
|
||||
hashCount++;
|
||||
if (v.size() > uint64_t(llmq_params.signingActiveQuorumCount)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "excess-quorums-calc-cbtx-quorummerkleroot");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "excess-quorums-calc-cbtx-quorummerkleroot");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -261,7 +261,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre
|
||||
LogPrint(BCLog::BENCHMARK, " - ComputeMerkleRoot: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeMerkle * 0.000001);
|
||||
|
||||
if (mutated) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "mutated-calc-cbtx-quorummerkleroot");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "mutated-calc-cbtx-quorummerkleroot");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -575,7 +575,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde
|
||||
mnListDiffsCache.emplace(pindex->GetBlockHash(), diff);
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("CDeterministicMNManager::%s -- internal error: %s\n", __func__, e.what());
|
||||
return _state.DoS(100, false, REJECT_INVALID, "failed-dmn-block");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "failed-dmn-block");
|
||||
}
|
||||
|
||||
// Don't hold cs while calling signals
|
||||
@ -588,7 +588,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde
|
||||
if (!consensusParams.DIP0003EnforcementHash.IsNull() && consensusParams.DIP0003EnforcementHash != pindex->GetBlockHash()) {
|
||||
LogPrintf("CDeterministicMNManager::%s -- DIP3 enforcement block has wrong hash: hash=%s, expected=%s, nHeight=%d\n", __func__,
|
||||
pindex->GetBlockHash().ToString(), consensusParams.DIP0003EnforcementHash.ToString(), nHeight);
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-dip3-enf-block");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-dip3-enf-block");
|
||||
}
|
||||
LogPrintf("CDeterministicMNManager::%s -- DIP3 is enforced now. nHeight=%d\n", __func__, nHeight);
|
||||
}
|
||||
@ -685,7 +685,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
|
||||
CProRegTx proTx;
|
||||
if (!GetTxPayload(tx, proTx)) {
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload");
|
||||
}
|
||||
|
||||
auto dmn = std::make_shared<CDeterministicMN>(newList.GetTotalRegisteredCount());
|
||||
@ -702,7 +702,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != 1000 * COIN)) {
|
||||
// should actually never get to this point as CheckProRegTx should have handled this case.
|
||||
// We do this additional check nevertheless to be 100% sure
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-collateral");
|
||||
}
|
||||
|
||||
auto replacedDmn = newList.GetMNByCollateral(dmn->collateralOutpoint);
|
||||
@ -718,10 +718,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
}
|
||||
|
||||
if (newList.HasUniqueProperty(proTx.addr)) {
|
||||
return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
||||
}
|
||||
if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) {
|
||||
return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-key");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_DUPLICATE, "bad-protx-dup-key");
|
||||
}
|
||||
|
||||
dmn->nOperatorReward = proTx.nOperatorReward;
|
||||
@ -743,16 +743,16 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
|
||||
CProUpServTx proTx;
|
||||
if (!GetTxPayload(tx, proTx)) {
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload");
|
||||
}
|
||||
|
||||
if (newList.HasUniqueProperty(proTx.addr) && newList.GetUniquePropertyMN(proTx.addr)->proTxHash != proTx.proTxHash) {
|
||||
return _state.DoS(100, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
||||
}
|
||||
|
||||
CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
|
||||
if (!dmn) {
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash");
|
||||
}
|
||||
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
|
||||
newState->addr = proTx.addr;
|
||||
@ -777,12 +777,12 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
|
||||
CProUpRegTx proTx;
|
||||
if (!GetTxPayload(tx, proTx)) {
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload");
|
||||
}
|
||||
|
||||
CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
|
||||
if (!dmn) {
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash");
|
||||
}
|
||||
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
|
||||
if (newState->pubKeyOperator.Get() != proTx.pubKeyOperator) {
|
||||
@ -803,12 +803,12 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) {
|
||||
CProUpRevTx proTx;
|
||||
if (!GetTxPayload(tx, proTx)) {
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload");
|
||||
}
|
||||
|
||||
CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
|
||||
if (!dmn) {
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash");
|
||||
}
|
||||
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
|
||||
newState->ResetOperatorFields();
|
||||
@ -824,7 +824,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
} else if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) {
|
||||
llmq::CFinalCommitmentTxPayload qc;
|
||||
if (!GetTxPayload(tx, qc)) {
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-qc-payload");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-payload");
|
||||
}
|
||||
if (!qc.commitment.IsNull()) {
|
||||
const auto& llmq_params = llmq::GetLLMQParams(qc.commitment.llmqType);
|
||||
@ -833,7 +833,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
|
||||
auto pQuorumBaseBlockIndex = pindexPrev->GetAncestor(quorumHeight);
|
||||
if (!pQuorumBaseBlockIndex || pQuorumBaseBlockIndex->GetBlockHash() != qc.commitment.quorumHash) {
|
||||
// we should actually never get into this case as validation should have caught it...but let's be sure
|
||||
return _state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash");
|
||||
return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-quorum-hash");
|
||||
}
|
||||
|
||||
HandleQuorumCommitment(qc.commitment, pQuorumBaseBlockIndex, newList, debugLogs);
|
||||
@ -1187,23 +1187,23 @@ template <typename ProTx>
|
||||
static bool CheckService(const ProTx& proTx, CValidationState& state)
|
||||
{
|
||||
if (!proTx.addr.IsValid()) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-ipaddr");
|
||||
}
|
||||
if (Params().RequireRoutableExternalIP() && !proTx.addr.IsRoutable()) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-ipaddr");
|
||||
}
|
||||
|
||||
static int mainnetDefaultPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPort();
|
||||
if (Params().NetworkIDString() == CBaseChainParams::MAIN) {
|
||||
if (proTx.addr.GetPort() != mainnetDefaultPort) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr-port");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-ipaddr-port");
|
||||
}
|
||||
} else if (proTx.addr.GetPort() == mainnetDefaultPort) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr-port");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-ipaddr-port");
|
||||
}
|
||||
|
||||
if (!proTx.addr.IsIPv4()) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-ipaddr");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-ipaddr");
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1214,7 +1214,7 @@ static bool CheckHashSig(const ProTx& proTx, const CKeyID& keyID, CValidationSta
|
||||
{
|
||||
std::string strError;
|
||||
if (!CHashSigner::VerifyHash(::SerializeHash(proTx), keyID, proTx.vchSig, strError)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError);
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-sig");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1224,7 +1224,7 @@ static bool CheckStringSig(const ProTx& proTx, const CKeyID& keyID, CValidationS
|
||||
{
|
||||
std::string strError;
|
||||
if (!CMessageSigner::VerifyMessage(keyID, proTx.vchSig, proTx.MakeSignString(), strError)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError);
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-sig");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1233,7 +1233,7 @@ template <typename ProTx>
|
||||
static bool CheckHashSig(const ProTx& proTx, const CBLSPublicKey& pubKey, CValidationState& state)
|
||||
{
|
||||
if (!proTx.sig.VerifyInsecure(pubKey, ::SerializeHash(proTx))) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false);
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-sig");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1241,16 +1241,16 @@ static bool CheckHashSig(const ProTx& proTx, const CBLSPublicKey& pubKey, CValid
|
||||
bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs)
|
||||
{
|
||||
if (tx.nType != TRANSACTION_PROVIDER_REGISTER) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type");
|
||||
}
|
||||
|
||||
CProRegTx ptx;
|
||||
if (!GetTxPayload(tx, ptx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload");
|
||||
}
|
||||
|
||||
if (auto maybe_err = ptx.IsTriviallyValid(); maybe_err.did_err) {
|
||||
return state.DoS(maybe_err.ban_amount, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
return state.Invalid(maybe_err.reason, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
}
|
||||
|
||||
// It's allowed to set addr to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be issues later
|
||||
@ -1267,31 +1267,31 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid
|
||||
if (!ptx.collateralOutpoint.hash.IsNull()) {
|
||||
Coin coin;
|
||||
if (!view.GetCoin(ptx.collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != 1000 * COIN) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral");
|
||||
}
|
||||
|
||||
if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-dest");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral-dest");
|
||||
}
|
||||
|
||||
// Extract key from collateral. This only works for P2PK and P2PKH collaterals and will fail for P2SH.
|
||||
// Issuer of this ProRegTx must prove ownership with this key by signing the ProRegTx
|
||||
keyForPayloadSig = boost::get<CKeyID>(&collateralTxDest);
|
||||
if (!keyForPayloadSig) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-pkh");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral-pkh");
|
||||
}
|
||||
|
||||
collateralOutpoint = ptx.collateralOutpoint;
|
||||
} else {
|
||||
if (ptx.collateralOutpoint.n >= tx.vout.size()) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-index");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral-index");
|
||||
}
|
||||
if (tx.vout[ptx.collateralOutpoint.n].nValue != 1000 * COIN) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral");
|
||||
}
|
||||
|
||||
if (!ExtractDestination(tx.vout[ptx.collateralOutpoint.n].scriptPubKey, collateralTxDest)) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-dest");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral-dest");
|
||||
}
|
||||
|
||||
collateralOutpoint = COutPoint(tx.GetHash(), ptx.collateralOutpoint.n);
|
||||
@ -1300,7 +1300,7 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid
|
||||
// don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
|
||||
// this check applies to internal and external collateral, but internal collaterals are not necessarily a P2PKH
|
||||
if (collateralTxDest == CTxDestination(ptx.keyIDOwner) || collateralTxDest == CTxDestination(ptx.keyIDVoting)) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral-reuse");
|
||||
}
|
||||
|
||||
if (pindexPrev) {
|
||||
@ -1308,23 +1308,23 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid
|
||||
|
||||
// only allow reusing of addresses when it's for the same collateral (which replaces the old MN)
|
||||
if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->collateralOutpoint != collateralOutpoint) {
|
||||
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
||||
}
|
||||
|
||||
// never allow duplicate keys, even if this ProTx would replace an existing MN
|
||||
if (mnList.HasUniqueProperty(ptx.keyIDOwner) || mnList.HasUniqueProperty(ptx.pubKeyOperator)) {
|
||||
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_DUPLICATE, "bad-protx-dup-key");
|
||||
}
|
||||
|
||||
if (!deterministicMNManager->IsDIP3Enforced(pindexPrev->nHeight)) {
|
||||
if (ptx.keyIDOwner != ptx.keyIDVoting) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-key-not-same");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto maybe_err = CheckInputsHash(tx, ptx); maybe_err.did_err) {
|
||||
return state.DoS(maybe_err.ban_amount, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
return state.Invalid(maybe_err.reason, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
}
|
||||
|
||||
if (keyForPayloadSig) {
|
||||
@ -1336,7 +1336,7 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid
|
||||
} else {
|
||||
// collateral is part of this ProRegTx, so we know the collateral is owned by the issuer
|
||||
if (!ptx.vchSig.empty()) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-sig");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1346,16 +1346,16 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid
|
||||
bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, bool check_sigs)
|
||||
{
|
||||
if (tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type");
|
||||
}
|
||||
|
||||
CProUpServTx ptx;
|
||||
if (!GetTxPayload(tx, ptx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload");
|
||||
}
|
||||
|
||||
if (auto maybe_err = ptx.IsTriviallyValid(); maybe_err.did_err) {
|
||||
return state.DoS(maybe_err.ban_amount, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
return state.Invalid(maybe_err.reason, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
}
|
||||
|
||||
if (!CheckService(ptx, state)) {
|
||||
@ -1367,27 +1367,27 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa
|
||||
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
|
||||
auto mn = mnList.GetMN(ptx.proTxHash);
|
||||
if (!mn) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash");
|
||||
}
|
||||
|
||||
// don't allow updating to addresses already used by other MNs
|
||||
if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->proTxHash != ptx.proTxHash) {
|
||||
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_DUPLICATE, "bad-protx-dup-addr");
|
||||
}
|
||||
|
||||
if (ptx.scriptOperatorPayout != CScript()) {
|
||||
if (mn->nOperatorReward == 0) {
|
||||
// don't allow setting operator reward payee in case no operatorReward was set
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-operator-payee");
|
||||
}
|
||||
if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash()) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-operator-payee");
|
||||
}
|
||||
}
|
||||
|
||||
// we can only check the signature if pindexPrev != nullptr and the MN is known
|
||||
if (auto maybe_err = CheckInputsHash(tx, ptx); maybe_err.did_err) {
|
||||
return state.DoS(maybe_err.ban_amount, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
return state.Invalid(maybe_err.reason, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
}
|
||||
if (check_sigs && !CheckHashSig(ptx, mn->pdmnState->pubKeyOperator.Get(), state)) {
|
||||
// pass the state returned by the function above
|
||||
@ -1401,66 +1401,66 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa
|
||||
bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs)
|
||||
{
|
||||
if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type");
|
||||
}
|
||||
|
||||
CProUpRegTx ptx;
|
||||
if (!GetTxPayload(tx, ptx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload");
|
||||
}
|
||||
|
||||
if (auto maybe_err = ptx.IsTriviallyValid(); maybe_err.did_err) {
|
||||
return state.DoS(maybe_err.ban_amount, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
return state.Invalid(maybe_err.reason, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
}
|
||||
|
||||
CTxDestination payoutDest;
|
||||
if (!ExtractDestination(ptx.scriptPayout, payoutDest)) {
|
||||
// should not happen as we checked script types before
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-payee-dest");
|
||||
}
|
||||
|
||||
if (pindexPrev) {
|
||||
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
|
||||
auto dmn = mnList.GetMN(ptx.proTxHash);
|
||||
if (!dmn) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash");
|
||||
}
|
||||
|
||||
// don't allow reuse of payee key for other keys (don't allow people to put the payee key onto an online server)
|
||||
if (payoutDest == CTxDestination(dmn->pdmnState->keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDVoting)) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-payee-reuse");
|
||||
}
|
||||
|
||||
Coin coin;
|
||||
if (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent()) {
|
||||
// this should never happen (there would be no dmn otherwise)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-collateral");
|
||||
}
|
||||
|
||||
// don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
|
||||
CTxDestination collateralTxDest;
|
||||
if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral-dest");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-collateral-dest");
|
||||
}
|
||||
if (collateralTxDest == CTxDestination(dmn->pdmnState->keyIDOwner) || collateralTxDest == CTxDestination(ptx.keyIDVoting)) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral-reuse");
|
||||
}
|
||||
|
||||
if (mnList.HasUniqueProperty(ptx.pubKeyOperator)) {
|
||||
auto otherDmn = mnList.GetUniquePropertyMN(ptx.pubKeyOperator);
|
||||
if (ptx.proTxHash != otherDmn->proTxHash) {
|
||||
return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_DUPLICATE, "bad-protx-dup-key");
|
||||
}
|
||||
}
|
||||
|
||||
if (!deterministicMNManager->IsDIP3Enforced(pindexPrev->nHeight)) {
|
||||
if (dmn->pdmnState->keyIDOwner != ptx.keyIDVoting) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-key-not-same");
|
||||
}
|
||||
}
|
||||
|
||||
if (auto maybe_err = CheckInputsHash(tx, ptx); maybe_err.did_err) {
|
||||
return state.DoS(maybe_err.ban_amount, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
return state.Invalid(maybe_err.reason, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
}
|
||||
if (check_sigs && !CheckHashSig(ptx, dmn->pdmnState->keyIDOwner, state)) {
|
||||
// pass the state returned by the function above
|
||||
@ -1474,26 +1474,26 @@ bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVal
|
||||
bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, bool check_sigs)
|
||||
{
|
||||
if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type");
|
||||
}
|
||||
|
||||
CProUpRevTx ptx;
|
||||
if (!GetTxPayload(tx, ptx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload");
|
||||
}
|
||||
|
||||
if (auto maybe_err = ptx.IsTriviallyValid(); maybe_err.did_err) {
|
||||
return state.DoS(maybe_err.ban_amount, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
return state.Invalid(maybe_err.reason, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
}
|
||||
|
||||
if (pindexPrev) {
|
||||
auto mnList = deterministicMNManager->GetListForBlock(pindexPrev);
|
||||
auto dmn = mnList.GetMN(ptx.proTxHash);
|
||||
if (!dmn)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash");
|
||||
|
||||
if (auto maybe_err = CheckInputsHash(tx, ptx); maybe_err.did_err) {
|
||||
return state.DoS(maybe_err.ban_amount, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
return state.Invalid(maybe_err.reason, false, REJECT_INVALID, std::string(maybe_err.error_str));
|
||||
}
|
||||
if (check_sigs && !CheckHashSig(ptx, dmn->pdmnState->pubKeyOperator.Get(), state)) {
|
||||
// pass the state returned by the function above
|
||||
|
@ -34,29 +34,29 @@ bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidat
|
||||
{
|
||||
MNHFTxPayload mnhfTx;
|
||||
if (!GetTxPayload(tx, mnhfTx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-payload");
|
||||
}
|
||||
|
||||
if (mnhfTx.nVersion == 0 || mnhfTx.nVersion > MNHFTxPayload::CURRENT_VERSION) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-version");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-version");
|
||||
}
|
||||
|
||||
const CBlockIndex* pindexQuorum = LookupBlockIndex(mnhfTx.signal.quorumHash);
|
||||
if (!pindexQuorum) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-quorum-hash");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-quorum-hash");
|
||||
}
|
||||
|
||||
if (pindexQuorum != pindexPrev->GetAncestor(pindexQuorum->nHeight)) {
|
||||
// not part of active chain
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-quorum-hash");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-quorum-hash");
|
||||
}
|
||||
|
||||
if (!Params().HasLLMQ(Params().GetConsensus().llmqTypeMnhf)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-type");
|
||||
}
|
||||
|
||||
if (!mnhfTx.signal.Verify(pindexQuorum)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-invalid");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-invalid");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -12,34 +12,34 @@
|
||||
maybe_error CProRegTx::IsTriviallyValid() const
|
||||
{
|
||||
if (nVersion == 0 || nVersion > CProRegTx::CURRENT_VERSION) {
|
||||
return {100, "bad-protx-version"};
|
||||
return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"};
|
||||
}
|
||||
if (nType != 0) {
|
||||
return {100, "bad-protx-type"};
|
||||
return {ValidationInvalidReason::CONSENSUS, "bad-protx-type"};
|
||||
}
|
||||
if (nMode != 0) {
|
||||
return {100, "bad-protx-mode"};
|
||||
return {ValidationInvalidReason::CONSENSUS, "bad-protx-mode"};
|
||||
}
|
||||
|
||||
if (keyIDOwner.IsNull() || !pubKeyOperator.IsValid() || keyIDVoting.IsNull()) {
|
||||
return {10, "bad-protx-key-null"};
|
||||
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-key-null"};
|
||||
}
|
||||
if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
|
||||
return {10, "bad-protx-payee"};
|
||||
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee"};
|
||||
}
|
||||
|
||||
CTxDestination payoutDest;
|
||||
if (!ExtractDestination(scriptPayout, payoutDest)) {
|
||||
// should not happen as we checked script types before
|
||||
return {10, "bad-protx-payee-dest"};
|
||||
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee-dest"};
|
||||
}
|
||||
// don't allow reuse of payout key for other keys (don't allow people to put the payee key onto an online server)
|
||||
if (payoutDest == CTxDestination(keyIDOwner) || payoutDest == CTxDestination(keyIDVoting)) {
|
||||
return {10, "bad-protx-payee-reuse"};
|
||||
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee-reuse"};
|
||||
}
|
||||
|
||||
if (nOperatorReward > 10000) {
|
||||
return {10, "bad-protx-operator-reward"};
|
||||
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-operator-reward"};
|
||||
}
|
||||
|
||||
return {};
|
||||
@ -85,7 +85,7 @@ std::string CProRegTx::ToString() const
|
||||
maybe_error CProUpServTx::IsTriviallyValid() const
|
||||
{
|
||||
if (nVersion == 0 || nVersion > CProUpServTx::CURRENT_VERSION) {
|
||||
return {100, "bad-protx-version"};
|
||||
return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"};
|
||||
}
|
||||
|
||||
return {};
|
||||
@ -106,17 +106,17 @@ std::string CProUpServTx::ToString() const
|
||||
maybe_error CProUpRegTx::IsTriviallyValid() const
|
||||
{
|
||||
if (nVersion == 0 || nVersion > CProUpRegTx::CURRENT_VERSION) {
|
||||
return {100, "bad-protx-version"};
|
||||
return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"};
|
||||
}
|
||||
if (nMode != 0) {
|
||||
return {100, "bad-protx-mode"};
|
||||
return {ValidationInvalidReason::CONSENSUS, "bad-protx-mode"};
|
||||
}
|
||||
|
||||
if (!pubKeyOperator.IsValid() || keyIDVoting.IsNull()) {
|
||||
return {10, "bad-protx-key-null"};
|
||||
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-key-null"};
|
||||
}
|
||||
if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) {
|
||||
return {10, "bad-protx-payee"};
|
||||
return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee"};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@ -136,13 +136,13 @@ std::string CProUpRegTx::ToString() const
|
||||
maybe_error CProUpRevTx::IsTriviallyValid() const
|
||||
{
|
||||
if (nVersion == 0 || nVersion > CProUpRevTx::CURRENT_VERSION) {
|
||||
return {100, "bad-protx-version"};
|
||||
return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"};
|
||||
}
|
||||
|
||||
// nReason < CProUpRevTx::REASON_NOT_SPECIFIED is always `false` since
|
||||
// nReason is unsigned and CProUpRevTx::REASON_NOT_SPECIFIED == 0
|
||||
if (nReason > CProUpRevTx::REASON_LAST) {
|
||||
return {100, "bad-protx-reason"};
|
||||
return {ValidationInvalidReason::CONSENSUS, "bad-protx-reason"};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <evo/specialtx.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
||||
#include <consensus/validation.h>
|
||||
#include <key_io.h>
|
||||
#include <netaddress.h>
|
||||
#include <pubkey.h>
|
||||
@ -20,11 +21,11 @@ class CValidationState;
|
||||
|
||||
struct maybe_error{
|
||||
bool did_err{false};
|
||||
int ban_amount{0};
|
||||
ValidationInvalidReason reason{ValidationInvalidReason::CONSENSUS};
|
||||
std::string_view error_str;
|
||||
|
||||
constexpr maybe_error() = default;
|
||||
constexpr maybe_error(int amount, std::string_view err): did_err(true), ban_amount(amount), error_str(err) {};
|
||||
constexpr maybe_error(ValidationInvalidReason reasonIn, std::string_view err): did_err(true), reason(reasonIn), error_str(err) {};
|
||||
};
|
||||
|
||||
class CProRegTx
|
||||
@ -235,7 +236,7 @@ template <typename ProTx>
|
||||
static maybe_error CheckInputsHash(const CTransaction& tx, const ProTx& proTx)
|
||||
{
|
||||
if (uint256 inputsHash = CalcTxInputsHash(tx); inputsHash != proTx.inputsHash) {
|
||||
return {100, "bad-protx-inputs-hash"};
|
||||
return {ValidationInvalidReason::CONSENSUS, "bad-protx-inputs-hash"};
|
||||
}
|
||||
|
||||
return {};
|
||||
|
@ -24,7 +24,7 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
|
||||
return true;
|
||||
|
||||
if (pindexPrev && pindexPrev->nHeight + 1 < Params().GetConsensus().DIP0003Height) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-tx-type");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-tx-type");
|
||||
}
|
||||
|
||||
try {
|
||||
@ -46,10 +46,10 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("%s -- failed: %s\n", __func__, e.what());
|
||||
return state.DoS(100, false, REJECT_INVALID, "failed-check-special-tx");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "failed-check-special-tx");
|
||||
}
|
||||
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-tx-type-check");
|
||||
return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-tx-type-check");
|
||||
}
|
||||
|
||||
bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValidationState& state)
|
||||
@ -72,7 +72,7 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida
|
||||
return true; // handled per block
|
||||
}
|
||||
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-tx-type-proc");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-tx-type-proc");
|
||||
}
|
||||
|
||||
bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex)
|
||||
@ -153,7 +153,7 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CV
|
||||
LogPrint(BCLog::BENCHMARK, " - CheckCbTxMerkleRoots: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeMerkle * 0.000001);
|
||||
} catch (const std::exception& e) {
|
||||
LogPrintf("%s -- failed: %s\n", __func__, e.what());
|
||||
return state.DoS(100, false, REJECT_INVALID, "failed-procspectxsinblock");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "failed-procspectxsinblock");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -153,11 +153,11 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex*
|
||||
const auto numCommitmentsInNewBlock = qcs.count(params.type);
|
||||
|
||||
if (!isCommitmentRequired && numCommitmentsInNewBlock > 0) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-not-allowed");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-not-allowed");
|
||||
}
|
||||
|
||||
if (isCommitmentRequired && numCommitmentsInNewBlock == 0) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-missing");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-missing");
|
||||
}
|
||||
if (llmq::CLLMQUtils::IsQuorumRotationEnabled(params.type, pindex)) {
|
||||
LogPrintf("[ProcessBlock] h[%d] isCommitmentRequired[%d] numCommitmentsInNewBlock[%d]\n", pindex->nHeight, isCommitmentRequired, numCommitmentsInNewBlock);
|
||||
@ -210,31 +210,31 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH
|
||||
if (quorumHash.IsNull()) {
|
||||
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s quorumHash is null.\n", __func__,
|
||||
nHeight, uint8_t(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), qc.quorumPublicKey.ToString());
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-block");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-block");
|
||||
}
|
||||
if (quorumHash != qc.quorumHash) {
|
||||
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, qc.quorumHash=%s signers=%s, validMembers=%d, quorumPublicKey=%s non equal quorumHash.\n", __func__,
|
||||
nHeight, uint8_t(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), qc.quorumPublicKey.ToString());
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-block");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-block");
|
||||
}
|
||||
|
||||
if (qc.IsNull()) {
|
||||
if (!qc.VerifyNull()) {
|
||||
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%dqc verifynull failed.\n", __func__,
|
||||
nHeight, uint8_t(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers());
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid-null");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-invalid-null");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasMinedCommitment(llmq_params.type, quorumHash)) {
|
||||
// should not happen as it's already handled in ProcessBlock
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-dup");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-dup");
|
||||
}
|
||||
|
||||
if (!IsMiningPhase(llmq_params, nHeight)) {
|
||||
// should not happen as it's already handled in ProcessBlock
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-height");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-height");
|
||||
}
|
||||
|
||||
const auto* pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash);
|
||||
@ -242,7 +242,7 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH
|
||||
if (!qc.Verify(pQuorumBaseBlockIndex, fBLSChecks)) {
|
||||
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s qc verify failed.\n", __func__,
|
||||
nHeight, uint8_t(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), qc.quorumPublicKey.ToString());
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-invalid");
|
||||
}
|
||||
|
||||
if (fJustCheck) {
|
||||
@ -385,13 +385,13 @@ bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const C
|
||||
if (!GetTxPayload(*tx, qc)) {
|
||||
// should not happen as it was verified before processing the block
|
||||
LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d GetTxPayload fails\n", __func__, pindex->nHeight);
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-payload");
|
||||
}
|
||||
|
||||
// only allow one commitment per type and per block (This was changed with rotation)
|
||||
if (!CLLMQUtils::IsQuorumRotationEnabled(qc.commitment.llmqType, pindex)) {
|
||||
if (ret.count(qc.commitment.llmqType) != 0) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-dup");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-dup");
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const C
|
||||
}
|
||||
|
||||
if (pindex->nHeight < consensus.DIP0003Height && !ret.empty()) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-premature");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-premature");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -165,7 +165,7 @@ bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev,
|
||||
CFinalCommitmentTxPayload qcTx;
|
||||
if (!GetTxPayload(tx, qcTx)) {
|
||||
LogPrintfFinalCommitment("h[%d] GetTxPayload failed\n", pindexPrev->nHeight);
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-payload");
|
||||
}
|
||||
const auto& llmq_params = GetLLMQParams(qcTx.commitment.llmqType);
|
||||
std::stringstream ss;
|
||||
@ -176,40 +176,40 @@ bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev,
|
||||
|
||||
if (qcTx.nVersion == 0 || qcTx.nVersion > CFinalCommitmentTxPayload::CURRENT_VERSION) {
|
||||
LogPrintfFinalCommitment("h[%d] invalid qcTx.nVersion[%d]\n", pindexPrev->nHeight, qcTx.nVersion);
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-version");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-version");
|
||||
}
|
||||
|
||||
if (qcTx.nHeight != uint32_t(pindexPrev->nHeight + 1)) {
|
||||
LogPrintfFinalCommitment("h[%d] invalid qcTx.nHeight[%d]\n", pindexPrev->nHeight, qcTx.nHeight);
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-height");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-height");
|
||||
}
|
||||
|
||||
const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(qcTx.commitment.quorumHash));
|
||||
if (!pQuorumBaseBlockIndex) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-quorum-hash");
|
||||
}
|
||||
|
||||
|
||||
if (pQuorumBaseBlockIndex != pindexPrev->GetAncestor(pQuorumBaseBlockIndex->nHeight)) {
|
||||
// not part of active chain
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-quorum-hash");
|
||||
}
|
||||
|
||||
if (!Params().HasLLMQ(qcTx.commitment.llmqType)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-type");
|
||||
}
|
||||
|
||||
if (qcTx.commitment.IsNull()) {
|
||||
if (!qcTx.commitment.VerifyNull()) {
|
||||
LogPrintfFinalCommitment("h[%d] invalid qcTx.commitment[%s] VerifyNull failed\n", pindexPrev->nHeight, qcTx.commitment.quorumHash.ToString());
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid-null");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-invalid-null");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!qcTx.commitment.Verify(pQuorumBaseBlockIndex, false)) {
|
||||
LogPrintfFinalCommitment("h[%d] invalid qcTx.commitment[%s] Verify failed\n", pindexPrev->nHeight, qcTx.commitment.quorumHash.ToString());
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-invalid");
|
||||
}
|
||||
|
||||
LogPrintfFinalCommitment("h[%d] CheckLLMQCommitment VALID\n", pindexPrev->nHeight);
|
||||
|
@ -402,7 +402,16 @@ struct CNodeState {
|
||||
|
||||
ObjectDownloadState m_object_download;
|
||||
|
||||
CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) {
|
||||
//! Whether this peer is an inbound connection
|
||||
bool m_is_inbound;
|
||||
|
||||
//! Whether this peer is a manual connection
|
||||
bool m_is_manual_connection;
|
||||
|
||||
CNodeState(CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
|
||||
address(addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
|
||||
m_is_manual_connection (is_manual)
|
||||
{
|
||||
fCurrentlyConnected = false;
|
||||
nMisbehavior = 0;
|
||||
fShouldBan = false;
|
||||
@ -899,7 +908,7 @@ void PeerLogicValidation::InitializeNode(CNode *pnode) {
|
||||
NodeId nodeid = pnode->GetId();
|
||||
{
|
||||
LOCK(cs_main);
|
||||
mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName)));
|
||||
mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->fInbound, pnode->m_manual_connection));
|
||||
}
|
||||
if(!pnode->fInbound)
|
||||
PushNodeVersion(pnode, connman, GetTime());
|
||||
@ -1148,6 +1157,94 @@ bool IsBanned(NodeId pnode)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given validation state result may result in a peer
|
||||
* banning/disconnecting us. We use this to determine which unaccepted
|
||||
* transactions from a whitelisted peer that we can safely relay.
|
||||
*/
|
||||
static bool TxRelayMayResultInDisconnect(const CValidationState& state)
|
||||
{
|
||||
assert(IsTransactionReason(state.GetReason()));
|
||||
return state.GetReason() == ValidationInvalidReason::CONSENSUS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Potentially ban a node based on the contents of a CValidationState object
|
||||
*
|
||||
* @param[in] via_compact_block: this bool is passed in because net_processing should
|
||||
* punish peers differently depending on whether the data was provided in a compact
|
||||
* block message or not. If the compact block had a valid header, but contained invalid
|
||||
* txs, the peer should not be punished. See BIP 152.
|
||||
*
|
||||
* @return Returns true if the peer was punished (probably disconnected)
|
||||
*
|
||||
* Changes here may need to be reflected in TxRelayMayResultInDisconnect().
|
||||
*/
|
||||
static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool via_compact_block, const std::string& message = "") {
|
||||
switch (state.GetReason()) {
|
||||
case ValidationInvalidReason::NONE:
|
||||
break;
|
||||
// The node is providing invalid data:
|
||||
case ValidationInvalidReason::CONSENSUS:
|
||||
case ValidationInvalidReason::BLOCK_MUTATED:
|
||||
if (!via_compact_block) {
|
||||
LOCK(cs_main);
|
||||
Misbehaving(nodeid, 100, message);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ValidationInvalidReason::CACHED_INVALID:
|
||||
{
|
||||
LOCK(cs_main);
|
||||
CNodeState *node_state = State(nodeid);
|
||||
if (node_state == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Ban outbound (but not inbound) peers if on an invalid chain.
|
||||
// Exempt HB compact block peers and manual connections.
|
||||
if (!via_compact_block && !node_state->m_is_inbound && !node_state->m_is_manual_connection) {
|
||||
Misbehaving(nodeid, 100, message);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ValidationInvalidReason::BLOCK_INVALID_HEADER:
|
||||
case ValidationInvalidReason::BLOCK_CHECKPOINT:
|
||||
case ValidationInvalidReason::BLOCK_INVALID_PREV:
|
||||
{
|
||||
LOCK(cs_main);
|
||||
Misbehaving(nodeid, 100, message);
|
||||
}
|
||||
return true;
|
||||
// Conflicting (but not necessarily invalid) data or different policy:
|
||||
case ValidationInvalidReason::BLOCK_MISSING_PREV:
|
||||
case ValidationInvalidReason::BLOCK_CHAINLOCK:
|
||||
case ValidationInvalidReason::TX_BAD_SPECIAL:
|
||||
case ValidationInvalidReason::TX_CONFLICT_LOCK:
|
||||
{
|
||||
// TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
|
||||
LOCK(cs_main);
|
||||
Misbehaving(nodeid, 10, message);
|
||||
}
|
||||
return true;
|
||||
case ValidationInvalidReason::RECENT_CONSENSUS_CHANGE:
|
||||
case ValidationInvalidReason::BLOCK_TIME_FUTURE:
|
||||
case ValidationInvalidReason::TX_NOT_STANDARD:
|
||||
case ValidationInvalidReason::TX_MISSING_INPUTS:
|
||||
case ValidationInvalidReason::TX_PREMATURE_SPEND:
|
||||
case ValidationInvalidReason::TX_CONFLICT:
|
||||
case ValidationInvalidReason::TX_MEMPOOL_POLICY:
|
||||
break;
|
||||
}
|
||||
if (message != "") {
|
||||
LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1337,14 +1434,12 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
|
||||
const uint256 hash(block.GetHash());
|
||||
std::map<uint256, std::pair<NodeId, bool> >::iterator it = mapBlockSource.find(hash);
|
||||
|
||||
int nDoS = 0;
|
||||
if (state.IsInvalid(nDoS)) {
|
||||
if (state.IsInvalid()) {
|
||||
// Don't send reject message with code 0 or an internal reject code.
|
||||
if (it != mapBlockSource.end() && State(it->second.first) && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) {
|
||||
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
|
||||
State(it->second.first)->rejects.push_back(reject);
|
||||
if (nDoS > 0 && it->second.second)
|
||||
Misbehaving(it->second.first, nDoS);
|
||||
MaybePunishNode(/*nodeid=*/ it->second.first, state, /*via_compact_block=*/ !it->second.second);
|
||||
}
|
||||
}
|
||||
// Check that:
|
||||
@ -1878,7 +1973,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
|
||||
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCKTXN, resp));
|
||||
}
|
||||
|
||||
bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool punish_duplicate_invalid)
|
||||
bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, ChainstateManager& chainman, CTxMemPool& mempool, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
|
||||
{
|
||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
|
||||
size_t nCount = headers.size();
|
||||
@ -1942,48 +2037,8 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, ChainstateMan
|
||||
CValidationState state;
|
||||
CBlockHeader first_invalid_header;
|
||||
if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
|
||||
int nDoS;
|
||||
if (state.IsInvalid(nDoS)) {
|
||||
LOCK(cs_main);
|
||||
if (nDoS > 0) {
|
||||
Misbehaving(pfrom->GetId(), nDoS, "invalid header received");
|
||||
} else {
|
||||
LogPrint(BCLog::NET, "peer=%d: invalid header received\n", pfrom->GetId());
|
||||
}
|
||||
if (punish_duplicate_invalid && LookupBlockIndex(first_invalid_header.GetHash())) {
|
||||
// Goal: don't allow outbound peers to use up our outbound
|
||||
// connection slots if they are on incompatible chains.
|
||||
//
|
||||
// We ask the caller to set punish_invalid appropriately based
|
||||
// on the peer and the method of header delivery (compact
|
||||
// blocks are allowed to be invalid in some circumstances,
|
||||
// under BIP 152).
|
||||
// Here, we try to detect the narrow situation that we have a
|
||||
// valid block header (ie it was valid at the time the header
|
||||
// was received, and hence stored in mapBlockIndex) but know the
|
||||
// block is invalid, and that a peer has announced that same
|
||||
// block as being on its active chain.
|
||||
// Disconnect the peer in such a situation.
|
||||
//
|
||||
// Note: if the header that is invalid was not accepted to our
|
||||
// mapBlockIndex at all, that may also be grounds for
|
||||
// disconnecting the peer, as the chain they are on is likely
|
||||
// to be incompatible. However, there is a circumstance where
|
||||
// that does not hold: if the header's timestamp is more than
|
||||
// 2 hours ahead of our current time. In that case, the header
|
||||
// may become valid in the future, and we don't want to
|
||||
// disconnect a peer merely for serving us one too-far-ahead
|
||||
// block header, to prevent an attacker from splitting the
|
||||
// network by mining a block right at the 2 hour boundary.
|
||||
//
|
||||
// TODO: update the DoS logic (or, rather, rewrite the
|
||||
// DoS-interface between validation and net_processing) so that
|
||||
// the interface is cleaner, and so that we disconnect on all the
|
||||
// reasons that a peer's headers chain is incompatible
|
||||
// with ours (eg block->nVersion softforks, MTP violations,
|
||||
// etc), and not just the duplicate-invalid case.
|
||||
pfrom->fDisconnect = true;
|
||||
}
|
||||
if (state.IsInvalid()) {
|
||||
MaybePunishNode(pfrom->GetId(), state, via_compact_block, "invalid header received");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -2119,13 +2174,13 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
|
||||
const CTransaction& orphanTx = *porphanTx;
|
||||
NodeId fromPeer = orphan_it->second.fromPeer;
|
||||
bool fMissingInputs2 = false;
|
||||
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
|
||||
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
|
||||
// anyone relaying LegitTxX banned)
|
||||
CValidationState stateDummy;
|
||||
// Use a new CValidationState because orphans come from different peers (and we call
|
||||
// MaybePunishNode based on the source peer from the orphan map, not based on the peer
|
||||
// that relayed the previous transaction).
|
||||
CValidationState orphan_state;
|
||||
|
||||
if (setMisbehaving.count(fromPeer)) continue;
|
||||
if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2 /* pfMissingInputs */,
|
||||
if (AcceptToMemoryPool(mempool, orphan_state, porphanTx, &fMissingInputs2 /* pfMissingInputs */,
|
||||
false /* bypass_limits */, 0 /* nAbsurdFee */)) {
|
||||
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
|
||||
RelayTransaction(orphanTx.GetHash(), *connman);
|
||||
@ -2140,20 +2195,19 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
|
||||
EraseOrphanTx(orphanHash);
|
||||
done = true;
|
||||
} else if (!fMissingInputs2) {
|
||||
int nDos = 0;
|
||||
if (stateDummy.IsInvalid(nDos) && nDos > 0) {
|
||||
if (orphan_state.IsInvalid()) {
|
||||
// Punish peer that gave us an invalid orphan tx
|
||||
Misbehaving(fromPeer, nDos);
|
||||
setMisbehaving.insert(fromPeer);
|
||||
if (MaybePunishNode(fromPeer, orphan_state, /*via_compact_block*/ false)) {
|
||||
setMisbehaving.insert(fromPeer);
|
||||
}
|
||||
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
|
||||
}
|
||||
// Has inputs but not accepted to mempool
|
||||
// Probably non-standard or insufficient fee
|
||||
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
|
||||
if (!stateDummy.CorruptionPossible()) {
|
||||
assert(recentRejects);
|
||||
recentRejects->insert(orphanHash);
|
||||
}
|
||||
assert(IsTransactionReason(orphan_state.GetReason()));
|
||||
assert(recentRejects);
|
||||
recentRejects->insert(orphanHash);
|
||||
EraseOrphanTx(orphanHash);
|
||||
done = true;
|
||||
}
|
||||
@ -3329,12 +3383,11 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
llmq::quorumInstantSendManager->TransactionRemovedFromMempool(ptx);
|
||||
}
|
||||
} else {
|
||||
if (!state.CorruptionPossible()) {
|
||||
assert(recentRejects);
|
||||
recentRejects->insert(tx.GetHash());
|
||||
if (RecursiveDynamicUsage(*ptx) < 100000) {
|
||||
AddToCompactExtraTransactions(ptx);
|
||||
}
|
||||
assert(IsTransactionReason(state.GetReason()));
|
||||
assert(recentRejects);
|
||||
recentRejects->insert(tx.GetHash());
|
||||
if (RecursiveDynamicUsage(*ptx) < 100000) {
|
||||
AddToCompactExtraTransactions(ptx);
|
||||
}
|
||||
|
||||
if (pfrom->HasPermission(PF_FORCERELAY)) {
|
||||
@ -3343,15 +3396,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
// to policy, allowing the node to function as a gateway for
|
||||
// nodes hidden behind it.
|
||||
//
|
||||
// Never relay transactions that we would assign a non-zero DoS
|
||||
// score for, as we expect peers to do the same with us in that
|
||||
// case.
|
||||
int nDoS = 0;
|
||||
if (!state.IsInvalid(nDoS) || nDoS == 0) {
|
||||
// Never relay transactions that might result in being
|
||||
// disconnected (or banned).
|
||||
if (state.IsInvalid() && TxRelayMayResultInDisconnect(state)) {
|
||||
LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state));
|
||||
} else {
|
||||
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
|
||||
RelayTransaction(tx.GetHash(), *connman);
|
||||
} else {
|
||||
LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state));
|
||||
}
|
||||
}
|
||||
llmq::quorumInstantSendManager->TransactionRemovedFromMempool(ptx);
|
||||
@ -3374,8 +3425,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
// peer simply for relaying a tx that our recentRejects has caught,
|
||||
// regardless of false positives.
|
||||
|
||||
int nDoS = 0;
|
||||
if (state.IsInvalid(nDoS))
|
||||
if (state.IsInvalid())
|
||||
{
|
||||
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
|
||||
pfrom->GetId(),
|
||||
@ -3384,9 +3434,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, msg_type, (unsigned char)state.GetRejectCode(),
|
||||
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
|
||||
}
|
||||
if (nDoS > 0) {
|
||||
Misbehaving(pfrom->GetId(), nDoS);
|
||||
}
|
||||
MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3422,14 +3470,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
const CBlockIndex *pindex = nullptr;
|
||||
CValidationState state;
|
||||
if (!chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
|
||||
int nDoS;
|
||||
if (state.IsInvalid(nDoS)) {
|
||||
if (nDoS > 0) {
|
||||
LOCK(cs_main);
|
||||
Misbehaving(pfrom->GetId(), nDoS, strprintf("Peer %d sent us invalid header via cmpctblock", pfrom->GetId()));
|
||||
} else {
|
||||
LogPrint(BCLog::NET, "Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId());
|
||||
}
|
||||
if (state.IsInvalid()) {
|
||||
MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -3573,7 +3615,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
// the peer if the header turns out to be for an invalid block.
|
||||
// Note that if a peer tries to build on an invalid chain, that
|
||||
// will be detected and the peer will be banned.
|
||||
return ProcessHeadersMessage(pfrom, connman, chainman, mempool, {cmpctblock.header}, chainparams, /*punish_duplicate_invalid=*/false);
|
||||
return ProcessHeadersMessage(pfrom, connman, chainman, mempool, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
|
||||
}
|
||||
|
||||
if (fBlockReconstructed) {
|
||||
@ -3726,12 +3768,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
|
||||
}
|
||||
}
|
||||
|
||||
// Headers received via a HEADERS message should be valid, and reflect
|
||||
// the chain the peer is on. If we receive a known-invalid header,
|
||||
// disconnect the peer if it is using one of our outbound connection
|
||||
// slots.
|
||||
bool should_punish = !pfrom->fInbound && !pfrom->m_manual_connection;
|
||||
return ProcessHeadersMessage(pfrom, connman, chainman, mempool, headers, chainparams, should_punish);
|
||||
return ProcessHeadersMessage(pfrom, connman, chainman, mempool, headers, chainparams, /*via_compact_block=*/false);
|
||||
}
|
||||
|
||||
if (msg_type == NetMsgType::BLOCK)
|
||||
|
@ -21,11 +21,11 @@ bool VerifyMNHFTx(const CTransaction& tx, CValidationState& state)
|
||||
{
|
||||
MNHFTxPayload mnhfTx;
|
||||
if (!GetTxPayload(tx, mnhfTx)) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-payload");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-payload");
|
||||
}
|
||||
|
||||
if (mnhfTx.nVersion == 0 || mnhfTx.nVersion > MNHFTxPayload::CURRENT_VERSION) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-mnhf-version");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-version");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -49,10 +49,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
|
||||
// Check that the validation state reflects the unsuccessful attempt.
|
||||
BOOST_CHECK(state.IsInvalid());
|
||||
BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase");
|
||||
|
||||
int nDoS;
|
||||
BOOST_CHECK_EQUAL(state.IsInvalid(nDoS), true);
|
||||
BOOST_CHECK_EQUAL(nDoS, 100);
|
||||
BOOST_CHECK(state.GetReason() == ValidationInvalidReason::CONSENSUS);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -372,18 +372,18 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
|
||||
tx.nType != TRANSACTION_COINBASE &&
|
||||
tx.nType != TRANSACTION_QUORUM_COMMITMENT &&
|
||||
tx.nType != TRANSACTION_MNHF_SIGNAL) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-type");
|
||||
}
|
||||
if (tx.IsCoinBase() && tx.nType != TRANSACTION_COINBASE)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-cb-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-cb-type");
|
||||
} else if (tx.nType != TRANSACTION_NORMAL) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-type");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-type");
|
||||
}
|
||||
}
|
||||
|
||||
// Size limits
|
||||
if (fDIP0001Active_context && ::GetSerializeSize(tx, PROTOCOL_VERSION) > MAX_STANDARD_TX_SIZE)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-oversize");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -534,35 +534,35 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
|
||||
if (tx.nVersion == 3 && tx.nType == TRANSACTION_QUORUM_COMMITMENT) {
|
||||
// quorum commitment is not allowed outside of blocks
|
||||
return state.DoS(100, false, REJECT_INVALID, "qc-not-allowed");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "qc-not-allowed");
|
||||
}
|
||||
|
||||
// Coinbase is only valid in a block, not as a loose transaction
|
||||
if (tx.IsCoinBase())
|
||||
return state.DoS(100, false, REJECT_INVALID, "coinbase");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "coinbase");
|
||||
|
||||
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
||||
std::string reason;
|
||||
if (fRequireStandard && !IsStandardTx(tx, reason))
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
|
||||
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, reason);
|
||||
|
||||
// Do not work on transactions that are too small.
|
||||
// A transaction with 1 empty scriptSig input and 1 P2SH output has size of 83 bytes.
|
||||
// Transactions smaller than this are not relayed to mitigate CVE-2017-12842 by not relaying
|
||||
// 64-byte transactions.
|
||||
if (::GetSerializeSize(tx, PROTOCOL_VERSION) < MIN_STANDARD_TX_SIZE)
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small");
|
||||
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "tx-size-small");
|
||||
|
||||
// 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
|
||||
// be mined yet.
|
||||
if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, "non-final");
|
||||
return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-final");
|
||||
|
||||
// is it already in the memory pool?
|
||||
if (pool.exists(hash)) {
|
||||
statsClient.inc("transactions.duplicate", 1.0f);
|
||||
return state.Invalid(false, REJECT_DUPLICATE, "txn-already-in-mempool");
|
||||
return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-in-mempool");
|
||||
}
|
||||
|
||||
llmq::CInstantSendLockPtr conflictLock = llmq::quorumInstantSendManager->GetConflictingLock(tx);
|
||||
@ -572,9 +572,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
if (txConflict) {
|
||||
GetMainSignals().NotifyInstantSendDoubleSpendAttempt(ptx, txConflict);
|
||||
}
|
||||
return state.DoS(10, error("AcceptToMemoryPool : Transaction %s conflicts with locked TX %s",
|
||||
hash.ToString(), conflictLock->txid.ToString()),
|
||||
REJECT_INVALID, "tx-txlock-conflict");
|
||||
return state.Invalid(ValidationInvalidReason::TX_CONFLICT_LOCK, error("AcceptToMemoryPool : Transaction %s conflicts with locked TX %s", hash.ToString(), conflictLock->txid.ToString()), REJECT_INVALID, "tx-txlock-conflict");
|
||||
}
|
||||
|
||||
if (llmq::quorumInstantSendManager->IsWaitingForTx(hash)) {
|
||||
@ -588,7 +586,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
if (ptxConflicting)
|
||||
{
|
||||
// Transaction conflicts with mempool and RBF doesn't exist in Dash
|
||||
return state.Invalid(false, REJECT_DUPLICATE, "txn-mempool-conflict");
|
||||
return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-mempool-conflict");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -616,7 +614,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
for (size_t out = 0; out < tx.vout.size(); out++) {
|
||||
// Optimistically just do efficient check of cache for outputs
|
||||
if (coins_cache.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
|
||||
@ -639,7 +637,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
// Must keep pool.cs for this unless we change CheckSequenceLocks to take a
|
||||
// CoinsViewCache instead of create its own
|
||||
if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
|
||||
return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final");
|
||||
|
||||
CAmount nFees = 0;
|
||||
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) {
|
||||
@ -648,7 +646,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
|
||||
// Check for non-standard pay-to-script-hash in inputs
|
||||
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");
|
||||
|
||||
unsigned int nSigOps = GetTransactionSigOpCount(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS);
|
||||
|
||||
@ -671,27 +669,22 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
fSpendsCoinbase, nSigOps, lp);
|
||||
unsigned int nSize = entry.GetTxSize();
|
||||
|
||||
// Check that the transaction doesn't have an excessive number of
|
||||
// sigops, making it impossible to mine. Since the coinbase transaction
|
||||
// itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than
|
||||
// MAX_BLOCK_SIGOPS; we still consider this an invalid rather than
|
||||
// merely non-standard transaction.
|
||||
if (nSigOps > MAX_STANDARD_TX_SIGOPS)
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
|
||||
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops",
|
||||
strprintf("%d", nSigOps));
|
||||
|
||||
CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
|
||||
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.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", nModifiedFees, mempoolRejectFee));
|
||||
}
|
||||
|
||||
// No transactions are allowed below minRelayTxFee except from disconnected blocks
|
||||
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.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
|
||||
}
|
||||
|
||||
if (nAbsurdFee && nFees > nAbsurdFee)
|
||||
return state.Invalid(false,
|
||||
return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false,
|
||||
REJECT_HIGHFEE, "absurdly-high-fee",
|
||||
strprintf("%d > %d", nFees, nAbsurdFee));
|
||||
|
||||
@ -718,7 +711,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
// this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html
|
||||
if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT ||
|
||||
!pool.CalculateMemPoolAncestors(entry, setAncestors, 2, nLimitAncestorSize, nLimitDescendants + 1, nLimitDescendantSize + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) {
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, "too-long-mempool-chain", false, errString);
|
||||
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString);
|
||||
}
|
||||
}
|
||||
|
||||
@ -730,7 +723,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
return false;
|
||||
|
||||
if (pool.existsProviderTxConflict(tx)) {
|
||||
return state.DoS(0, false, REJECT_DUPLICATE, "protx-dup");
|
||||
return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "protx-dup");
|
||||
}
|
||||
|
||||
constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
||||
@ -738,8 +731,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
// Check against previous transactions
|
||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
||||
PrecomputedTransactionData txdata;
|
||||
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata))
|
||||
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata)) {
|
||||
assert(IsTransactionReason(state.GetReason()));
|
||||
return false; // state filled in by CheckInputs
|
||||
}
|
||||
|
||||
// Check again against the current block tip's script verification
|
||||
// flags to cache our script execution flags. This is, of course,
|
||||
@ -796,7 +791,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||
if (!bypass_limits) {
|
||||
LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
|
||||
if (!pool.exists(hash))
|
||||
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full");
|
||||
return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1323,7 +1318,7 @@ void static ConflictingChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIR
|
||||
|
||||
void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
|
||||
statsClient.inc("warnings.InvalidBlockFound", 1.0f);
|
||||
if (!state.CorruptionPossible()) {
|
||||
if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) {
|
||||
pindex->nStatus |= BLOCK_FAILED_VALID;
|
||||
m_blockman.m_failed_blocks.insert(pindex);
|
||||
setDirtyBlockIndex.insert(pindex);
|
||||
@ -1398,6 +1393,9 @@ void InitScriptExecutionCache() {
|
||||
* which are matched. This is useful for checking blocks where we will likely never need the cache
|
||||
* 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
|
||||
*/
|
||||
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)
|
||||
@ -1459,22 +1457,26 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
||||
// Check whether the failure was caused by a
|
||||
// non-mandatory script verification check, such as
|
||||
// non-standard DER encodings or non-null dummy
|
||||
// arguments; if so, don't trigger DoS protection to
|
||||
// avoid splitting the network between upgraded and
|
||||
// non-upgraded nodes.
|
||||
// arguments; if so, ensure we return NOT_STANDARD
|
||||
// instead of CONSENSUS to avoid downstream users
|
||||
// splitting the network between upgraded and
|
||||
// non-upgraded nodes by banning CONSENSUS-failing
|
||||
// data providers.
|
||||
CScriptCheck check2(coin.out, tx, i,
|
||||
(flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS) | SCRIPT_ENABLE_DIP0020_OPCODES, cacheSigStore, &txdata);
|
||||
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
|
||||
// invalid in new blocks, e.g. an invalid P2SH. We DoS ban
|
||||
// such nodes as they are not following the protocol. That
|
||||
// said during an upgrade careful thought should be taken
|
||||
// as to the correct behavior - we may want to continue
|
||||
// peering with non-upgraded nodes even after soft-fork
|
||||
// super-majority signaling has occurred.
|
||||
return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
|
||||
// MANDATORY flag failures correspond to
|
||||
// ValidationInvalidReason::CONSENSUS. Because CONSENSUS
|
||||
// failures are the most serious case of validation
|
||||
// failures, we may need to consider using
|
||||
// RECENT_CONSENSUS_CHANGE for any script failure that
|
||||
// could be due to non-upgraded nodes which we may want to
|
||||
// support, to avoid splitting the network (but this
|
||||
// depends on the details of how net_processing handles
|
||||
// such errors).
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1982,7 +1984,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
// re-enforce that rule here (at least until we make it impossible for
|
||||
// GetAdjustedTime() to go backward).
|
||||
if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck)) {
|
||||
if (state.CorruptionPossible()) {
|
||||
if (state.GetReason() == ValidationInvalidReason::BLOCK_MUTATED) {
|
||||
// We don't write down blocks to disk if they may have been
|
||||
// corrupted, so this should be impossible unless we're having hardware
|
||||
// problems.
|
||||
@ -1992,7 +1994,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
}
|
||||
|
||||
if (pindex->pprev && pindex->phashBlock && llmq::chainLocksHandler->HasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
|
||||
return state.DoS(10, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
|
||||
}
|
||||
|
||||
// verify that the view's current state corresponds to the previous block
|
||||
@ -2076,8 +2078,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
for (const auto& tx : block.vtx) {
|
||||
for (size_t o = 0; o < tx->vout.size(); o++) {
|
||||
if (view.HaveCoin(COutPoint(tx->GetHash(), o))) {
|
||||
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
|
||||
REJECT_INVALID, "bad-txns-BIP30");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2088,10 +2089,9 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
// make sure old budget is the real one
|
||||
if (pindex->nHeight == chainparams.GetConsensus().nSuperblockStartBlock &&
|
||||
chainparams.GetConsensus().nSuperblockStartHash != uint256() &&
|
||||
block.GetHash() != chainparams.GetConsensus().nSuperblockStartHash)
|
||||
return state.DoS(100, error("ConnectBlock(): invalid superblock start"),
|
||||
REJECT_INVALID, "bad-sb-start");
|
||||
|
||||
block.GetHash() != chainparams.GetConsensus().nSuperblockStartHash) {
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): invalid superblock start"), REJECT_INVALID, "bad-sb-start");
|
||||
}
|
||||
/// END DASH
|
||||
|
||||
// Start enforcing BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic.
|
||||
@ -2147,12 +2147,19 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
{
|
||||
CAmount txfee = 0;
|
||||
if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) {
|
||||
if (!IsBlockReason(state.GetReason())) {
|
||||
// CheckTxInputs may return MISSING_INPUTS or
|
||||
// PREMATURE_SPEND but we can't return that, as it's not
|
||||
// defined for a block, so we reset the reason flag to
|
||||
// CONSENSUS here.
|
||||
state.Invalid(ValidationInvalidReason::CONSENSUS, false,
|
||||
state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage());
|
||||
}
|
||||
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
|
||||
}
|
||||
nFees += txfee;
|
||||
if (!MoneyRange(nFees)) {
|
||||
return state.DoS(100, error("%s: accumulated fee in the block out of range.", __func__),
|
||||
REJECT_INVALID, "bad-txns-accumulated-fee-outofrange");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, error("%s: accumulated fee in the block out of range.", __func__), REJECT_INVALID, "bad-txns-accumulated-fee-outofrange");
|
||||
}
|
||||
|
||||
// Check that transaction is BIP68 final
|
||||
@ -2164,8 +2171,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
}
|
||||
|
||||
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
|
||||
return state.DoS(100, error("%s: contains a non-BIP68-final transaction", __func__),
|
||||
REJECT_INVALID, "bad-txns-nonfinal");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, error("%s: contains a non-BIP68-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal");
|
||||
}
|
||||
|
||||
if (fAddressIndex || fSpentIndex)
|
||||
@ -2214,9 +2220,9 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
// * legacy (always)
|
||||
// * p2sh (when P2SH enabled in flags and excludes coinbase)
|
||||
nSigOps += GetTransactionSigOpCount(tx, view, flags);
|
||||
if (nSigOps > MaxBlockSigOps(fDIP0001Active_context))
|
||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||
REJECT_INVALID, "bad-blk-sigops");
|
||||
if (nSigOps > MaxBlockSigOps(fDIP0001Active_context)) {
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops");
|
||||
}
|
||||
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
@ -2224,6 +2230,16 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
std::vector<CScriptCheck> vChecks;
|
||||
bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
|
||||
if (fScriptChecks && !CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) {
|
||||
if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) {
|
||||
// CheckInputs may return NOT_STANDARD for extra flags we passed,
|
||||
// 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.Invalid(ValidationInvalidReason::CONSENSUS, false,
|
||||
state.GetRejectCode(), state.GetRejectReason(), state.GetDebugMessage());
|
||||
}
|
||||
return error("ConnectBlock(): CheckInputs on %s failed with %s",
|
||||
tx.GetHash().ToString(), FormatStateMessage(state));
|
||||
}
|
||||
@ -2273,7 +2289,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
LogPrint(BCLog::BENCHMARK, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal);
|
||||
|
||||
if (!control.Wait())
|
||||
return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed");
|
||||
int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2;
|
||||
LogPrint(BCLog::BENCHMARK, " - 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);
|
||||
|
||||
@ -2301,8 +2317,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
} else {
|
||||
// The node which relayed this should switch to correct chain.
|
||||
// TODO: relay instantsend data/proof.
|
||||
return state.DoS(10, error("ConnectBlock(DASH): transaction %s conflicts with transaction lock %s",
|
||||
tx->GetHash().ToString(), conflictLock->txid.ToString()), REJECT_INVALID, "conflict-tx-lock");
|
||||
return state.Invalid(ValidationInvalidReason::TX_CONFLICT_LOCK, error("ConnectBlock(DASH): transaction %s conflicts with transaction lock %s", tx->GetHash().ToString(), conflictLock->txid.ToString()), REJECT_INVALID, "conflict-tx-lock");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2321,15 +2336,16 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||
LogPrint(BCLog::BENCHMARK, " - GetBlockSubsidy: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5_2 - nTime5_1), nTimeSubsidy * MICRO, nTimeSubsidy * MILLI / nBlocksTotal);
|
||||
|
||||
if (!IsBlockValueValid(block, pindex->nHeight, blockReward, strError)) {
|
||||
return state.DoS(0, error("ConnectBlock(DASH): %s", strError), REJECT_INVALID, "bad-cb-amount");
|
||||
// NOTE: Do not punish, the node might be missing governance data
|
||||
return state.Invalid(ValidationInvalidReason::NONE, error("ConnectBlock(DASH): %s", strError), REJECT_INVALID, "bad-cb-amount");
|
||||
}
|
||||
|
||||
int64_t nTime5_3 = GetTimeMicros(); nTimeValueValid += nTime5_3 - nTime5_2;
|
||||
LogPrint(BCLog::BENCHMARK, " - IsBlockValueValid: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5_3 - nTime5_2), nTimeValueValid * MICRO, nTimeValueValid * MILLI / nBlocksTotal);
|
||||
|
||||
if (!IsBlockPayeeValid(*block.vtx[0], pindex->nHeight, blockReward)) {
|
||||
return state.DoS(0, error("ConnectBlock(DASH): couldn't find masternode or superblock payments"),
|
||||
REJECT_INVALID, "bad-cb-payee");
|
||||
// NOTE: Do not punish, the node might be missing governance data
|
||||
return state.Invalid(ValidationInvalidReason::NONE, error("ConnectBlock(DASH): couldn't find masternode or superblock payments"), REJECT_INVALID, "bad-cb-payee");
|
||||
}
|
||||
|
||||
int64_t nTime5_4 = GetTimeMicros(); nTimePayeeValid += nTime5_4 - nTime5_3;
|
||||
@ -2973,7 +2989,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
|
||||
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
|
||||
if (state.IsInvalid()) {
|
||||
// The block violates a consensus rule.
|
||||
if (!state.CorruptionPossible()) {
|
||||
if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) {
|
||||
InvalidChainFound(vpindexToConnect.front());
|
||||
}
|
||||
state = CValidationState();
|
||||
@ -3688,14 +3704,13 @@ static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state,
|
||||
{
|
||||
// Check proof of work matches claimed amount
|
||||
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
|
||||
return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_INVALID, "high-hash", "proof of work failed");
|
||||
|
||||
// Check DevNet
|
||||
if (!consensusParams.hashDevnetGenesisBlock.IsNull() &&
|
||||
block.hashPrevBlock == consensusParams.hashGenesisBlock &&
|
||||
block.GetHash() != consensusParams.hashDevnetGenesisBlock) {
|
||||
return state.DoS(100, error("CheckBlockHeader(): wrong devnet genesis"),
|
||||
REJECT_INVALID, "devnet-genesis");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, error("CheckBlockHeader(): wrong devnet genesis"), REJECT_INVALID, "devnet-genesis");
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -3720,13 +3735,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
|
||||
bool mutated;
|
||||
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated);
|
||||
if (block.hashMerkleRoot != hashMerkleRoot2)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", true, "hashMerkleRoot mismatch");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-txnmrklroot", "hashMerkleRoot mismatch");
|
||||
|
||||
// Check for merkle tree malleability (CVE-2012-2459): repeating sequences
|
||||
// of transactions in a block without affecting the merkle root of a block,
|
||||
// while still invalidating it.
|
||||
if (mutated)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, REJECT_INVALID, "bad-txns-duplicate", "duplicate transaction");
|
||||
}
|
||||
|
||||
// All potential-corruption validation must be done before we do any
|
||||
@ -3735,19 +3750,19 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
|
||||
|
||||
// Size limits (relaxed)
|
||||
if (block.vtx.empty() || block.vtx.size() > MaxBlockSize() || ::GetSerializeSize(block, PROTOCOL_VERSION) > MaxBlockSize())
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-length", "size limits failed");
|
||||
|
||||
// First transaction must be coinbase, the rest must not be
|
||||
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.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-missing", "first tx is not coinbase");
|
||||
for (unsigned int i = 1; i < block.vtx.size(); i++)
|
||||
if (block.vtx[i]->IsCoinBase())
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-multiple", "more than one coinbase");
|
||||
|
||||
// Check transactions
|
||||
for (const auto& tx : block.vtx)
|
||||
if (!CheckTransaction(*tx, state))
|
||||
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()));
|
||||
|
||||
unsigned int nSigOps = 0;
|
||||
@ -3757,7 +3772,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
|
||||
}
|
||||
// sigops limits (relaxed)
|
||||
if (nSigOps > MaxBlockSigOps())
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-sigops", "out-of-bounds SigOpCount");
|
||||
|
||||
if (fCheckPOW && fCheckMerkleRoot)
|
||||
block.fChecked = true;
|
||||
@ -3808,11 +3823,11 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
|
||||
double n2 = ConvertBitsToDouble(nBitsNext);
|
||||
|
||||
if (abs(n1-n2) > n1*0.5)
|
||||
return state.DoS(100, error("%s : incorrect proof of work (DGW pre-fork) - %f %f %f at %d", __func__, abs(n1-n2), n1, n2, nHeight),
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, error("%s : incorrect proof of work (DGW pre-fork) - %f %f %f at %d", __func__, abs(n1-n2), n1, n2, nHeight),
|
||||
REJECT_INVALID, "bad-diffbits");
|
||||
} else {
|
||||
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, strprintf("incorrect proof of work at %d", nHeight));
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_INVALID, "bad-diffbits", strprintf("incorrect proof of work at %d", nHeight));
|
||||
}
|
||||
|
||||
// Check against checkpoints
|
||||
@ -3822,23 +3837,22 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta
|
||||
// BlockIndex().
|
||||
CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints());
|
||||
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.Invalid(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
|
||||
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
|
||||
return state.Invalid(false, REJECT_INVALID, "time-too-old", strprintf("block's timestamp is too early %d %d", block.GetBlockTime(), pindexPrev->GetMedianTimePast()));
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_INVALID, "time-too-old", strprintf("block's timestamp is too early %d %d", block.GetBlockTime(), pindexPrev->GetMedianTimePast()));
|
||||
|
||||
// Check timestamp
|
||||
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
|
||||
return state.Invalid(false, REJECT_INVALID, "time-too-new", strprintf("block timestamp too far in the future %d %d", block.GetBlockTime(), nAdjustedTime + 2 * 60 * 60));
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_TIME_FUTURE, false, REJECT_INVALID, "time-too-new", strprintf("block timestamp too far in the future %d %d", block.GetBlockTime(), nAdjustedTime + 2 * 60 * 60));
|
||||
|
||||
// check for version 2, 3 and 4 upgrades
|
||||
if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) ||
|
||||
(block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) ||
|
||||
(block.nVersion < 4 && nHeight >= consensusParams.BIP65Height))
|
||||
return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion),
|
||||
strprintf("rejected nVersion=0x%08x block", block.nVersion));
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion), strprintf("rejected nVersion=0x%08x block", block.nVersion));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -3871,14 +3885,14 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
||||
// Size limits
|
||||
unsigned int nMaxBlockSize = MaxBlockSize(fDIP0001Active_context);
|
||||
if (block.vtx.empty() || block.vtx.size() > nMaxBlockSize || ::GetSerializeSize(block, PROTOCOL_VERSION) > nMaxBlockSize)
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-length", "size limits failed");
|
||||
|
||||
// Check that all transactions are finalized and not over-sized
|
||||
// Also count sigops
|
||||
unsigned int nSigOps = 0;
|
||||
for (const auto& tx : block.vtx) {
|
||||
if (!IsFinalTx(*tx, nHeight, nLockTimeCutoff)) {
|
||||
return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-nonfinal", "non-final transaction");
|
||||
}
|
||||
if (!ContextualCheckTransaction(*tx, state, consensusParams, pindexPrev)) {
|
||||
return false;
|
||||
@ -3888,7 +3902,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
||||
|
||||
// Check sigops
|
||||
if (nSigOps > MaxBlockSigOps(fDIP0001Active_context))
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-blk-sigops", "out-of-bounds SigOpCount");
|
||||
|
||||
// Enforce rule that the coinbase starts with serialized block height
|
||||
// After DIP3/DIP4 activation, we don't enforce the height in the input script anymore.
|
||||
@ -3898,13 +3912,13 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
|
||||
CScript expect = CScript() << nHeight;
|
||||
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
|
||||
!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.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-height", "block height mismatch in coinbase");
|
||||
}
|
||||
}
|
||||
|
||||
if (fDIP0003Active_context) {
|
||||
if (block.vtx[0]->nType != TRANSACTION_COINBASE) {
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-cb-type", false, "coinbase is not a CbTx");
|
||||
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-type", "coinbase is not a CbTx");
|
||||
}
|
||||
}
|
||||
|
||||
@ -3927,9 +3941,9 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState
|
||||
if (ppindex)
|
||||
*ppindex = pindex;
|
||||
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");
|
||||
if (pindex->nStatus & BLOCK_CONFLICT_CHAINLOCK)
|
||||
return state.Invalid(error("%s: block %s is marked conflicting", __func__, hash.ToString()), 0, "duplicate");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: block %s is marked conflicting", __func__, hash.ToString()), 0, "duplicate");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3940,16 +3954,16 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState
|
||||
CBlockIndex* pindexPrev = nullptr;
|
||||
BlockMap::iterator mi = m_block_index.find(block.hashPrevBlock);
|
||||
if (mi == m_block_index.end())
|
||||
return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_MISSING_PREV, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
|
||||
pindexPrev = (*mi).second;
|
||||
assert(pindexPrev);
|
||||
|
||||
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
|
||||
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
|
||||
|
||||
if (pindexPrev->nStatus & BLOCK_CONFLICT_CHAINLOCK)
|
||||
// it's ok-ish, the other node is probably missing the latest chainlock
|
||||
return state.DoS(10, error("%s: prev block %s conflicts with chainlock", __func__, block.hashPrevBlock.ToString()), REJECT_INVALID, "bad-prevblk-chainlock");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: prev block %s conflicts with chainlock", __func__, block.hashPrevBlock.ToString()), REJECT_INVALID, "bad-prevblk-chainlock");
|
||||
|
||||
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
|
||||
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
|
||||
@ -3987,7 +4001,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState
|
||||
setDirtyBlockIndex.insert(invalid_walk);
|
||||
invalid_walk = invalid_walk->pprev;
|
||||
}
|
||||
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3996,7 +4010,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState
|
||||
if (pindex == nullptr) {
|
||||
AddToBlockIndex(block, BLOCK_CONFLICT_CHAINLOCK);
|
||||
}
|
||||
return state.DoS(10, error("%s: header %s conflicts with chainlock", __func__, hash.ToString()), REJECT_INVALID, "bad-chainlock");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: header %s conflicts with chainlock", __func__, hash.ToString()), REJECT_INVALID, "bad-chainlock");
|
||||
}
|
||||
}
|
||||
if (pindex == nullptr)
|
||||
@ -4113,7 +4127,8 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
|
||||
|
||||
if (!CheckBlock(block, state, chainparams.GetConsensus()) ||
|
||||
!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) {
|
||||
if (state.IsInvalid() && !state.CorruptionPossible()) {
|
||||
assert(IsBlockReason(state.GetReason()));
|
||||
if (state.IsInvalid() && state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) {
|
||||
pindex->nStatus |= BLOCK_FAILED_VALID;
|
||||
setDirtyBlockIndex.insert(pindex);
|
||||
}
|
||||
@ -4194,7 +4209,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
|
||||
|
||||
uint256 hash = block.GetHash();
|
||||
if (llmq::chainLocksHandler->HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) {
|
||||
return state.DoS(10, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
|
||||
return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
|
||||
}
|
||||
|
||||
CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip());
|
||||
|
@ -58,7 +58,7 @@ class BadTxTemplate:
|
||||
|
||||
class OutputMissing(BadTxTemplate):
|
||||
reject_reason = "bad-txns-vout-empty"
|
||||
expect_disconnect = False
|
||||
expect_disconnect = True
|
||||
|
||||
def get_tx(self):
|
||||
tx = CTransaction()
|
||||
@ -69,7 +69,7 @@ class OutputMissing(BadTxTemplate):
|
||||
|
||||
class InputMissing(BadTxTemplate):
|
||||
reject_reason = "bad-txns-vin-empty"
|
||||
expect_disconnect = False
|
||||
expect_disconnect = True
|
||||
|
||||
def get_tx(self):
|
||||
tx = CTransaction()
|
||||
|
@ -301,7 +301,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
self.log.info("Reject a block spending an immature coinbase.")
|
||||
self.move_tip(15)
|
||||
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)
|
||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
|
||||
@ -314,7 +314,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
self.send_blocks([b21], False)
|
||||
|
||||
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_SIZE and make sure its accepted/rejected
|
||||
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
|
||||
@ -636,7 +636,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
while b47.sha256 <= target:
|
||||
b47.nNonce += 1
|
||||
b47.rehash()
|
||||
self.send_blocks([b47], False, force_send=True, reject_reason='high-hash')
|
||||
self.send_blocks([b47], False, force_send=True, reject_reason='high-hash', reconnect=True)
|
||||
|
||||
self.log.info("Reject a block with a timestamp >2 hours in the future")
|
||||
self.move_tip(44)
|
||||
@ -687,7 +687,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
b54 = self.next_block(54, spend=out[15])
|
||||
b54.nTime = b35.nTime - 1
|
||||
b54.solve()
|
||||
self.send_blocks([b54], False, force_send=True, reject_reason='time-too-old')
|
||||
self.send_blocks([b54], False, force_send=True, reject_reason='time-too-old', reconnect=True)
|
||||
|
||||
# valid timestamp
|
||||
self.move_tip(53)
|
||||
@ -857,7 +857,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
assert tx.vin[0].nSequence < 0xffffffff
|
||||
tx.calc_sha256()
|
||||
b62 = self.update_block(62, [tx])
|
||||
self.send_blocks([b62], success=False, reject_reason='bad-txns-nonfinal')
|
||||
self.send_blocks([b62], success=False, reject_reason='bad-txns-nonfinal', reconnect=True)
|
||||
|
||||
# Test a non-final coinbase is also rejected
|
||||
#
|
||||
@ -871,7 +871,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
b63.vtx[0].vin[0].nSequence = 0xDEADBEEF
|
||||
b63.vtx[0].rehash()
|
||||
b63 = self.update_block(63, [])
|
||||
self.send_blocks([b63], success=False, reject_reason='bad-txns-nonfinal')
|
||||
self.send_blocks([b63], success=False, reject_reason='bad-txns-nonfinal', reconnect=True)
|
||||
|
||||
# This checks that a block with a bloated VARINT between the block_header and the array of tx such that
|
||||
# the block is > MAX_BLOCK_SIZE with the bloated varint, but <= MAX_BLOCK_SIZE without the bloated varint,
|
||||
@ -1285,7 +1285,7 @@ class FullBlockTest(BitcoinTestFramework):
|
||||
|
||||
self.log.info("Reject a block with an invalid block header version")
|
||||
b_v1 = self.next_block('b_v1', version=1)
|
||||
self.send_blocks([b_v1], success=False, force_send=True, reject_reason='bad-version(0x00000001)')
|
||||
self.send_blocks([b_v1], success=False, force_send=True, reject_reason='bad-version(0x00000001)', reconnect=True)
|
||||
|
||||
self.move_tip(chain1_tip + 2)
|
||||
b_cb34 = self.next_block('b_cb34', version=4)
|
||||
|
@ -85,6 +85,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
||||
"policy/policy -> policy/settings -> policy/policy"
|
||||
"evo/specialtxman -> validation -> evo/specialtxman"
|
||||
"bloom -> llmq/commitment -> llmq/utils -> net -> bloom"
|
||||
"banman -> bloom -> llmq/commitment -> llmq/utils -> net -> banman"
|
||||
|
||||
"evo/simplifiedmns -> llmq/blockprocessor -> net_processing -> llmq/snapshot -> evo/simplifiedmns"
|
||||
"llmq/blockprocessor -> net_processing -> llmq/snapshot -> llmq/blockprocessor"
|
||||
|
Loading…
Reference in New Issue
Block a user