Skip to content

Commit

Permalink
Policy: Add acceptnonstddatacarrier option to reject non-standard dat…
Browse files Browse the repository at this point in the history
…acarrier regardless of size
  • Loading branch information
luke-jr committed Mar 15, 2024
1 parent 71d397f commit 09d7c5c
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 38 deletions.
4 changes: 4 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,10 @@ void SetupServerArgs(ArgsManager& argsman)

SetupChainParamsBaseOptions(argsman);

argsman.AddArg("-acceptnonstddatacarrier",
strprintf("Relay and mine non-OP_RETURN datacarrier injection (default: %u)",
DEFAULT_ACCEPT_NON_STD_DATACARRIER),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (default: %u)", DEFAULT_ACCEPT_NON_STD_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define cost of relay, used for mempool limiting and replacement policy. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
Expand Down
3 changes: 3 additions & 0 deletions src/kernel/mempool_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ static constexpr unsigned int DEFAULT_BLOCKSONLY_MAX_MEMPOOL_SIZE_MB{5};
static constexpr unsigned int DEFAULT_MEMPOOL_EXPIRY_HOURS{336};
/** Default for -mempoolreplacement; must update docs in init.cpp manually */
static constexpr RBFPolicy DEFAULT_MEMPOOL_RBF_POLICY{RBFPolicy::Always};
/** Default for -acceptnonstddatacarrier */
static constexpr bool DEFAULT_ACCEPT_NON_STD_DATACARRIER{false};
/** Default for -acceptnonstdtxn */
static constexpr bool DEFAULT_ACCEPT_NON_STD_TXN{false};

Expand Down Expand Up @@ -58,6 +60,7 @@ struct MemPoolOptions {
bool datacarrier_fullcount{DEFAULT_DATACARRIER_FULLCOUNT};
bool permit_bare_pubkey{DEFAULT_PERMIT_BAREPUBKEY};
bool permit_bare_multisig{DEFAULT_PERMIT_BAREMULTISIG};
bool accept_non_std_datacarrier{DEFAULT_ACCEPT_NON_STD_DATACARRIER};
bool require_standard{true};
RBFPolicy rbf_policy{DEFAULT_MEMPOOL_RBF_POLICY};
MemPoolLimits limits{};
Expand Down
1 change: 1 addition & 0 deletions src/node/mempool_args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
mempool_opts.max_datacarrier_bytes = std::nullopt;
}
mempool_opts.datacarrier_fullcount = argsman.GetBoolArg("-datacarrierfullcount", DEFAULT_DATACARRIER_FULLCOUNT);
mempool_opts.accept_non_std_datacarrier = argsman.GetBoolArg("-acceptnonstddatacarrier", DEFAULT_ACCEPT_NON_STD_DATACARRIER);

mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", DEFAULT_ACCEPT_NON_STD_TXN);

Expand Down
19 changes: 13 additions & 6 deletions src/policy/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <algorithm>
#include <cstddef>
#include <utility>
#include <vector>

unsigned int g_script_size_policy_limit{DEFAULT_SCRIPT_SIZE_POLICY_LIMIT};
Expand Down Expand Up @@ -431,17 +432,21 @@ std::pair<CScript, unsigned int> GetScriptForTransactionInput(CScript prevScript
return std::make_pair(CScript(), 0);
}

size_t DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view)
std::pair<size_t, size_t> DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view)
{
size_t ret{0};
std::pair<size_t, size_t> ret{0, 0};

for (const CTxIn& txin : tx.vin) {
const CTxOut &utxo = view.AccessCoin(txin.prevout).out;
auto[script, consensus_weight_per_byte] = GetScriptForTransactionInput(utxo.scriptPubKey, txin);
ret += script.DatacarrierBytes();
const auto dcb = script.DatacarrierBytes();
ret.first += dcb.first;
ret.second += dcb.second;
}
for (const CTxOut& txout : tx.vout) {
ret += txout.scriptPubKey.DatacarrierBytes();
const auto dcb = txout.scriptPubKey.DatacarrierBytes();
ret.first += dcb.first;
ret.second += dcb.second;
}

return ret;
Expand All @@ -457,12 +462,14 @@ int32_t CalculateExtraTxWeight(const CTransaction& tx, const CCoinsViewCache& vi
const CTxOut &utxo = view.AccessCoin(txin.prevout).out;
auto[script, consensus_weight_per_byte] = GetScriptForTransactionInput(utxo.scriptPubKey, txin);
if (weight_per_data_byte > consensus_weight_per_byte) {
mod_weight += script.DatacarrierBytes() * (weight_per_data_byte - consensus_weight_per_byte);
const auto dcb = script.DatacarrierBytes();
mod_weight += (dcb.first + dcb.second) * (weight_per_data_byte - consensus_weight_per_byte);
}
}
if (weight_per_data_byte > WITNESS_SCALE_FACTOR) {
for (const CTxOut& txout : tx.vout) {
mod_weight += txout.scriptPubKey.DatacarrierBytes() * (weight_per_data_byte - WITNESS_SCALE_FACTOR);
const auto dcb = txout.scriptPubKey.DatacarrierBytes();
mod_weight += (dcb.first + dcb.second) * (weight_per_data_byte - WITNESS_SCALE_FACTOR);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ static inline int64_t GetVirtualTransactionInputSize(const CTxIn& tx)

std::pair<CScript, unsigned int> GetScriptForTransactionInput(CScript prevScript, const CTxIn&);

size_t DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view);
std::pair<size_t, size_t> DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view);

int32_t CalculateExtraTxWeight(const CTransaction& tx, const CCoinsViewCache& view, const unsigned int weight_per_data_byte);

Expand Down
10 changes: 5 additions & 5 deletions src/script/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ bool CScript::HasValidOps() const
return true;
}

size_t CScript::DatacarrierBytes() const
std::pair<size_t, size_t> CScript::DatacarrierBytes() const
{
size_t counted{0};
opcodetype opcode, last_opcode{OP_INVALIDOPCODE};
Expand All @@ -294,17 +294,17 @@ size_t CScript::DatacarrierBytes() const
opcode_it = it;
if (!GetOp(it, opcode, push_data)) {
// Invalid scripts are necessarily all data
return size();
return {0, size()};
}

if (opcode == OP_IF || opcode == OP_NOTIF) {
++inside_conditional;
} else if (opcode == OP_ENDIF) {
if (!inside_conditional) return size(); // invalid
if (!inside_conditional) return {0, size()}; // invalid
--inside_conditional;
} else if (opcode == OP_RETURN && !inside_conditional) {
// unconditional OP_RETURN is unspendable
return size();
return {size(), 0};
}

// Match OP_FALSE OP_IF
Expand All @@ -330,7 +330,7 @@ size_t CScript::DatacarrierBytes() const
counted += it - data_began;
}
}
return counted;
return {0, counted};
}

bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet)
Expand Down
3 changes: 2 additions & 1 deletion src/script/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <stdint.h>
#include <string.h>
#include <string>
#include <utility>
#include <vector>

// Maximum number of bytes pushable to the stack
Expand Down Expand Up @@ -553,7 +554,7 @@ class CScript : public CScriptBase
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE);
}

size_t DatacarrierBytes() const;
std::pair<size_t, size_t> DatacarrierBytes() const;

void clear()
{
Expand Down
51 changes: 28 additions & 23 deletions src/test/script_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1468,28 +1468,33 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps)
BOOST_CHECK(!script.HasValidOps());
}

static const std::string DatacarrierBytesStr(const CScript &script) {
auto dcb = script.DatacarrierBytes();
return strprintf("%s+%s", dcb.first, dcb.second);
}

BOOST_AUTO_TEST_CASE(script_DataCarrierBytes)
{
using zeros = std::vector<unsigned char>;

// empty script
BOOST_CHECK_EQUAL(0, (CScript()).DatacarrierBytes());
BOOST_CHECK_EQUAL("0+0", DatacarrierBytesStr(CScript()));
// series of pushes are not data
BOOST_CHECK_EQUAL(0, (CScript() << OP_0 << OP_0 << OP_0).DatacarrierBytes());
BOOST_CHECK_EQUAL("0+0", DatacarrierBytesStr(CScript() << OP_0 << OP_0 << OP_0));
// unspendable if first op is OP_RETURN, then length(1), zeros(11)
BOOST_CHECK_EQUAL(13, (CScript() << OP_RETURN << zeros(11)).DatacarrierBytes());
BOOST_CHECK_EQUAL("13+0", DatacarrierBytesStr(CScript() << OP_RETURN << zeros(11)));
// invalid script (no data following PUSHDATA) makes it all data
BOOST_CHECK_EQUAL(2, (CScript() << OP_0 << OP_PUSHDATA4).DatacarrierBytes());
BOOST_CHECK_EQUAL("0+2", DatacarrierBytesStr(CScript() << OP_0 << OP_PUSHDATA4));
// no data here
BOOST_CHECK_EQUAL(0, (CScript() << OP_TRUE << OP_IF << OP_ENDIF).DatacarrierBytes());
BOOST_CHECK_EQUAL("0+0", DatacarrierBytesStr(CScript() << OP_TRUE << OP_IF << OP_ENDIF));
// specific data pattern, entire script is data
BOOST_CHECK_EQUAL(4, (CScript() << OP_FALSE << OP_IF << OP_7 << OP_ENDIF).DatacarrierBytes());
BOOST_CHECK_EQUAL("0+4", DatacarrierBytesStr(CScript() << OP_FALSE << OP_IF << OP_7 << OP_ENDIF));
// consecutive data
BOOST_CHECK_EQUAL(6, (CScript() << OP_FALSE << OP_IF << OP_ENDIF << OP_FALSE << OP_IF << OP_ENDIF).DatacarrierBytes());
BOOST_CHECK_EQUAL("0+6", DatacarrierBytesStr(CScript() << OP_FALSE << OP_IF << OP_ENDIF << OP_FALSE << OP_IF << OP_ENDIF));
// nested data (all is data)
BOOST_CHECK_EQUAL(6, (CScript() << OP_FALSE << OP_IF << OP_TRUE << OP_IF << OP_ENDIF << OP_ENDIF).DatacarrierBytes());
BOOST_CHECK_EQUAL("0+6", DatacarrierBytesStr(CScript() << OP_FALSE << OP_IF << OP_TRUE << OP_IF << OP_ENDIF << OP_ENDIF));
// pushing then immediately dropping is data: length(1), zero(11), OP_DROP
BOOST_CHECK_EQUAL(13, (CScript() << zeros(11) << OP_DROP).DatacarrierBytes());
BOOST_CHECK_EQUAL("0+13", DatacarrierBytesStr(CScript() << zeros(11) << OP_DROP));
}

BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
Expand All @@ -1504,7 +1509,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
BOOST_CHECK(ret_script == tx_in.scriptSig);
BOOST_CHECK_EQUAL(scale, WITNESS_SCALE_FACTOR);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2PKH - no datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1515,7 +1520,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
BOOST_CHECK(ret_script == tx_in.scriptSig);
BOOST_CHECK_EQUAL(scale, WITNESS_SCALE_FACTOR);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2SH - no datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1528,7 +1533,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
BOOST_CHECK(ret_script == redeem_script);
BOOST_CHECK_EQUAL(scale, WITNESS_SCALE_FACTOR);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2SH - with datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1543,7 +1548,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
BOOST_CHECK(ret_script == redeem_script);
BOOST_CHECK_EQUAL(scale, WITNESS_SCALE_FACTOR);
// OP_RETURN(1), length(1), zeros(27) = 29
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 29);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "29+0");
}
{ // P2WPKH - no datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1559,7 +1564,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
// should have no script at all since it's wrapped P2WPKH
BOOST_CHECK(ret_script == CScript());
BOOST_CHECK_EQUAL(scale, 0);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2WSH - no datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1575,7 +1580,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
BOOST_CHECK(ret_script == redeem_script);
BOOST_CHECK_EQUAL(scale, 1);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2WSH - some datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1592,7 +1597,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
BOOST_CHECK(ret_script == redeem_script);
BOOST_CHECK_EQUAL(scale, 1);
// OP_FALSE(1), OP_IF(1), length(1), zeros(10), OP_ENDIF(1)
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 14);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+14");
}
{ // P2SH-P2WPKH - no datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1607,7 +1612,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
BOOST_CHECK(ret_script == CScript());
// data bytes in the witness get discounted (*1 instead of *4)
BOOST_CHECK_EQUAL(scale, 0);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2SH-P2WSH - no datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1631,7 +1636,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
BOOST_CHECK(ret_script == witness_redeem_script);
// data bytes in the witness get discounted (*1 instead of *4)
BOOST_CHECK_EQUAL(scale, 1);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2SH-P2WSH - some datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1656,7 +1661,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
// data bytes in the witness get discounted (*1 instead of *4)
BOOST_CHECK_EQUAL(scale, 1);
// OP_FALSE(1), OP_IF(1), length(1), zeros(10), OP_ENDIF(1) = 14
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 14);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+14");
}
{ // P2TR keypath - no datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1668,7 +1673,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
BOOST_CHECK(ret_script == CScript());
BOOST_CHECK_EQUAL(scale, 0);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2TR keypath - annex but no script - no datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1682,7 +1687,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
BOOST_CHECK(ret_script == CScript());
BOOST_CHECK_EQUAL(scale, 0);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2TR scriptpath - no datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1701,7 +1706,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
BOOST_CHECK(ret_script == script);
BOOST_CHECK_EQUAL(scale, 1);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 0);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "0+0");
}
{ // P2TR scriptpath - some datacarrier bytes
CScript prev_script; // scriptPubKey
Expand All @@ -1719,7 +1724,7 @@ BOOST_AUTO_TEST_CASE(script_GetScriptForTransactionInput)
auto [ret_script, scale] = GetScriptForTransactionInput(prev_script, tx_in);
BOOST_CHECK(ret_script == script);
BOOST_CHECK_EQUAL(scale, 1);
BOOST_CHECK_EQUAL(ret_script.DatacarrierBytes(), 3);
BOOST_CHECK_EQUAL(DatacarrierBytesStr(ret_script), "3+0");
}
}

Expand Down
1 change: 1 addition & 0 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ CTxMemPool::CTxMemPool(const Options& opts)
m_permit_bare_multisig{opts.permit_bare_multisig},
m_max_datacarrier_bytes{opts.max_datacarrier_bytes},
m_datacarrier_fullcount{opts.datacarrier_fullcount},
m_accept_non_std_datacarrier{opts.accept_non_std_datacarrier},
m_require_standard{opts.require_standard},
m_rbf_policy{opts.rbf_policy},
m_limits{opts.limits}
Expand Down
1 change: 1 addition & 0 deletions src/txmempool.h
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ class CTxMemPool
bool m_permit_bare_multisig;
std::optional<unsigned> m_max_datacarrier_bytes;
bool m_datacarrier_fullcount;
bool m_accept_non_std_datacarrier;
bool m_require_standard;
RBFPolicy m_rbf_policy;

Expand Down
10 changes: 8 additions & 2 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,8 +931,14 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, reason);
}

if (m_pool.m_datacarrier_fullcount && (!ignore_rejects.count("txn-datacarrier-exceeded")) && DatacarrierBytes(tx, m_view) > m_pool.m_max_datacarrier_bytes.value_or(0)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "txn-datacarrier-exceeded");
if (m_pool.m_datacarrier_fullcount || !m_pool.m_accept_non_std_datacarrier) {
const auto dcb = DatacarrierBytes(tx, m_view);
if (dcb.second > 0 && !(m_pool.m_accept_non_std_datacarrier || ignore_rejects.count("txn-datacarrier-nonstandard"))) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "txn-datacarrier-nonstandard");
}
if (m_pool.m_datacarrier_fullcount && (!ignore_rejects.count("txn-datacarrier-exceeded")) && dcb.first + dcb.second > m_pool.m_max_datacarrier_bytes.value_or(0)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "txn-datacarrier-exceeded");
}
}

// Check for non-standard witnesses.
Expand Down

0 comments on commit 09d7c5c

Please sign in to comment.