feat: introduce TxTemplate as an intermediate stage - #73
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
27084ae to
e9c2962
Compare
e9c2962 to
de8bfc5
Compare
78bb570 to
0f1c8b2
Compare
noahjoeris
left a comment
There was a problem hiding this comment.
cACK 0f1c8b2
Nice improvements, thanks.
Pure rename — same struct, same methods, same parameters. No behaviour change. The next commit adds the resolved tx-shape fields (version, lock_time, fallback_sequence), the corresponding setters, and the PSBT/AFS pipeline that consumes them. Selection -> TxTemplate Selection::new -> TxTemplate::from_parts (still pub(crate)) IntoSelectionError -> IntoTxTemplateError InputCandidates::into_selection -> into_tx_template Selector::try_finalize() -> Option<TxTemplate> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes bitcoindevkit#57. TxTemplate now owns the resolved tx-shape fields and the methods that mutate them. The selector hands you a TxTemplate already configured with sensible defaults; everything else is method calls on it. New fields on TxTemplate: - version (default V2) - lock_time (= max(input CLTV) or ZERO) - fallback_sequence (default ENABLE_RBF_NO_LOCKTIME) New setters with validation: - set_version -> SetVersionError::RelativeTimelockRequiresV2 - set_locktime -> SetLockTimeError::{BelowInputCltv, UnitMismatch} - set_fallback_sequence The PSBT/AFS pipeline is restructured around these fields: - PsbtParams -> PsbtBuildParams (PSBT-only knobs; version/locktime /AFS removed) - CreatePsbtError -> BuildPsbtError - create_psbt(params) -> (Psbt, Finalizer) (was just Psbt) - anti-fee-sniping moves off PsbtParams::anti_fee_sniping into TxTemplate::apply_anti_fee_sniping(tip, &mut rng), a separate chainable step that composes the public set_locktime / Input::set_sequence - to_unsigned_tx() materializes the tx for non-PSBT signing flows Chain ergonomics: sort_inputs_by / shuffle_inputs (etc.) now consume self and return Self. into_finalizer is dropped — Finalizer comes from create_psbt or from Finalizer::new for callers that want it standalone. What was previously silent is now an explicit error: - min_locktime of the wrong unit was silently ignored - min_locktime below an input's CLTV was silently clamped up Both now error via SetLockTimeError. Setting v < 2 with a relative- timelock input errors via SetVersionError. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
create_psbt no longer needs an RNG (AFS — the only consumer — takes its own rng explicitly), so the create_psbt_with_rng wrapper and its thread_rng() call were dead weight. Collapses both into a single create_psbt(self, params) and moves rand to dev-dependencies. The library now depends only on rand_core (for the RngCore trait) + miniscript + bdk_coin_select. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Settle on the "build" verb so the method, params, and error type agree: create_psbt -> build_psbt, PsbtBuildParams -> BuildPsbtParams (also fixing the word order). Move BuildPsbtParams/BuildPsbtError into a new build_psbt module; the build_psbt method stays inherent on TxTemplate since it touches private fields. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fee-sniping apply_anti_fee_sniping now consumes the template and returns a SealedTxTemplate exposing only reads + emission, so version/locktime/sequence/ordering can't be changed after AFS. TxTemplate wraps SealedTxTemplate and derefs to it for the shared read/emit surface; the free afs helper mutates &mut self in place and the method does the sealing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0e7d063 to
d5ef9f6
Compare
|
I'm not sold on the idea of TxTemplate I think it adds a layer of indirection with little benefit. |
|
@ValuedMammal Could you be a bit more specific on where you think the proposed API is problematic? Which costs are you weighing? |
|
I agree with the merits of this change. It detaches AFS, version, locktime and sequence from the PSBT creation, and TxTemplate ensures the invariants for a single party. |
Oh, the impression that TxTemplate is an indirection may have come from it being proposed as an intermediate stage; I can see that TxTemplate replaced the Selection type. The issue cites the anti-fee-sniping logic as an awkward API, but so far the shape of the API hasn't prevented me from creating PSBTs, so I don't know if I'm missing something. I'm also not satisfied with the AFS logic, but for different reasons
|
Description
Closes #57.
The transaction-building API had grown awkward, and a revisit traced the awkwardness back to how anti-fee-sniping (AFS) was bolted on. The concrete symptoms:
create_psbtmeant everyone had to reckon with two extra error variants and an extra input.PsbtParamswas doing two jobs. It carried both bitcoin-transaction-shape fields (version,min_locktime, sequence) and PSBT-specific emission options. Because AFS depended on those tx-shape fields, it was structurally pinned inside the PSBT step and couldn't move out.The right place for AFS is before PSBT creation — it decides tx fields, then those feed into emission. That mirrors Bitcoin Core, where
DiscourageFeeSnipingruns as a step before the transaction is built. But AFS couldn't be moved without first untangling the tx-shape fields fromPsbtParams— i.e. an architectural change, not a local one.This PR introduces that change: a
TxTemplatestage that sits betweenSelectorandPsbtand owns the fully-resolved tx shape (version, locktime, fallback sequence, per-input sequence, ordering).Changelog notice
SelectiontoTxTemplate, now the single workspace for transaction shaping (version, locktime, fallback sequence, per-input sequence, ordering, anti-fee-sniping, emission).Selector::try_finalizenow returnsOption<TxTemplate>;InputCandidates::into_selectionis nowinto_tx_template, returningResult<TxTemplate, IntoTxTemplateError>(wasIntoSelectionError).PsbtParamsintoBuildPsbtParams(emission-only). Tx-shape options moved toTxTemplatesetters:set_version,set_locktime,set_fallback_sequence,apply_anti_fee_sniping.apply_anti_fee_snipingconsumes theTxTemplateand returns a newSealedTxTemplatethat exposes only reads and emission, so the tx shape cannot be mutated after AFS.TxTemplatederefs toSealedTxTemplate.build_psbt(renamed fromcreate_psbt) and returns(Psbt, Finalizer); its params/error areBuildPsbtParams/BuildPsbtError.shuffle_inputsis now consuming (returnsSelf).min_locktimewas silently ignored and a below-CLTV value silently clamped;set_locktimenow returnsSetLockTimeError::UnitMismatch/BelowInputCltv, andset_versionreturnsSetVersionError::RelativeTimelockRequiresV2. The hardcodedENABLE_RBF_NO_LOCKTIMEfallback sequence is now configurable viaset_fallback_sequence(default unchanged).Before submitting