policy: uncap datacarrier by default

Datacarrier output script sizes and output counts are now
uncapped by default.

To avoid introducing another startup argument, we modify the
OP_RETURN accounting to "budget" the spk sizes.

If a user has set a custom default, this results in that
budget being spent over the sum of all OP_RETURN outputs'
scripts in the transaction, no longer capping the number
of OP_RETURN outputs themselves. This should allow a
superset of current behavior while respecting the passed
argument in terms of total arbitrary data storage.

Co-authored-by: Anthony Towns <aj@erisian.com.au>
This commit is contained in:
Greg Sanders
2025-05-02 10:01:13 -04:00
parent 4b1d48a686
commit 9f36962b07
10 changed files with 64 additions and 67 deletions

View File

@@ -797,14 +797,14 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
CKey key = GenerateRandomKey();
t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
constexpr auto CheckIsStandard = [](const auto& t) {
constexpr auto CheckIsStandard = [](const auto& t, const unsigned int max_op_return_relay = MAX_OP_RETURN_RELAY) {
std::string reason;
BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, g_dust, reason));
BOOST_CHECK(IsStandardTx(CTransaction{t}, max_op_return_relay, g_bare_multi, g_dust, reason));
BOOST_CHECK(reason.empty());
};
constexpr auto CheckIsNotStandard = [](const auto& t, const std::string& reason_in) {
constexpr auto CheckIsNotStandard = [](const auto& t, const std::string& reason_in, const unsigned int max_op_return_relay = MAX_OP_RETURN_RELAY) {
std::string reason;
BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, g_dust, reason));
BOOST_CHECK(!IsStandardTx(CTransaction{t}, max_op_return_relay, g_bare_multi, g_dust, reason));
BOOST_CHECK_EQUAL(reason_in, reason);
};
@@ -858,15 +858,13 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].scriptPubKey = CScript() << OP_1;
CheckIsNotStandard(t, "scriptpubkey");
// MAX_OP_RETURN_RELAY-byte TxoutType::NULL_DATA (standard)
// Custom 83-byte TxoutType::NULL_DATA (standard with max_op_return_relay of 83)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
CheckIsStandard(t);
BOOST_CHECK_EQUAL(83, t.vout[0].scriptPubKey.size());
CheckIsStandard(t, /*max_op_return_relay=*/83);
// MAX_OP_RETURN_RELAY+1-byte TxoutType::NULL_DATA (non-standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"_hex;
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
CheckIsNotStandard(t, "scriptpubkey");
// Non-standard if max_op_return_relay datacarrier arg is one less
CheckIsNotStandard(t, "datacarrier", /*max_op_return_relay=*/82);
// Data payload can be encoded in any way...
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ""_hex;
@@ -888,21 +886,28 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
CheckIsStandard(t);
// Only one TxoutType::NULL_DATA permitted in all cases
// Multiple TxoutType::NULL_DATA are permitted
t.vout.resize(2);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
t.vout[0].nValue = 0;
t.vout[1].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
t.vout[1].nValue = 0;
CheckIsNotStandard(t, "multi-op-return");
CheckIsStandard(t);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
CheckIsNotStandard(t, "multi-op-return");
CheckIsStandard(t);
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
CheckIsNotStandard(t, "multi-op-return");
CheckIsStandard(t);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
t.vout[1].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
const auto datacarrier_size = t.vout[0].scriptPubKey.size() + t.vout[1].scriptPubKey.size();
CheckIsStandard(t); // Default max relay should never trigger
CheckIsStandard(t, /*max_op_return_relay=*/datacarrier_size);
CheckIsNotStandard(t, "datacarrier", /*max_op_return_relay=*/datacarrier_size-1);
// Check large scriptSig (non-standard if size is >1650 bytes)
t.vout.resize(1);