- Breaking: Fee math is done in
f64instead off32.FeeRatestores anf64, andfrom_sat_per_vb,from_sat_per_wu,from_btc_per_kvb,as_sat_vbandspwutake or returnf64; so doCandidate::effective_value,value_pwu,effective_value_pwu,implied_feeandfee_per_value,CoinSelector::wasteandDrainWeights::waste.BnbMetric::scoreandbound,CoinSelector::run_bnbandbnb_solutionsuseOrdf64instead ofOrdf32, andFloatExtis implemented forf64.f32rounds by several sats at ~1 BTC values, which letLowestFee::boundexceed the true fee and prune the cheapest selection, and made fees of 2^24 sats or more score the same (#82). - Breaking: Every transaction is now priced as segwit, and
Candidate::is_segwitis removed.Candidate::weightis the input's segwit-serialized weight (TxIn::segwit_weight), so legacy inputs include their 1 WU empty witness. A transaction that spends only legacy inputs is overestimated by 2 WU plus 1 WU per input, and never undershoots the target feerate. The same margin applies toTarget::max_weight, so such a selection close to the cap can be rejected even though the real transaction fits. This fixesCoinSelector::input_weightundercounting candidates that group several legacy inputs in a segwit transaction. - Breaking:
Candidate::new(value, satisfaction_weight, is_segwit)is nowCandidate::new(value, satisfaction_weight), andsatisfaction_weightnow means the weight over an unsatisfiedTxIn::default(), which is what miniscript'sDescriptor::max_weight_to_satisfyreturns for every script type. Previously it was documented as including thescriptSiglength and thescriptWitnessstack item count. To migrate, passmax_weight_to_satisfy()?.to_wu()directly. If you compute it by hand, it is the satisfied input'sTxIn::segwit_weight()minusTxIn::default().segwit_weight(). - Breaking: Public weight constants changed value without changing type.
TXIN_BASE_WEIGHTis now 165 (was 164) and includes the stack item count of an emptyscriptWitness.TX_FIXED_FIELD_WEIGHTis now 34 (was 32) and includes the segwitmarkerandflag.TR_KEYSPEND_SATISFACTION_WEIGHTis now 65 (was 66) and excludes thescriptWitnessstack item count.TR_KEYSPEND_TXIN_WEIGHTis unchanged at 230. - Breaking:
CoinSelectornow owns itsTarget.CoinSelector::new(candidates, target)takes it andCoinSelector::target()returns it, and it is fixed for the life of the selector. Every method that took atarget: Targetargument no longer does, includingexcess,missing,rate_excess,implied_fee,is_funded,is_within_max_weight,drain,select_until_target_met,select_srd,run_bnb, andbnb_solutions. The same goes for arguments that merely restated part of the target:weightandimplied_feerateno longer takeTargetOutputs,feeno longer takestarget_value, andeffective_valueandselect_all_effectiveno longer take aFeeRate.BnbMetric's methods read the target from theCoinSelectorthey are given — they are nowfn score(&mut self, cs: &CoinSelector<'_>) -> Option<Ordf64>,fn bound(&mut self, cs: &CoinSelector<'_>) -> Option<Ordf64>andfn drain(&mut self, cs: &CoinSelector<'_>) -> Drain— soLowestFeeandChangelessno longer store atargetfield. This removes the target thatChangeless<M>previously had to keep in sync with its inner metric. To measure a selection against a second target, useCoinSelector::with_target(target), which copies the selection, the bans and the candidate order over to the new target;CoinSelector::newstarts from an empty selection. - Breaking:
BnbMetricmetrics now decide the change output themselves. The trait gains adrain(&mut self, cs) -> Drainmethod; call it on a branch-and-bound solution (or theLowestFeemetric directly) to get the change output the metric optimized against, instead of computing a separateChangePolicy. - Breaking:
CoinSelector::run_bnbnow returns(Ordf64, Drain)instead of justOrdf32, handing back the change output the metric decided on for the winning selection. - Breaking:
LowestFeeno longer takes achange_policy. It now takesdust_relay_feerate: FeeRateanddrain_weights: DrainWeights, and adds change only when doing so lowers the long-term fee and the change would not be dust. - Add
DrainWeights::dust_threshold(dust_relay_feerate), the minimum value a change output with these weights must have to not be dust. - Add
CoinSelector::select_srd, a Single Random Draw selector (port of Bitcoin Core'sSelectCoinsSRD) that adds candidates in random order until the change reacheschange_lower, producing a healthy-sized (privacy-friendly) change output instead of minimizing fees. Adds theCHANGE_LOWERconstant for Core's value. - Breaking:
Changelessis nowChangeless<M>, wrapping an inner metric it constrains to changeless solutions (e.g.Changeless<LowestFee>), replacing the previous tuple-composition approach. - Breaking: Removed the
BnbMetrictuple implementations (impl BnbMetric for ((A, f32), ...)). Weighted composition of independent metrics is no longer supported; the only composition still provided is the changeless constraint, now expressed asChangeless<M>. If you relied on tuples to blend multiple objectives, there is no drop-in replacement. - Breaking:
CoinSelector::selected_indicesandCoinSelector::bannednow return&Bitsetinstead of&BTreeSet<usize>.Bitsetexposescontains/len/is_empty/iter(#46) - Replace the internal
Cow<BTreeSet>/Cow<[usize]>selection state with aBitsetand anArc-shared candidate order, making the per-branch clones in branch-and-bound substantially cheaper (#46) - Fix compilation error when building with
--no-default-features(#36)
- Use
u64for weights instead of u32 - Fix feerate not being rounded up to vbytes #29
- Fix
new_tr_keyspendweight
- Remove
is_target_met_with_change_policy: it was redundant. If the target is met without a change policy it will always be met with it. - Remove
min_feein favour ofreplacewhich allows you to replace a transaction - Remove
Drainargument fromCoinSelector::select_until_target_metbecause adding a drain won't change when the target is met. - No more
base_weightinCoinSelector. Weight of the outputs is tracked intarget. - You now account for the number of outputs in both drain and target and their weight.
- Removed waste metric because it was pretty broken and took a lot to maintain