Replace MAX_PACKAGE_SIZE with MAX_PACKAGE_WEIGHT to avoid vbyte confusion

While allowing submitted packages to be slightly larger than what
may be allowed in the mempool to allow simpler reasoning
about contextual-less checks vs chain limits.
pull/28471/head
Greg Sanders 1 year ago
parent 3966b0a0b6
commit 533660c58a

@ -18,16 +18,19 @@ tip or some preceding transaction in the package.
The following rules are enforced for all packages: The following rules are enforced for all packages:
* Packages cannot exceed `MAX_PACKAGE_COUNT=25` count and `MAX_PACKAGE_SIZE=101KvB` total size * Packages cannot exceed `MAX_PACKAGE_COUNT=25` count and `MAX_PACKAGE_WEIGHT=404000` total weight
(#20833) (#20833)
- *Rationale*: This is already enforced as mempool ancestor/descendant limits. If - *Rationale*: We want package size to be as small as possible to mitigate DoS via package
transactions in a package are all related, exceeding this limit would mean that the package validation. However, we want to make sure that the limit does not restrict ancestor
can either be split up or it wouldn't pass individual mempool policy. packages that would be allowed if submitted individually.
- Note that, if these mempool limits change, package limits should be reconsidered. Users may - Note that, if these mempool limits change, package limits should be reconsidered. Users may
also configure their mempool limits differently. also configure their mempool limits differently.
- Note that the this is transaction weight, not "virtual" size as with other limits to allow
simpler context-less checks.
* Packages must be topologically sorted. (#20833) * Packages must be topologically sorted. (#20833)
* Packages cannot have conflicting transactions, i.e. no two transactions in a package can spend * Packages cannot have conflicting transactions, i.e. no two transactions in a package can spend

@ -23,10 +23,10 @@ bool CheckPackage(const Package& txns, PackageValidationState& state)
return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions"); return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-many-transactions");
} }
const int64_t total_size = std::accumulate(txns.cbegin(), txns.cend(), 0, const int64_t total_weight = std::accumulate(txns.cbegin(), txns.cend(), 0,
[](int64_t sum, const auto& tx) { return sum + GetVirtualTransactionSize(*tx); }); [](int64_t sum, const auto& tx) { return sum + GetTransactionWeight(*tx); });
// If the package only contains 1 tx, it's better to report the policy violation on individual tx size. // If the package only contains 1 tx, it's better to report the policy violation on individual tx weight.
if (package_count > 1 && total_size > MAX_PACKAGE_SIZE * 1000) { if (package_count > 1 && total_weight > MAX_PACKAGE_WEIGHT) {
return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large"); return state.Invalid(PackageValidationResult::PCKG_POLICY, "package-too-large");
} }

@ -15,18 +15,22 @@
/** Default maximum number of transactions in a package. */ /** Default maximum number of transactions in a package. */
static constexpr uint32_t MAX_PACKAGE_COUNT{25}; static constexpr uint32_t MAX_PACKAGE_COUNT{25};
/** Default maximum total virtual size of transactions in a package in KvB. */ /** Default maximum total weight of transactions in a package in weight
static constexpr uint32_t MAX_PACKAGE_SIZE{101}; to allow for context-less checks. This must allow a superset of sigops
static_assert(MAX_PACKAGE_SIZE * WITNESS_SCALE_FACTOR * 1000 >= MAX_STANDARD_TX_WEIGHT); weighted vsize limited transactions to not disallow transactions we would
have otherwise accepted individually. */
static constexpr uint32_t MAX_PACKAGE_WEIGHT = 404'000;
static_assert(MAX_PACKAGE_WEIGHT >= MAX_STANDARD_TX_WEIGHT);
// If a package is submitted, it must be within the mempool's ancestor/descendant limits. Since a // If a package is to be evaluated, it must be at least as large as the mempool's ancestor/descendant limits,
// submitted package must be child-with-unconfirmed-parents (all of the transactions are an ancestor // otherwise transactions that would be individually accepted may be rejected in a package erroneously.
// Since a submitted package must be child-with-unconfirmed-parents (all of the transactions are an ancestor
// of the child), package limits are ultimately bounded by mempool package limits. Ensure that the // of the child), package limits are ultimately bounded by mempool package limits. Ensure that the
// defaults reflect this constraint. // defaults reflect this constraint.
static_assert(DEFAULT_DESCENDANT_LIMIT >= MAX_PACKAGE_COUNT); static_assert(DEFAULT_DESCENDANT_LIMIT >= MAX_PACKAGE_COUNT);
static_assert(DEFAULT_ANCESTOR_LIMIT >= MAX_PACKAGE_COUNT); static_assert(DEFAULT_ANCESTOR_LIMIT >= MAX_PACKAGE_COUNT);
static_assert(DEFAULT_ANCESTOR_SIZE_LIMIT_KVB >= MAX_PACKAGE_SIZE); static_assert(MAX_PACKAGE_WEIGHT >= DEFAULT_ANCESTOR_SIZE_LIMIT_KVB * WITNESS_SCALE_FACTOR * 1000);
static_assert(DEFAULT_DESCENDANT_SIZE_LIMIT_KVB >= MAX_PACKAGE_SIZE); static_assert(MAX_PACKAGE_WEIGHT >= DEFAULT_DESCENDANT_SIZE_LIMIT_KVB * WITNESS_SCALE_FACTOR * 1000);
/** A "reason" why a package was invalid. It may be that one or more of the included /** A "reason" why a package was invalid. It may be that one or more of the included
* transactions is invalid or the package itself violates our rules. * transactions is invalid or the package itself violates our rules.
@ -47,7 +51,7 @@ class PackageValidationState : public ValidationState<PackageValidationResult> {
/** Context-free package policy checks: /** Context-free package policy checks:
* 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT. * 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT.
* 2. The total virtual size cannot exceed MAX_PACKAGE_SIZE. * 2. The total weight cannot exceed MAX_PACKAGE_WEIGHT.
* 3. If any dependencies exist between transactions, parents must appear before children. * 3. If any dependencies exist between transactions, parents must appear before children.
* 4. Transactions cannot conflict, i.e., spend the same inputs. * 4. Transactions cannot conflict, i.e., spend the same inputs.
*/ */

@ -51,14 +51,14 @@ BOOST_FIXTURE_TEST_CASE(package_sanitization_tests, TestChain100Setup)
BOOST_CHECK_EQUAL(state_too_many.GetResult(), PackageValidationResult::PCKG_POLICY); BOOST_CHECK_EQUAL(state_too_many.GetResult(), PackageValidationResult::PCKG_POLICY);
BOOST_CHECK_EQUAL(state_too_many.GetRejectReason(), "package-too-many-transactions"); BOOST_CHECK_EQUAL(state_too_many.GetRejectReason(), "package-too-many-transactions");
// Packages can't have a total size of more than 101KvB. // Packages can't have a total weight of more than 404'000WU.
CTransactionRef large_ptx = create_placeholder_tx(150, 150); CTransactionRef large_ptx = create_placeholder_tx(150, 150);
Package package_too_large; Package package_too_large;
auto size_large = GetVirtualTransactionSize(*large_ptx); auto size_large = GetTransactionWeight(*large_ptx);
size_t total_size{0}; size_t total_weight{0};
while (total_size <= MAX_PACKAGE_SIZE * 1000) { while (total_weight <= MAX_PACKAGE_WEIGHT) {
package_too_large.push_back(large_ptx); package_too_large.push_back(large_ptx);
total_size += size_large; total_weight += size_large;
} }
BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT); BOOST_CHECK(package_too_large.size() <= MAX_PACKAGE_COUNT);
PackageValidationState state_too_large; PackageValidationState state_too_large;
@ -122,7 +122,7 @@ BOOST_FIXTURE_TEST_CASE(package_validation_tests, TestChain100Setup)
// A single, giant transaction submitted through ProcessNewPackage fails on single tx policy. // A single, giant transaction submitted through ProcessNewPackage fails on single tx policy.
CTransactionRef giant_ptx = create_placeholder_tx(999, 999); CTransactionRef giant_ptx = create_placeholder_tx(999, 999);
BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > MAX_PACKAGE_SIZE * 1000); BOOST_CHECK(GetVirtualTransactionSize(*giant_ptx) > DEFAULT_ANCESTOR_SIZE_LIMIT_KVB * 1000);
auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /*test_accept=*/true); auto result_single_large = ProcessNewPackage(m_node.chainman->ActiveChainstate(), *m_node.mempool, {giant_ptx}, /*test_accept=*/true);
BOOST_CHECK(result_single_large.m_state.IsInvalid()); BOOST_CHECK(result_single_large.m_state.IsInvalid());
BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX); BOOST_CHECK_EQUAL(result_single_large.m_state.GetResult(), PackageValidationResult::PCKG_TX);

Loading…
Cancel
Save