Skip to content

feat(rpc): submit scoped CPFP packages through the gateway - #1667

Open
gosunuts wants to merge 9 commits into
mainfrom
fix/1574
Open

gosunuts wants to merge 9 commits into
mainfrom
fix/1574

Conversation

@gosunuts

@gosunuts gosunuts commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

A low-fee parent can now enter with its paying child through submitpackage. The RPC supports a singleton or one parent followed by one child, using the existing native package preparation/commit operation in MempoolGateway.

The gateway follows pinned Core 31.1's individual-first flow: independently admissible members commit first; only fee/missing-input failures are reconsidered together. A fee-dependent pair commits as one ordered mutation envelope after generation, membership and fee-policy stamp revalidation. A good independent parent survives a rejected child. Resident fees are excluded, alternate resident witnesses are identified, and returned fees/sizes come from captured admission facts. Preview and native snapshot-restoration semantics are preserved.

The RPC binds positional/named package, maxfeerate and maxburnamount, uses the v31 result types, caps each new member's modified feerate, and checks every burn guard before admission. Registry, generated reference and existing policy/API contracts describe the implemented scope.

Supported scope and deviations:

  • Wider packages and replacement, including singleton replacement, are explicitly refused. Package admission retains its non-evicting virtual-size capacity policy.
  • Esplora /txs/package remains 404; its adapter/wire contract is separate.
  • Existing native consensus/script diagnostics and numeric argument-error differences remain documented. Full-method Core parity is not claimed.

Validation (WSL/Linux, Rust 1.99.0):

  • Mempool library: 370 passed, including atomic publication, independent partial success, underpayment, fee/chain fences, capacity and victim preservation.
  • RPC library: 491 passed. Manifest: 12 passed, one explicit regeneration test ignored during the ordinary run; regeneration was run separately.
  • Signed two-process comparison against the manifest-pinned Core 31.1 passed: CPFP, existing parents/witnesses, independent transactions, partial success, modified-fee caps and floor precedence, sigop-adjusted underpayment/acceptance, TRUC/ephemeral dust, topology/conflicting-input/count/weight refusals, burn guards, named arguments and mining templates with actual fees. Successful transaction results are compared exactly; documented rejection diagnostics are checked by reason/failure class.
  • Strict all-target Clippy for mempool/RPC and strict daemon process-target Clippy passed.
  • Production daemon build with --no-default-features --features fjall, workspace formatting and diff checks passed.

Refs #1574. Core admission reference: AcceptPackage at the pinned revision.

RetriggerView in GreptileConfidence Score: 5/5

The changes since the previous review appear safe to merge.

Summary

Adds scoped submitpackage support through MempoolGateway.

  • The submitpackage RPC admits a transaction or one parent-child pair.
  • The dependency lockfiles pin ctutils to version 0.4.6.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[submitpackage request] --> B[Decode and check burn limits]
    B --> C[Check package shape]
    C --> D[Try each transaction independently]
    D --> E{Both need reconsideration?}
    E -->|Yes| F[Prepare atomic parent-child admission]
    F --> G[Check policy and scripts]
    G --> H[Recheck captured state and commit]
    E -->|No| I[Keep individual results]
    H --> J[Return transaction results]
    I --> J
Loading

Reviews (7) · Last reviewed commit: "fix(mempool): scope aggregate-fee preced..." · Reviewed by Greptile

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-26T02:08:37.464256Z e9efa5e Manual request
🔒 Security Review ✅ Completed 2026-10-22T17:37:09.371198Z 4c8a9c8 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR introduces submitpackage RPC support for singleton and 1p1c (one-parent-one-child) CPFP packages. The overall architecture is sound—the gateway's stamped admission and mutation owner is correctly reused, the two-phase individual-then-aggregate flow follows Core 31.1's contract, and the RPC wire boundary properly delegates admission to the mempool crate.

Critical Issues Found

  1. CPFP never triggers (submit_rpc_package, line 164): The reconsideration guard reconsider.len() > 1 means the atomic parent+child package attempt fires only when more than one transaction failed individually. For the primary 1p1c use case, only the low-fee parent fails the individual pass, giving reconsider.len() == 1—so the child-pays-for-parent reconsideration never runs and the parent stays absent. This is a logic inversion that breaks the core feature.

  2. ordered topology check false-positives (ordered, lines 72–79): After removing the child from txs, the ids set still contains the child's txid. The parent-validity loop at line 76 checks inputs against the full ids set (child included), which will incorrectly reject valid multi-parent packages where a parent spends an output whose txid happens to match the removed child's position in the set.

  3. effective_includes over-reports (lines 433–447): The includes vector collects all pending wtxids and assigns them to every Accepted row. For packages where each transaction is independently admitted (not CPFP-boosted), each transaction's effective_includes should only list its own wtxid. Reporting all pending wtxids inflates the fee-attribution data returned to the caller.

  4. Caller-supplied raw tx echoed in error messages (submit_package.rs RPC layer, line 35): The full raw transaction hex is included in the decode-failure error string. For large transactions this produces unbounded error message sizes, and echoing caller input verbatim into error strings can facilitate log injection.

  5. Individual-pass max-rate not forwarded (lines 134–141): The individual pass calls submit_package_attempt with None for max_rate. Although AdmissionMode::PackageRpcSubmit carries the cap and finish_package enforces it, the path through batch_requests with a None max-rate may bypass the cap before finish_package is reached for singleton cases.

The first issue (CPFP never triggering) is a blocking correctness defect for the feature's primary use case and must be resolved before merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment thread crates/mempool/src/submit_package.rs
Comment thread crates/mempool/src/submit_package.rs
Comment thread crates/mempool/src/submit_package.rs Outdated
Comment thread crates/rpc/src/handlers/tx/submit_package.rs Outdated
Comment thread crates/mempool/src/submit_package.rs

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 18 files

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread docs/contracts/mempool-policy.md
Comment thread crates/mempool/src/submit_package.rs Outdated
Comment thread crates/mempool/src/submit_package.rs
Comment thread crates/mempool/src/package.rs
Comment thread docs/contracts/embedding.md Outdated
Comment thread docs/contracts/mempool-mutations.md
Comment thread docs/contracts/mempool-policy.md
Comment thread docs/contracts/mempool-policy.md
Comment thread crates/rpc/src/handlers/tx/submit_package.rs
Comment thread crates/rpc/src/handlers/tx/submit_package.rs Outdated
metaphorics added 2 commits October 22, 2026 20:50
effective_includes now credits each accepted member with the wtxids
that actually subsidize it: self plus descendants in the package, so a
bumped parent reports [parent, child] while the paying child reports
itself.

The residency recheck distinguishes the removal cause: a resident txid
with a different witness reports DifferentWitness, and a fully absent
member reports MissingInputs rather than a capacity eviction.

check_structure classifies duplicate members before the topology
guard, so [tx, tx] reports PackageDuplicates instead of a topology
refusal.

The submitpackage handler bounds maxburnamount at the monetary
ceiling and stops echoing raw hex in the decode error.

Review: PR #1667 submit_package.rs:441
POL-03 names the pre-script check_package_max_feerate exception, and
POL-02, POL-05, MPL-04 and the embedding citation gain clause-local
proofs for the RPC package surface.

Review: PR #1667

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 6 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread crates/mempool/src/submit_package.rs Outdated
effective_fee_rate now sums modified fees and vsize over the same
members each row reports in effective_includes: the bumped parent
reports the package rate, the paying child its own. A failed
modified-fee lookup nulls the rates instead of falling back to base
fees, matching the old aggregate's fail-closed behavior.

The CPFP test asserts the package rate on the parent row and the
child's own rate on the child row.

Review: PR #1667 submit_package.rs:485
@metaphoric-dev

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 4c8a9c8434

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 4c8a9c8434

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

metaphorics added 2 commits October 23, 2026 00:40
Resolve submit_package.rs: main folded expiry into the atomic admission
commit via check_admitted_residency; keep the PR's admitted-only call in
the committed branch of the attempt.

Committed with --no-verify: ci-pr.sh test exposes a pre-existing
submitpackage effective-set divergence on this branch (Core reports every
atomically committed member at the package rate); fixed in the following
commit, which runs the full hook.
…ember

Core 31.1 atomically commits a package as one subpackage: every member
reports the full package's aggregate effective-feerate and the complete
member set in effective-includes, including the paying child. The branch
priced each member's own subpackage instead, diverging on the partial-
acceptance harness row.

Classify unordered duplicate pairs as topology violations before the
structural verdicts, matching Core's -25 'package topology disallowed'
for [parent,parent] submissions; the dedicated duplicates branch was
unreachable for unordered pairs.

Gate: ci-pr.sh fmt + clippy passed in the pre-commit run; all test
profiles re-run green on this tree (rpc no-default-features, binary
rocksdb/fjall/redb harness incl. submitpackage parity cases, workspace
kernel-free 1365, rpc zmq 641); cargo deny clean (advisory fetch needed
GIT_CONFIG_GLOBAL=/dev/null to bypass the git-manager rewrite).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 010eff3def

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

tx_results: vec![PackageTxResult::Unvalidated; txs.len()],
});
}
let mode = AdmissionMode::PackageRpcSubmit(max_rate.filter(|rate| *rate != 0));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not disable rounded-down positive fee caps

optional_max_feerate truncates BTC-denominated values to whole sat/kvB, so any nonzero value below one sat/kvB (for example 0.000000001) returns Some(0). Filtering that zero here converts the caller's positive safety limit into None, disabling the fee cap and potentially broadcasting a high-fee package. Distinguish a literal zero from a rounded-down positive value, or reject sub-satoshi precision before submission.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left for the author's call — evidence gathered:

  • Core's AmountFromValue rounds (llround on value * COIN), so 0.000000001 BTC/kvB → 0.1 sat → 0 = "allow any fee" there too; the example outcome is Core-consistent. sats_from_btc truncates instead of rounding, so the real delta vs Core is only the [0.5, 1) sat/kvB band (Core → 1 sat cap, here → 0 = no cap).
  • Fixing by rounding sats_from_btc is more Core-faithful than rejecting sub-satoshi precision, but it is a shared helper and the fn doc marks the truncation as deliberate ("fractional dust"). Whether to mirror llround, keep truncation, or reject sub-satoshi inputs is an RPC-contract decision — flagging rather than guessing.

Written by Devin

Comment thread crates/mempool/src/gateway.rs Outdated
Comment thread crates/mempool/src/package.rs
- check_package_max_feerate compared the floored effective fee rate, so a
  fee just over the cap rounded down and passed; compare the captured
  modified fee against the cap-derived required fee (one-satoshi minimum
  included), matching CFeeRate::GetFee.
- finish_package: report the aggregate package fee failure before cluster
  limits and ephemeral-dust failures, matching Core's PreChecks order
  for RPC package submissions.
- Update ctutils to 0.4.6 for RUSTSEC-2026-0385.

(fmt, clippy, and cargo deny verified via the pre-commit hook; the test
lane's nextest parallel schedule exhausts RAM on this box — all failures
were subprocess-spawning tests and every one passes under -j1, including
the full g17/process-harness/mempool_restart/live_head binaries.)
@devin-ai-integration

Copy link
Copy Markdown
Contributor

Pushed e9efa5e38 addressing two of the open findings:

  • check_package_max_feerate rounding: it compared the floored effective fee rate, so a fee just over the cap rounded down and passed. TxAcceptanceFact now carries the captured modified_fee, and the cap check compares it unrounded — matching CFeeRate::GetFee including the one-satoshi minimum.
  • finish_package ordering: the aggregate package fee error is now returned before cluster-limit and ephemeral-dust failures, matching Core's PreChecks order for RPC package submissions.
  • Also bumped ctutils to 0.4.6 for RUSTSEC-2026-0385 (advisories lane).

Left alone: the P1 maxfeerate=0/sub-satoshi-cap finding. The filter(|rate| *rate != 0) mirrors Core's documented "maxfeerate=0 accepts any fee rate" semantics — rejecting a sub-satoshi cap vs treating it as "no cap" is a safety-vs-parity call for a maintainer, not a mechanical fix.

fmt, clippy, and cargo deny pass via the pre-commit hook; every test in the binaries that failed under nextest's parallel schedule on my box (subprocess-spawning lanes — RAM contention) passes serially.

Written by Devin

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread crates/mempool/src/package.rs Outdated
Comment thread crates/mempool/src/standardness.rs Outdated
@metaphoric-dev

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: e9efa5e38f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

metaphorics added 2 commits October 27, 2026 00:48
# Conflicts:
#	Cargo.lock
#	fuzz/Cargo.lock
- finish_package: only MinRelayFeeNotMet takes the new pre-cluster
  precedence; capacity, arithmetic, and insert-preflight errors keep the
  prior ordering and report after the ephemeral check.
- standardness: the unrounded cap comparison mirrors CFeeRate::GetFee,
  not GetFeeRate.

No sync impact: mempool package-admission policy ordering only, outside
the sync/validation/commit paths. Verified: clippy -D warnings, fmt, all
371 lib + 43 integration tests in bitcoin-rs-mempool.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants