Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 17 additions & 63 deletions src/input_candidates.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -16,19 +15,14 @@ use crate::{
#[must_use]
#[derive(Debug, Clone)]
pub struct InputCandidates {
contains: HashSet<OutPoint>,
/// Pre-selected input group that is included before optional candidates.
must_select: Option<InputGroup>,
/// Optional input groups that coin selection may add.
can_select: Vec<InputGroup>,
/// Cached coin-select candidate metadata, kept in the same order as [`Self::groups`].
cs_candidates: Vec<Candidate>,
}

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<OutPoint>,
}

impl InputCandidates {
Expand All @@ -53,10 +47,10 @@ impl InputCandidates {
.collect::<Vec<_>>();
let cs_candidates = Self::build_cs_candidates(&must_select, &can_select);
InputCandidates {
contains,
must_select,
can_select,
cs_candidates,
contains,
}
}

Expand All @@ -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()
}

Expand All @@ -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<Item = Input> {
self.into_groups().flat_map(InputGroup::into_inputs)
}
Expand All @@ -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<Candidate> {
&self.cs_candidates
}
Expand Down Expand Up @@ -160,10 +159,10 @@ impl InputCandidates {
let no_dup = self.contains;

Self {
contains: no_dup,
must_select,
can_select,
cs_candidates,
contains: no_dup,
}
}

Expand Down Expand Up @@ -241,51 +240,6 @@ impl<E: fmt::Display> fmt::Display for IntoSelectionError<E> {
#[cfg(feature = "std")]
impl<E: fmt::Debug + fmt::Display> std::error::Error for IntoSelectionError<E> {}

/// Occurs when we are missing outputs.
#[derive(Debug)]
pub struct MissingOutputs(HashSet<OutPoint>);

impl Deref for MissingOutputs {
type Target = HashSet<OutPoint>;

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<PF> {
/// Missing outputs.
MissingOutputs(MissingOutputs),
/// Policy failure.
PolicyFailure(PF),
}

impl<PF: fmt::Display> fmt::Display for PolicyFailure<PF> {
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<PF: fmt::Debug + fmt::Display> std::error::Error for PolicyFailure<PF> {}

/// Select for lowest fee with bnb
pub fn selection_algorithm_lowest_fee_bnb(
longterm_feerate: FeeRate,
Expand Down
Loading