IsWellFormedPackage() enforces MAX_PACKAGE_WEIGHT by summing each transaction's weight:
const int64_t total_weight = std::accumulate(txns.cbegin(), txns.cend(), 0,
[](int64_t sum, const auto& tx) { return sum + GetTransactionWeight(*tx); });
if (package_count > 1 && total_weight > MAX_PACKAGE_WEIGHT) { ... "package-too-large" }
std::accumulate's accumulator type is the type of the init value. The init here is the literal 0, so the accumulator is an int: the 64-bit lambda result is narrowed back to 32 bits on every iteration. A package whose true combined weight exceeds INT_MAX therefore wraps to a small or negative value, and the total_weight > MAX_PACKAGE_WEIGHT check is silently bypassed.
This is the only place MAX_PACKAGE_WEIGHT is enforced. The two sibling weight/fee accumulations in the package-acceptance path already use a 64-bit init (int64_t{0} / CAmount{0}); this call site is the inconsistent one.
Impact
Limited and policy-only — not a consensus issue:
- Oversized transactions are still rejected individually by the per-transaction weight/standardness checks that run after
IsWellFormedPackage(), so this does not let over-weight transactions into the mempool. - The effect is that the package-level early rejection (
package-too-large) fails to fire for a package whose combined weight crossesINT_MAX, so the node does more work before rejecting. Such a package is reachable e.g. via thesubmitpackageRPC with sufficiently large transactions.
Still, the guard should do what it says, and relying on 32-bit truncation of a weight sum is fragile.
Fix
One line — initialise the accumulator with int64_t{0}.
Test
Adds package_weight_overflow_tests to txpackage_tests.cpp. Because triggering the wrap requires a combined weight above INT_MAX and MAX_PACKAGE_COUNT is 25, each transaction must be large. To avoid allocating 25 separate large transactions, the test reuses one ~27 MB transaction 25 times — the weight check runs before the duplicate-txid check, so the accumulator still sees 25 × weight. The test asserts the rejection reason is package-too-large; on the unfixed code the wrapped total bypasses the weight check and the reason is instead package-contains-duplicates. Happy to drop or slim the test if the maintainers prefer.
How to reproduce the mechanism
The truncation can be observed in isolation with a minimal program that mirrors the exact std::accumulate expression: 25 weights of ~90,000,000 sum to 2,250,000,000 (> INT_MAX); the int-accumulator version returns -2,044,967,296 (guard bypassed) while an int64_t accumulator returns the correct 2,250,000,000 (guard fires).