Ability to ignore IsStandardTx rejection reasons

This commit is contained in:
Luke Dashjr 2023-08-01 18:12:32 +00:00
parent ccc3f45e09
commit d5b82da92c
3 changed files with 40 additions and 23 deletions

View File

@ -67,6 +67,10 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
}
/**
* Note this must assign whichType even if returning false, in case
* IsStandardTx ignores the "scriptpubkey" rejection.
*/
bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_datacarrier_bytes, TxoutType& whichType)
{
std::vector<std::vector<unsigned char> > vSolutions;
@ -91,21 +95,36 @@ bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_
return true;
}
bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason)
{
if (tx.nVersion > TX_MAX_STANDARD_VERSION || tx.nVersion < 1) {
reason = "version";
static inline bool MaybeReject_(std::string& out_reason, const std::string& reason, const std::string& reason_prefix, const ignore_rejects_type& ignore_rejects) {
if (ignore_rejects.count(reason_prefix + reason)) {
return false;
}
out_reason = reason_prefix + reason;
return true;
}
#define MaybeReject(reason) do { \
if (MaybeReject_(out_reason, reason, reason_prefix, ignore_rejects)) { \
return false; \
} \
} while(0)
bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& out_reason, const ignore_rejects_type& ignore_rejects)
{
const std::string reason_prefix;
if (tx.nVersion > TX_MAX_STANDARD_VERSION || tx.nVersion < 1) {
MaybeReject("version");
}
// Extremely large transactions with lots of inputs can cost the network
// almost as much to process as they cost the sender in fees, because
// computing signature hashes is O(ninputs*txsize). Limiting transactions
// to MAX_STANDARD_TX_WEIGHT mitigates CPU exhaustion attacks.
unsigned int sz = GetTransactionWeight(tx);
if (sz > MAX_STANDARD_TX_WEIGHT) {
reason = "tx-size";
return false;
MaybeReject("tx-size");
}
for (const CTxIn& txin : tx.vin)
@ -119,12 +138,10 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
// 20-of-20 CHECKMULTISIG scriptPubKey, though such a scriptPubKey
// is not considered standard.
if (txin.scriptSig.size() > MAX_STANDARD_SCRIPTSIG_SIZE) {
reason = "scriptsig-size";
return false;
MaybeReject("scriptsig-size");
}
if (!txin.scriptSig.IsPushOnly()) {
reason = "scriptsig-not-pushonly";
return false;
MaybeReject("scriptsig-not-pushonly");
}
}
@ -132,28 +149,28 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
TxoutType whichType;
for (const CTxOut& txout : tx.vout) {
if (!::IsStandard(txout.scriptPubKey, max_datacarrier_bytes, whichType)) {
reason = "scriptpubkey";
if (whichType == TxoutType::WITNESS_UNKNOWN) {
reason += "-unknown-witnessversion";
MaybeReject("scriptpubkey-unknown-witnessversion");
} else {
MaybeReject("scriptpubkey");
}
return false;
}
if (whichType == TxoutType::NULL_DATA)
if (whichType == TxoutType::NULL_DATA) {
nDataOut++;
continue;
}
else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
reason = "bare-multisig";
return false;
} else if (IsDust(txout, dust_relay_fee)) {
reason = "dust";
return false;
MaybeReject("bare-multisig");
}
if (IsDust(txout, dust_relay_fee)) {
MaybeReject("dust");
}
}
// only one OP_RETURN txout is permitted
if (nDataOut > 1) {
reason = "multi-op-return";
return false;
MaybeReject("multi-op-return");
}
return true;

View File

@ -141,7 +141,7 @@ static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{2};
* Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
*/
bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason);
bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& out_reason, const ignore_rejects_type& ignore_rejects=empty_ignore_rejects);
/**
* Check for standard transaction types
* @param[in] mapInputs Map of previous transactions that have outputs we're spending

View File

@ -735,7 +735,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
std::string reason;
if (m_pool.m_require_standard && !IsStandardTx(tx, m_pool.m_max_datacarrier_bytes, m_pool.m_permit_bare_multisig, m_pool.m_dust_relay_feerate, reason)) {
if (m_pool.m_require_standard && !IsStandardTx(tx, m_pool.m_max_datacarrier_bytes, m_pool.m_permit_bare_multisig, m_pool.m_dust_relay_feerate, reason, ignore_rejects)) {
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
}