refactor: policy: Pass kernel::MemPoolOptions to IsStandard[Tx] rather than long list of individual options

Github-Pull: #30232
Rebased-From: d4c3c2e9ae2536c345cc8f20c2de3a466eef7ced
This commit is contained in:
Luke Dashjr 2024-06-05 14:18:03 +00:00
parent bfaf703665
commit 734576e3e3
9 changed files with 44 additions and 29 deletions

View File

@ -11,6 +11,7 @@
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <kernel/mempool_options.h>
#include <policy/feerate.h>
#include <primitives/transaction.h>
#include <script/interpreter.h>
@ -71,7 +72,7 @@ bool IsDust(const CTxOut& txout, const CFeeRate& 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)
bool IsStandard(const CScript& scriptPubKey, const kernel::MemPoolOptions& opts, TxoutType& whichType)
{
std::vector<std::vector<unsigned char> > vSolutions;
whichType = Solver(scriptPubKey, vSolutions);
@ -87,7 +88,7 @@ bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_
if (m < 1 || m > n)
return false;
} else if (whichType == TxoutType::NULL_DATA) {
if (!max_datacarrier_bytes || scriptPubKey.size() > *max_datacarrier_bytes) {
if (!opts.max_datacarrier_bytes || scriptPubKey.size() > *opts.max_datacarrier_bytes) {
return false;
}
}
@ -110,7 +111,7 @@ static inline bool MaybeReject_(std::string& out_reason, const std::string& reas
} \
} 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)
bool IsStandardTx(const CTransaction& tx, const kernel::MemPoolOptions& opts, std::string& out_reason, const ignore_rejects_type& ignore_rejects)
{
const std::string reason_prefix;
@ -148,7 +149,7 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
unsigned int nDataOut = 0;
TxoutType whichType;
for (const CTxOut& txout : tx.vout) {
if (!::IsStandard(txout.scriptPubKey, max_datacarrier_bytes, whichType)) {
if (!::IsStandard(txout.scriptPubKey, opts, whichType)) {
if (whichType == TxoutType::WITNESS_UNKNOWN) {
MaybeReject("scriptpubkey-unknown-witnessversion");
} else {
@ -160,10 +161,10 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
nDataOut++;
continue;
}
else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
else if ((whichType == TxoutType::MULTISIG) && (!opts.permit_bare_multisig)) {
MaybeReject("bare-multisig");
}
if (IsDust(txout, dust_relay_fee)) {
if (IsDust(txout, opts.dust_relay_feerate)) {
MaybeReject("dust");
}
}

View File

@ -19,6 +19,9 @@
class CCoinsViewCache;
class CFeeRate;
class CScript;
namespace kernel {
struct MemPoolOptions;
};
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
static constexpr unsigned int DEFAULT_BLOCK_MAX_WEIGHT{MAX_BLOCK_WEIGHT - 4000};
@ -129,7 +132,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee);
bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_datacarrier_bytes, TxoutType& whichType);
bool IsStandard(const CScript& scriptPubKey, const kernel::MemPoolOptions& opts, TxoutType& whichType);
// Changing the default transaction version requires a two step process: first
@ -141,7 +144,7 @@ static constexpr decltype(CTransaction::version) TX_MAX_STANDARD_VERSION{3};
* 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& out_reason, const ignore_rejects_type& ignore_rejects=empty_ignore_rejects);
bool IsStandardTx(const CTransaction& tx, const kernel::MemPoolOptions& opts, 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

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
#include <kernel/mempool_options.h>
#include <key.h>
#include <key_io.h>
#include <outputtype.h>
@ -149,12 +150,12 @@ FUZZ_TARGET(key, .init = initialize_key)
assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
TxoutType which_type_tx_pubkey;
const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, std::nullopt, which_type_tx_pubkey);
const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, kernel::MemPoolOptions{}, which_type_tx_pubkey);
assert(is_standard_tx_pubkey);
assert(which_type_tx_pubkey == TxoutType::PUBKEY);
TxoutType which_type_tx_multisig;
const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, std::nullopt, which_type_tx_multisig);
const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, kernel::MemPoolOptions{.permit_bare_multisig = true}, which_type_tx_multisig);
assert(is_standard_tx_multisig);
assert(which_type_tx_multisig == TxoutType::MULTISIG);

View File

@ -6,6 +6,7 @@
#include <compressor.h>
#include <core_io.h>
#include <core_memusage.h>
#include <kernel/mempool_options.h>
#include <key_io.h>
#include <policy/policy.h>
#include <pubkey.h>
@ -53,7 +54,7 @@ FUZZ_TARGET(script, .init = initialize_script)
}
TxoutType which_type;
bool is_standard_ret = IsStandard(script, std::nullopt, which_type);
bool is_standard_ret = IsStandard(script, kernel::MemPoolOptions{}, which_type);
if (!is_standard_ret) {
assert(which_type == TxoutType::NONSTANDARD ||
which_type == TxoutType::NULL_DATA ||

View File

@ -9,6 +9,7 @@
#include <consensus/validation.h>
#include <core_io.h>
#include <core_memusage.h>
#include <kernel/mempool_options.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <primitives/transaction.h>
@ -57,10 +58,9 @@ FUZZ_TARGET(transaction, .init = initialize_transaction)
Assert(res == state_with_dupe_check.IsValid());
}
const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE};
std::string reason;
const bool is_standard_with_permit_bare_multisig = IsStandardTx(tx, std::nullopt, /* permit_bare_multisig= */ true, dust_relay_fee, reason);
const bool is_standard_without_permit_bare_multisig = IsStandardTx(tx, std::nullopt, /* permit_bare_multisig= */ false, dust_relay_fee, reason);
const bool is_standard_with_permit_bare_multisig = IsStandardTx(tx, kernel::MemPoolOptions{.permit_bare_multisig = true}, reason);
const bool is_standard_without_permit_bare_multisig = IsStandardTx(tx, kernel::MemPoolOptions{.permit_bare_multisig = false}, reason);
if (is_standard_without_permit_bare_multisig) {
assert(is_standard_with_permit_bare_multisig);
}

View File

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <kernel/mempool_options.h>
#include <key.h>
#include <policy/policy.h>
#include <script/interpreter.h>
@ -143,7 +144,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
const auto is_standard{[](const CScript& spk) {
TxoutType type;
bool res{::IsStandard(spk, std::nullopt, type)};
bool res{::IsStandard(spk, kernel::MemPoolOptions{.max_datacarrier_bytes = std::nullopt}, type)};
if (res) {
BOOST_CHECK_EQUAL(type, TxoutType::MULTISIG);
}

View File

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/tx_verify.h>
#include <kernel/mempool_options.h>
#include <key.h>
#include <policy/policy.h>
#include <policy/settings.h>
@ -20,13 +21,20 @@
// Helpers:
static bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason)
{
return IsStandardTx(tx, std::nullopt, permit_bare_multisig, CFeeRate{DUST_RELAY_TX_FEE}, reason);
const kernel::MemPoolOptions opts{
.permit_bare_multisig = permit_bare_multisig,
};
return IsStandardTx(tx, opts, reason);
}
static bool IsStandardTx(const CTransaction& tx, std::string& reason)
{
return IsStandardTx(tx, std::nullopt, /*permit_bare_multisig=*/true, CFeeRate{DUST_RELAY_TX_FEE}, reason) &&
IsStandardTx(tx, std::nullopt, /*permit_bare_multisig=*/false, CFeeRate{DUST_RELAY_TX_FEE}, reason);
kernel::MemPoolOptions opts{
.permit_bare_multisig = true,
};
if (!IsStandardTx(tx, opts, reason)) return false;
opts.permit_bare_multisig = false;
return IsStandardTx(tx, opts, reason);
}
static std::vector<unsigned char> Serialize(const CScript& s)

View File

@ -12,6 +12,7 @@
#include <consensus/tx_check.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <kernel/mempool_options.h>
#include <key.h>
#include <policy/policy.h>
#include <policy/settings.h>
@ -44,8 +45,7 @@ using util::ToString;
typedef std::vector<unsigned char> valtype;
static CFeeRate g_dust{DUST_RELAY_TX_FEE};
static bool g_bare_multi{DEFAULT_PERMIT_BAREMULTISIG};
static kernel::MemPoolOptions g_mempool_opts;
static std::map<std::string, unsigned int> mapFlagNames = {
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
@ -798,19 +798,19 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
constexpr auto CheckIsStandard = [](const auto& t) {
std::string reason;
BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, g_dust, reason));
BOOST_CHECK(IsStandardTx(CTransaction{t}, g_mempool_opts, reason));
BOOST_CHECK(reason.empty());
};
constexpr auto CheckIsNotStandard = [](const auto& t, const std::string& reason_in) {
std::string reason;
BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, g_dust, reason));
BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_mempool_opts, reason));
BOOST_CHECK_EQUAL(reason_in, reason);
};
CheckIsStandard(t);
// Check dust with default relay fee:
CAmount nDustThreshold = 182 * g_dust.GetFeePerK() / 1000;
CAmount nDustThreshold = 182 * g_mempool_opts.dust_relay_feerate.GetFeePerK() / 1000;
BOOST_CHECK_EQUAL(nDustThreshold, 546);
// dust:
t.vout[0].nValue = nDustThreshold - 1;
@ -838,14 +838,14 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
// Check dust with odd relay fee to verify rounding:
// nDustThreshold = 182 * 3702 / 1000
g_dust = CFeeRate(3702);
g_mempool_opts.dust_relay_feerate = CFeeRate(3702);
// dust:
t.vout[0].nValue = 674 - 1;
CheckIsNotStandard(t, "dust");
// not dust:
t.vout[0].nValue = 674;
CheckIsStandard(t);
g_dust = CFeeRate{DUST_RELAY_TX_FEE};
g_mempool_opts.dust_relay_feerate = CFeeRate{DUST_RELAY_TX_FEE};
t.vout[0].scriptPubKey = CScript() << OP_1;
CheckIsNotStandard(t, "scriptpubkey");
@ -958,15 +958,15 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
CheckIsNotStandard(t, "tx-size");
// Check bare multisig (standard if policy flag g_bare_multi is set)
g_bare_multi = true;
g_mempool_opts.permit_bare_multisig = true;
t.vout[0].scriptPubKey = GetScriptForMultisig(1, {key.GetPubKey()}); // simple 1-of-1
t.vin.resize(1);
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(65, 0);
CheckIsStandard(t);
g_bare_multi = false;
g_mempool_opts.permit_bare_multisig = false;
CheckIsNotStandard(t, "bare-multisig");
g_bare_multi = DEFAULT_PERMIT_BAREMULTISIG;
g_mempool_opts.permit_bare_multisig = DEFAULT_PERMIT_BAREMULTISIG;
// Check compressed P2PK outputs dust threshold (must have leading 02 or 03)
t.vout[0].scriptPubKey = CScript() << std::vector<unsigned char>(33, 0x02) << OP_CHECKSIG;

View File

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