SelectorParams mempool policy#47
Conversation
21d52e8 to
c98521b
Compare
noahjoeris
left a comment
There was a problem hiding this comment.
I think the policy checks should be integrated more directly into the normal transaction-building flow.
Right now the caller has to remember to do a separate post-build check:
let policy = MempoolPolicy { tip_height, tip_mtp };
policy.check_all(&selection, &tx)?;This is easy for callers to forget. It also asks them to provide both a Selection and a Transaction, even though the transaction should be derived from Selection + PsbtParams. A checked path should instead construct the transaction itself and validate exactly the transaction the crate is going to return.
Suggested shape:
-
Output checks in
SelectorParamsBuilderbuild()could validate against a default policy.build_with_policy(policy)could validate against a customMempoolPolicy.- For unchecked, e.g.
build_unchecked()
-
Final transaction checks should happen in
Selection.create_psbt(...)defaults to e.g. Core v30- Add
create_psbt_with_policy(policy)andcreate_psbt_unchecked() - This avoids a public
check_all(&Selection, &Transaction)API and makesSelectionTxMismatchunnecessary
-
So I think we should have some form of abstraction/config over policy.
- No hardcoded policy values but make them part of MempoolPolicy struct.
- Also chain-tip data is an unrelated runtime input that should be passed alongside, not embedded
- Use
MempoolPolicy::bitcoin_core_v30()as the default, for example. - Users should be able to pass a different policy if their node has different relay settings.
- Then this policy can be passed into both
SelectorParamsBuilder::build_with_policy(...)andSelection::create_psbt_with_policy(...).
Let me know what you think.
|
Thank you for the review. I agree with all of this. The two-layer split made sense when I wrote it, but you're right that the manual policy.check_all(&selection, &tx) step is invisible at the type level. Nothing prompts the caller to run it, and accepting both a Selection and a Transaction lets the two drift. I will fold the validation into construction and have create_psbt_with_policy build the tx it validates. |
c98521b to
97c1bfd
Compare
There was a problem hiding this comment.
Nice, thanks for updating 🔥
I’d wait now for a Concept ACK on this direction by @ValuedMammal before spending more time on details.
- One API point: I’d prefer to keep
create_psbt(...). It could either preserve the old unchecked behavior for compatibility, or become checked by default ascreate_psbt(params, tip)usingMempoolPolicy::default(). I think we should discuss that explicitly. Right now most examples moved tocreate_psbt_unchecked(...), which makes the unchecked path look like the normal path. - Also,
MempoolPolicyonly models part of Core v30 policy for now, so we should expect it to grow as more checks are added.
|
I did a first pass. Overall I like the idea and it appears to be well implemented. Biggest concern is the long term API stability as policies change and grow outdated. Pros
Cons
|
| pub fn is_standard_script(script: &Script) -> bool { | ||
| script.is_p2pk() | ||
| || script.is_p2pkh() | ||
| || script.is_p2sh() | ||
| || script.is_p2wpkh() | ||
| || script.is_p2wsh() | ||
| || script.is_p2tr() | ||
| || script.is_op_return() | ||
| } |
There was a problem hiding this comment.
We could collapse the segwit/taproot conditions to script.is_witness_program(), which should also cover pay-to-anchor?
| /// Output-side standardness checks (dust, `OP_RETURN`, standard script | ||
| /// types) are evaluated against `policy`. The dust feerate used by the | ||
| /// change-policy computation also defaults to `policy.dust_relay_feerate` | ||
| /// unless explicitly overridden via [`Self::dust_relay_feerate`]. |
There was a problem hiding this comment.
Confused why the dust-feerate of the params would override the MempoolPolicy when we're supposed to be validating the specified params against a chosen policy.
Description
Add a validated builder interface for
SelectorParamsthat catches dustoutputs, zero-value OP_RETURNs, oversized OP_RETURN aggregates, and
non-standard script types at construction time. Pair it with a
post-selection
MempoolPolicythat enforces transaction-level standardnesschecks (weight, locktime, version, min-relay-fee, input spendability,
witness stack limits, non-witness size), and fold that validation into PSBT construction so callers can't forget to run it.
Closes #41
Closes #49
Notes to the reviewers
SelectorParams::check_standardnessruns output-sidechecks before coin selection.
MempoolPolicyruns transaction-levelchecks after the Selection is built and the PSBT is materialized,
invoked from inside the validated
create_psbt_*methods.MempoolPolicy. All limits previously hardcoded(dust feerate, min relay feerate, OP_RETURN cap, max weight, allowed
versions, etc.) are now public fields, with
MempoolPolicy::bitcoin_core_v30()as the canonical preset and
Default. Chain state (tip_height,tip_mtp) is split out into a separateChainTipstruct passedper-call rather than baked into the policy.
change_dust_relay_feeratetodust_relay_feerate. The field applies to all outputs (target and change), not just change.Changelog notice
SelectorParamsBuilderwith chainable setters and three terminalmethods:
build(validates againstMempoolPolicy::default),build_with_policy(validates against a caller-supplied policy), andbuild_unchecked(skips validation).MempoolPolicywith configurable limits.ChainTipfor passing chain state to policy checks that need it.Selection::create_psbt_with_policyandSelection::create_psbt_uncheckedfor std callers, pluscreate_psbt_with_rng,create_psbt_with_policy_and_rng, andcreate_psbt_unchecked_with_rngforno_std. Each entry point isexplicit about which policy (if any) validates the constructed PSBT.
CreatePsbtError::Policy(MempoolPolicyError)variant with aFrom<MempoolPolicyError>conversion.SelectorParams::change_dust_relay_feeratetodust_relay_feerate. The field applies to all outputs (target andchange), not just change.
Selection::create_psbtin favor of the explicitcreate_psbt_with_policy/create_psbt_uncheckedpair. Existingcallers should migrate to
create_psbt_with_policy(params, &policy, tip)for validated PSBTs or
create_psbt_unchecked(params)to preserve theprior unchecked behavior.
Checklists