diff --git a/src/input_candidates.rs b/src/input_candidates.rs index d257877..131f6af 100644 --- a/src/input_candidates.rs +++ b/src/input_candidates.rs @@ -1,6 +1,5 @@ use alloc::{vec, vec::Vec}; use core::fmt; -use core::ops::Deref; use bdk_coin_select::{metrics::LowestFee, Candidate, NoBnbSolution}; use bitcoin::{absolute, FeeRate, OutPoint}; @@ -16,19 +15,14 @@ use crate::{ #[must_use] #[derive(Debug, Clone)] pub struct InputCandidates { - contains: HashSet, + /// Pre-selected input group that is included before optional candidates. must_select: Option, + /// Optional input groups that coin selection may add. can_select: Vec, + /// Cached coin-select candidate metadata, kept in the same order as [`Self::groups`]. cs_candidates: Vec, -} - -fn cs_candidate_from_group(group: &InputGroup) -> Candidate { - Candidate { - value: group.value().to_sat(), - weight: group.weight(), - input_count: group.input_count(), - is_segwit: group.is_segwit(), - } + /// Cached outpoints used for deduplication and O(1) membership checks. + contains: HashSet, } impl InputCandidates { @@ -53,10 +47,10 @@ impl InputCandidates { .collect::>(); let cs_candidates = Self::build_cs_candidates(&must_select, &can_select); InputCandidates { - contains, must_select, can_select, cs_candidates, + contains, } } @@ -67,7 +61,12 @@ impl InputCandidates { must_select .iter() .chain(can_select) - .map(cs_candidate_from_group) + .map(|group| Candidate { + value: group.value().to_sat(), + weight: group.weight(), + input_count: group.input_count(), + is_segwit: group.is_segwit(), + }) .collect() } @@ -76,7 +75,7 @@ impl InputCandidates { self.groups().flat_map(InputGroup::inputs) } - /// Consume and iterate over all conatined inputs of all groups. + /// Consume and iterate over all contained inputs of all groups. pub fn into_inputs(self) -> impl Iterator { self.into_groups().flat_map(InputGroup::into_inputs) } @@ -91,17 +90,17 @@ impl InputCandidates { self.must_select.into_iter().chain(self.can_select) } - /// Can select + /// Inputs that coin selection may choose from. pub fn can_select(&self) -> &[InputGroup] { &self.can_select } - /// Must select + /// Inputs that must be selected, if any. pub fn must_select(&self) -> Option<&InputGroup> { self.must_select.as_ref() } - /// cs candidates + /// Cached candidate metadata used by `bdk_coin_select`. pub fn coin_select_candidates(&self) -> &Vec { &self.cs_candidates } @@ -160,10 +159,10 @@ impl InputCandidates { let no_dup = self.contains; Self { - contains: no_dup, must_select, can_select, cs_candidates, + contains: no_dup, } } @@ -241,51 +240,6 @@ impl fmt::Display for IntoSelectionError { #[cfg(feature = "std")] impl std::error::Error for IntoSelectionError {} -/// Occurs when we are missing outputs. -#[derive(Debug)] -pub struct MissingOutputs(HashSet); - -impl Deref for MissingOutputs { - type Target = HashSet; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl fmt::Display for MissingOutputs { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // TODO: should not use fmt::Debug on Display - write!(f, "missing outputs: {:?}", self.0) - } -} - -#[cfg(feature = "std")] -impl std::error::Error for MissingOutputs {} - -/// Occurs when a must-select policy cannot be fulfilled. -#[derive(Debug)] -pub enum PolicyFailure { - /// Missing outputs. - MissingOutputs(MissingOutputs), - /// Policy failure. - PolicyFailure(PF), -} - -impl fmt::Display for PolicyFailure { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - PolicyFailure::MissingOutputs(err) => write!(f, "{err}"), - PolicyFailure::PolicyFailure(err) => { - write!(f, "policy failure: {err}") - } - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for PolicyFailure {} - /// Select for lowest fee with bnb pub fn selection_algorithm_lowest_fee_bnb( longterm_feerate: FeeRate,