]> Untitled Git - bdk/commitdiff
example: Update example_cli
authorvalued mammal <valuedmammal@protonmail.com>
Mon, 24 Jun 2024 15:59:22 +0000 (11:59 -0400)
committervalued mammal <valuedmammal@protonmail.com>
Sun, 28 Jul 2024 16:34:07 +0000 (11:34 -0500)
16 files changed:
Cargo.toml
example-crates/example_bitcoind_rpc_polling/src/main.rs
example-crates/example_cli/Cargo.toml
example-crates/example_cli/src/lib.rs
example-crates/example_electrum/src/main.rs
example-crates/example_esplora/src/main.rs
nursery/coin_select/Cargo.toml [deleted file]
nursery/coin_select/src/bnb.rs [deleted file]
nursery/coin_select/src/coin_selector.rs [deleted file]
nursery/coin_select/src/lib.rs [deleted file]
nursery/tmp_plan/Cargo.toml [deleted file]
nursery/tmp_plan/README.md [deleted file]
nursery/tmp_plan/src/lib.rs [deleted file]
nursery/tmp_plan/src/plan_impls.rs [deleted file]
nursery/tmp_plan/src/requirements.rs [deleted file]
nursery/tmp_plan/src/template.rs [deleted file]

index f9dbbf885333db368316eafa79b035b9fd8919e3..1c29bbaf5dd8f1f481deed63eba53e11baca7d2b 100644 (file)
@@ -17,8 +17,6 @@ members = [
     "example-crates/wallet_esplora_blocking",
     "example-crates/wallet_esplora_async",
     "example-crates/wallet_rpc",
-    "nursery/tmp_plan",
-    "nursery/coin_select"
 ]
 
 [workspace.package]
index 75c658510375da5e1eaaac79e8eb9bcca1657388..d1833b0711e7e86fcf3290fa9614b9975ab42c3b 100644 (file)
@@ -2,7 +2,7 @@ use std::{
     path::PathBuf,
     sync::{
         atomic::{AtomicBool, Ordering},
-        Arc, Mutex,
+        Arc,
     },
     time::{Duration, Instant},
 };
@@ -12,16 +12,13 @@ use bdk_bitcoind_rpc::{
     Emitter,
 };
 use bdk_chain::{
-    bitcoin::{constants::genesis_block, Block, Transaction},
-    indexed_tx_graph,
-    indexer::keychain_txout,
-    local_chain::{self, LocalChain},
-    ConfirmationBlockTime, IndexedTxGraph, Merge,
+    bitcoin::{Block, Transaction},
+    local_chain, Merge,
 };
 use example_cli::{
     anyhow,
     clap::{self, Args, Subcommand},
-    Keychain,
+    ChangeSet, Keychain,
 };
 
 const DB_MAGIC: &[u8] = b"bdk_example_rpc";
@@ -36,11 +33,6 @@ const MEMPOOL_EMIT_DELAY: Duration = Duration::from_secs(30);
 /// Delay for committing to persistence.
 const DB_COMMIT_DELAY: Duration = Duration::from_secs(60);
 
-type ChangeSet = (
-    local_chain::ChangeSet,
-    indexed_tx_graph::ChangeSet<ConfirmationBlockTime, keychain_txout::ChangeSet>,
-);
-
 #[derive(Debug)]
 enum Emission {
     Block(bdk_bitcoind_rpc::BlockEvent<Block>),
@@ -111,52 +103,26 @@ enum RpcCommands {
 
 fn main() -> anyhow::Result<()> {
     let start = Instant::now();
+
     let example_cli::Init {
         args,
-        keymap,
-        index,
+        graph,
+        chain,
         db,
-        init_changeset,
-    } = example_cli::init::<RpcCommands, RpcArgs, ChangeSet>(DB_MAGIC, DB_PATH)?;
-    println!(
-        "[{:>10}s] loaded initial changeset from db",
-        start.elapsed().as_secs_f32()
-    );
-    let (init_chain_changeset, init_graph_changeset) = init_changeset;
-
-    let graph = Mutex::new({
-        let mut graph = IndexedTxGraph::new(index);
-        graph.apply_changeset(init_graph_changeset);
-        graph
-    });
-    println!(
-        "[{:>10}s] loaded indexed tx graph from changeset",
-        start.elapsed().as_secs_f32()
-    );
-
-    let chain = Mutex::new(if init_chain_changeset.is_empty() {
-        let genesis_hash = genesis_block(args.network).block_hash();
-        let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
-        let mut db = db.lock().unwrap();
-        db.append_changeset(&(chain_changeset, Default::default()))?;
-        chain
-    } else {
-        LocalChain::from_changeset(init_chain_changeset)?
-    });
-    println!(
-        "[{:>10}s] loaded local chain from changeset",
-        start.elapsed().as_secs_f32()
-    );
+        network,
+    } = match example_cli::init_or_load::<RpcCommands, RpcArgs>(DB_MAGIC, DB_PATH)? {
+        Some(init) => init,
+        None => return Ok(()),
+    };
 
     let rpc_cmd = match args.command {
         example_cli::Commands::ChainSpecific(rpc_cmd) => rpc_cmd,
         general_cmd => {
             return example_cli::handle_commands(
                 &graph,
-                &db,
                 &chain,
-                &keymap,
-                args.network,
+                &db,
+                network,
                 |rpc_args, tx| {
                     let client = rpc_args.new_client()?;
                     client.send_raw_transaction(tx)?;
@@ -191,7 +157,12 @@ fn main() -> anyhow::Result<()> {
                     .apply_update(emission.checkpoint)
                     .expect("must always apply as we receive blocks in order from emitter");
                 let graph_changeset = graph.apply_block_relevant(&emission.block, height);
-                db_stage.merge((chain_changeset, graph_changeset));
+                db_stage.merge(ChangeSet {
+                    local_chain: chain_changeset,
+                    tx_graph: graph_changeset.tx_graph,
+                    indexer: graph_changeset.indexer,
+                    ..Default::default()
+                });
 
                 // commit staged db changes in intervals
                 if last_db_commit.elapsed() >= DB_COMMIT_DELAY {
@@ -220,7 +191,7 @@ fn main() -> anyhow::Result<()> {
                         )
                     };
                     println!(
-                        "[{:>10}s] synced to {} @ {} | total: {} sats",
+                        "[{:>10}s] synced to {} @ {} | total: {}",
                         start.elapsed().as_secs_f32(),
                         synced_to.hash(),
                         synced_to.height(),
@@ -235,7 +206,11 @@ fn main() -> anyhow::Result<()> {
             );
             {
                 let db = &mut *db.lock().unwrap();
-                db_stage.merge((local_chain::ChangeSet::default(), graph_changeset));
+                db_stage.merge(ChangeSet {
+                    tx_graph: graph_changeset.tx_graph,
+                    indexer: graph_changeset.indexer,
+                    ..Default::default()
+                });
                 if let Some(changeset) = db_stage.take() {
                     db.append_changeset(&changeset)?;
                 }
@@ -300,7 +275,7 @@ fn main() -> anyhow::Result<()> {
                 let mut graph = graph.lock().unwrap();
                 let mut chain = chain.lock().unwrap();
 
-                let changeset = match emission {
+                let (chain_changeset, graph_changeset) = match emission {
                     Emission::Block(block_emission) => {
                         let height = block_emission.block_height();
                         let chain_changeset = chain
@@ -321,7 +296,13 @@ fn main() -> anyhow::Result<()> {
                         continue;
                     }
                 };
-                db_stage.merge(changeset);
+
+                db_stage.merge(ChangeSet {
+                    local_chain: chain_changeset,
+                    tx_graph: graph_changeset.tx_graph,
+                    indexer: graph_changeset.indexer,
+                    ..Default::default()
+                });
 
                 if last_db_commit.elapsed() >= DB_COMMIT_DELAY {
                     let db = &mut *db.lock().unwrap();
@@ -348,7 +329,7 @@ fn main() -> anyhow::Result<()> {
                         )
                     };
                     println!(
-                        "[{:>10}s] synced to {} @ {} / {} | total: {} sats",
+                        "[{:>10}s] synced to {} @ {} / {} | total: {}",
                         start.elapsed().as_secs_f32(),
                         synced_to.hash(),
                         synced_to.height(),
index c85d2e996310b0fc8e3fc28347e12cc867cce433..9b8c4debbbf7cb363f0aa729d38d707e3e7869f9 100644 (file)
@@ -7,11 +7,10 @@ edition = "2021"
 
 [dependencies]
 bdk_chain = { path = "../../crates/chain", features = ["serde", "miniscript"]}
+bdk_coin_select = "0.3.0"
 bdk_file_store = { path = "../../crates/file_store" }
-bdk_tmp_plan = { path = "../../nursery/tmp_plan" }
-bdk_coin_select = { path = "../../nursery/coin_select" }
 
-clap = { version = "3.2.23", features = ["derive", "env"] }
 anyhow = "1"
+clap = { version = "3.2.23", features = ["derive", "env"] }
 serde = { version = "1", features = ["derive"] }
-serde_json = { version = "^1.0" }
+serde_json = "1.0"
index 568123d922d6168c361b9fa840bdd0ed02c71e08..ee0c9b3761ac5cf92ca7a1549362c85d10d469f3 100644 (file)
@@ -1,62 +1,88 @@
-pub use anyhow;
+use bdk_chain::ConfirmationBlockTime;
+use serde_json::json;
+use std::cmp;
+use std::collections::HashMap;
+use std::env;
+use std::fmt;
+use std::str::FromStr;
+use std::sync::Mutex;
+
+use anyhow::bail;
 use anyhow::Context;
-use bdk_coin_select::{coin_select_bnb, CoinSelector, CoinSelectorOpt, WeightedValue};
-use bdk_file_store::Store;
-use serde::{de::DeserializeOwned, Serialize};
-use std::fmt::Debug;
-use std::{cmp::Reverse, collections::BTreeMap, path::PathBuf, sync::Mutex, time::Duration};
-
+use bdk_chain::bitcoin::{
+    absolute,
+    address::NetworkUnchecked,
+    bip32, consensus, constants,
+    hex::DisplayHex,
+    relative,
+    secp256k1::{rand::prelude::*, Secp256k1},
+    transaction, Address, Amount, Network, NetworkKind, PrivateKey, Psbt, PublicKey, Sequence,
+    Transaction, TxIn, TxOut,
+};
+use bdk_chain::miniscript::{
+    descriptor::{DescriptorSecretKey, SinglePubKey},
+    plan::{Assets, Plan},
+    psbt::PsbtExt,
+    Descriptor, DescriptorPublicKey,
+};
 use bdk_chain::{
-    bitcoin::{
-        absolute, address,
-        secp256k1::Secp256k1,
-        sighash::{Prevouts, SighashCache},
-        transaction, Address, Amount, Network, Sequence, Transaction, TxIn, TxOut,
-    },
-    indexed_tx_graph::{self, IndexedTxGraph},
+    indexed_tx_graph,
     indexer::keychain_txout::{self, KeychainTxOutIndex},
-    local_chain,
-    miniscript::{
-        descriptor::{DescriptorSecretKey, KeyMap},
-        Descriptor, DescriptorPublicKey,
-    },
-    Anchor, ChainOracle, DescriptorExt, FullTxOut, Merge,
+    local_chain::{self, LocalChain},
+    tx_graph, ChainOracle, DescriptorExt, FullTxOut, IndexedTxGraph, Merge,
 };
-pub use bdk_file_store;
-pub use clap;
-
+use bdk_coin_select::{
+    metrics::LowestFee, Candidate, ChangePolicy, CoinSelector, DrainWeights, FeeRate, Target,
+    TargetFee, TargetOutputs,
+};
+use bdk_file_store::Store;
 use clap::{Parser, Subcommand};
 
-pub type KeychainTxGraph<A> = IndexedTxGraph<A, KeychainTxOutIndex<Keychain>>;
-pub type KeychainChangeSet<A> = (
-    local_chain::ChangeSet,
-    indexed_tx_graph::ChangeSet<A, keychain_txout::ChangeSet>,
-);
+pub use anyhow;
+pub use clap;
+
+/// Alias for a `IndexedTxGraph` with specific `Anchor` and `Indexer`.
+pub type KeychainTxGraph = IndexedTxGraph<ConfirmationBlockTime, KeychainTxOutIndex<Keychain>>;
+
+/// ChangeSet
+#[derive(Default, Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
+pub struct ChangeSet {
+    /// Descriptor for recipient addresses.
+    pub descriptor: Option<Descriptor<DescriptorPublicKey>>,
+    /// Descriptor for change addresses.
+    pub change_descriptor: Option<Descriptor<DescriptorPublicKey>>,
+    /// Stores the network type of the transaction data.
+    pub network: Option<Network>,
+    /// Changes to the [`LocalChain`].
+    pub local_chain: local_chain::ChangeSet,
+    /// Changes to [`TxGraph`](tx_graph::TxGraph).
+    pub tx_graph: tx_graph::ChangeSet<ConfirmationBlockTime>,
+    /// Changes to [`KeychainTxOutIndex`].
+    pub indexer: keychain_txout::ChangeSet,
+}
 
 #[derive(Parser)]
 #[clap(author, version, about, long_about = None)]
 #[clap(propagate_version = true)]
 pub struct Args<CS: clap::Subcommand, S: clap::Args> {
-    #[clap(env = "DESCRIPTOR")]
-    pub descriptor: String,
-    #[clap(env = "CHANGE_DESCRIPTOR")]
-    pub change_descriptor: Option<String>,
-
-    #[clap(env = "BITCOIN_NETWORK", long, default_value = "signet")]
-    pub network: Network,
-
-    #[clap(env = "BDK_DB_PATH", long, default_value = ".bdk_example_db")]
-    pub db_path: PathBuf,
-
-    #[clap(env = "BDK_CP_LIMIT", long, default_value = "20")]
-    pub cp_limit: usize,
-
     #[clap(subcommand)]
     pub command: Commands<CS, S>,
 }
 
 #[derive(Subcommand, Debug, Clone)]
 pub enum Commands<CS: clap::Subcommand, S: clap::Args> {
+    /// Initialize a new data store.
+    Init {
+        /// Network
+        #[clap(long, short, default_value = "signet")]
+        network: Network,
+        /// Descriptor
+        #[clap(env = "DESCRIPTOR")]
+        descriptor: String,
+        /// Change descriptor
+        #[clap(long, short, env = "CHANGE_DESCRIPTOR")]
+        change_descriptor: Option<String>,
+    },
     #[clap(flatten)]
     ChainSpecific(CS),
     /// Address generation and inspection.
@@ -72,70 +98,17 @@ pub enum Commands<CS: clap::Subcommand, S: clap::Args> {
         #[clap(subcommand)]
         txout_cmd: TxOutCmd,
     },
-    /// Send coins to an address.
-    Send {
-        /// Amount to send in satoshis
-        value: u64,
-        /// Destination address
-        address: Address<address::NetworkUnchecked>,
-        #[clap(short, default_value = "bnb")]
-        coin_select: CoinSelectionAlgo,
-        #[clap(flatten)]
-        chain_specific: S,
+    /// PSBT operations
+    Psbt {
+        #[clap(subcommand)]
+        psbt_cmd: PsbtCmd<S>,
+    },
+    /// Generate new BIP86 descriptors.
+    Generate {
+        /// Network
+        #[clap(long, short, default_value = "signet")]
+        network: Network,
     },
-}
-
-#[derive(Clone, Debug)]
-pub enum CoinSelectionAlgo {
-    LargestFirst,
-    SmallestFirst,
-    OldestFirst,
-    NewestFirst,
-    BranchAndBound,
-}
-
-impl Default for CoinSelectionAlgo {
-    fn default() -> Self {
-        Self::LargestFirst
-    }
-}
-
-impl core::str::FromStr for CoinSelectionAlgo {
-    type Err = anyhow::Error;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        use CoinSelectionAlgo::*;
-        Ok(match s {
-            "largest-first" => LargestFirst,
-            "smallest-first" => SmallestFirst,
-            "oldest-first" => OldestFirst,
-            "newest-first" => NewestFirst,
-            "bnb" => BranchAndBound,
-            unknown => {
-                return Err(anyhow::anyhow!(
-                    "unknown coin selection algorithm '{}'",
-                    unknown
-                ))
-            }
-        })
-    }
-}
-
-impl core::fmt::Display for CoinSelectionAlgo {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        use CoinSelectionAlgo::*;
-        write!(
-            f,
-            "{}",
-            match self {
-                LargestFirst => "largest-first",
-                SmallestFirst => "smallest-first",
-                OldestFirst => "oldest-first",
-                NewestFirst => "newest-first",
-                BranchAndBound => "bnb",
-            }
-        )
-    }
 }
 
 #[derive(Subcommand, Debug, Clone)]
@@ -173,6 +146,48 @@ pub enum TxOutCmd {
     },
 }
 
+#[derive(Subcommand, Debug, Clone)]
+pub enum PsbtCmd<S: clap::Args> {
+    /// Create a new PSBT.
+    New {
+        /// Amount to send in satoshis
+        value: u64,
+        /// Recipient address
+        address: Address<NetworkUnchecked>,
+        /// Set max absolute timelock (from consensus value)
+        #[clap(long, short)]
+        after: Option<u32>,
+        /// Set max relative timelock (from consensus value)
+        #[clap(long, short)]
+        older: Option<u32>,
+        /// Coin selection algorithm
+        #[clap(long, short, default_value = "bnb")]
+        coin_select: CoinSelectionAlgo,
+        /// Debug print the PSBT
+        #[clap(long, short)]
+        debug: bool,
+    },
+    /// Sign with a hot signer
+    Sign {
+        /// PSBT
+        #[clap(long)]
+        psbt: Option<String>,
+        /// Private descriptor
+        #[clap(long, short = 'd')]
+        descriptor: Option<String>,
+    },
+    /// Extract transaction
+    Extract {
+        /// PSBT
+        psbt: String,
+        /// Whether to try broadcasting the tx
+        #[clap(long, short = 'b')]
+        try_broadcast: bool,
+        #[clap(flatten)]
+        chain_specific: S,
+    },
+}
+
 #[derive(
     Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize, serde::Serialize,
 )]
@@ -181,7 +196,7 @@ pub enum Keychain {
     Internal,
 }
 
-impl core::fmt::Display for Keychain {
+impl fmt::Display for Keychain {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
             Keychain::External => write!(f, "external"),
@@ -190,242 +205,228 @@ impl core::fmt::Display for Keychain {
     }
 }
 
-pub struct CreateTxChange {
-    pub index_changeset: keychain_txout::ChangeSet,
+#[derive(Clone, Debug, Default)]
+pub enum CoinSelectionAlgo {
+    LargestFirst,
+    SmallestFirst,
+    OldestFirst,
+    NewestFirst,
+    #[default]
+    BranchAndBound,
+}
+
+impl FromStr for CoinSelectionAlgo {
+    type Err = anyhow::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        use CoinSelectionAlgo::*;
+        Ok(match s {
+            "largest-first" => LargestFirst,
+            "smallest-first" => SmallestFirst,
+            "oldest-first" => OldestFirst,
+            "newest-first" => NewestFirst,
+            "bnb" => BranchAndBound,
+            unknown => bail!("unknown coin selection algorithm '{}'", unknown),
+        })
+    }
+}
+
+impl fmt::Display for CoinSelectionAlgo {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        use CoinSelectionAlgo::*;
+        write!(
+            f,
+            "{}",
+            match self {
+                LargestFirst => "largest-first",
+                SmallestFirst => "smallest-first",
+                OldestFirst => "oldest-first",
+                NewestFirst => "newest-first",
+                BranchAndBound => "bnb",
+            }
+        )
+    }
+}
+
+// Records changes to the internal keychain when we
+// have to include a change output during tx creation.
+#[derive(Debug)]
+pub struct ChangeInfo {
     pub change_keychain: Keychain,
+    pub indexer: keychain_txout::ChangeSet,
     pub index: u32,
 }
 
-pub fn create_tx<A: Anchor, O: ChainOracle>(
-    graph: &mut KeychainTxGraph<A>,
+pub fn create_tx<O: ChainOracle>(
+    graph: &mut KeychainTxGraph,
     chain: &O,
-    keymap: &BTreeMap<DescriptorPublicKey, DescriptorSecretKey>,
+    assets: &Assets,
     cs_algorithm: CoinSelectionAlgo,
     address: Address,
     value: u64,
-) -> anyhow::Result<(Transaction, Option<CreateTxChange>)>
+) -> anyhow::Result<(Psbt, Option<ChangeInfo>)>
 where
     O::Error: std::error::Error + Send + Sync + 'static,
 {
     let mut changeset = keychain_txout::ChangeSet::default();
 
-    let assets = bdk_tmp_plan::Assets {
-        keys: keymap.iter().map(|(pk, _)| pk.clone()).collect(),
-        ..Default::default()
-    };
-
-    // TODO use planning module
-    let mut candidates = planned_utxos(graph, chain, &assets)?;
+    // get planned utxos
+    let mut plan_utxos = planned_utxos(graph, chain, assets)?;
 
-    // apply coin selection algorithm
+    // sort utxos if cs-algo requires it
     match cs_algorithm {
         CoinSelectionAlgo::LargestFirst => {
-            candidates.sort_by_key(|(_, utxo)| Reverse(utxo.txout.value))
-        }
-        CoinSelectionAlgo::SmallestFirst => candidates.sort_by_key(|(_, utxo)| utxo.txout.value),
-        CoinSelectionAlgo::OldestFirst => {
-            candidates.sort_by_key(|(_, utxo)| utxo.chain_position.clone())
+            plan_utxos.sort_by_key(|(_, utxo)| cmp::Reverse(utxo.txout.value))
         }
+        CoinSelectionAlgo::SmallestFirst => plan_utxos.sort_by_key(|(_, utxo)| utxo.txout.value),
+        CoinSelectionAlgo::OldestFirst => plan_utxos.sort_by_key(|(_, utxo)| utxo.chain_position),
         CoinSelectionAlgo::NewestFirst => {
-            candidates.sort_by_key(|(_, utxo)| Reverse(utxo.chain_position.clone()))
+            plan_utxos.sort_by_key(|(_, utxo)| cmp::Reverse(utxo.chain_position))
         }
-        CoinSelectionAlgo::BranchAndBound => {}
+        CoinSelectionAlgo::BranchAndBound => plan_utxos.shuffle(&mut thread_rng()),
     }
 
-    // turn the txos we chose into weight and value
-    let wv_candidates = candidates
+    // build candidate set
+    let candidates: Vec<Candidate> = plan_utxos
         .iter()
         .map(|(plan, utxo)| {
-            WeightedValue::new(
+            Candidate::new(
                 utxo.txout.value.to_sat(),
-                plan.expected_weight() as _,
+                plan.satisfaction_weight() as u32,
                 plan.witness_version().is_some(),
             )
         })
         .collect();
 
+    // create recipient output(s)
     let mut outputs = vec![TxOut {
         value: Amount::from_sat(value),
         script_pubkey: address.script_pubkey(),
     }];
 
-    let internal_keychain = if graph
+    let (change_keychain, _) = graph
         .index
         .keychains()
-        .any(|(k, _)| k == Keychain::Internal)
-    {
-        Keychain::Internal
-    } else {
-        Keychain::External
-    };
+        .last()
+        .expect("must have a keychain");
 
-    let ((change_index, change_script), change_changeset) = graph
+    let ((change_index, change_script), index_changeset) = graph
         .index
-        .next_unused_spk(internal_keychain)
+        .next_unused_spk(change_keychain)
         .expect("Must exist");
-    changeset.merge(change_changeset);
-
-    let change_plan = bdk_tmp_plan::plan_satisfaction(
-        &graph
-            .index
-            .keychains()
-            .find(|(k, _)| *k == internal_keychain)
-            .expect("must exist")
-            .1
-            .at_derivation_index(change_index)
-            .expect("change_index can't be hardened"),
-        &assets,
-    )
-    .expect("failed to obtain change plan");
+    changeset.merge(index_changeset);
 
     let mut change_output = TxOut {
         value: Amount::ZERO,
         script_pubkey: change_script,
     };
 
-    let cs_opts = CoinSelectorOpt {
-        target_feerate: 0.5,
-        min_drain_value: graph
-            .index
-            .keychains()
-            .find(|(k, _)| *k == internal_keychain)
-            .expect("must exist")
-            .1
-            .dust_value(),
-        ..CoinSelectorOpt::fund_outputs(
-            &outputs,
-            &change_output,
-            change_plan.expected_weight() as u32,
-        )
+    let change_desc = graph
+        .index
+        .keychains()
+        .find(|(k, _)| k == &change_keychain)
+        .expect("must exist")
+        .1;
+
+    let min_drain_value = change_desc.dust_value();
+
+    let target = Target {
+        outputs: TargetOutputs::fund_outputs(
+            outputs
+                .iter()
+                .map(|output| (output.weight().to_wu() as u32, output.value.to_sat())),
+        ),
+        fee: TargetFee::default(),
     };
 
-    // TODO: How can we make it easy to shuffle in order of inputs and outputs here?
-    // apply coin selection by saying we need to fund these outputs
-    let mut coin_selector = CoinSelector::new(&wv_candidates, &cs_opts);
+    let change_policy = ChangePolicy {
+        min_value: min_drain_value,
+        drain_weights: DrainWeights::TR_KEYSPEND,
+    };
 
-    // just select coins in the order provided until we have enough
-    // only use the first result (least waste)
-    let selection = match cs_algorithm {
+    // run coin selection
+    let mut selector = CoinSelector::new(&candidates);
+    match cs_algorithm {
         CoinSelectionAlgo::BranchAndBound => {
-            coin_select_bnb(Duration::from_secs(10), coin_selector.clone())
-                .map_or_else(|| coin_selector.select_until_finished(), |cs| cs.finish())?
+            let metric = LowestFee {
+                target,
+                long_term_feerate: FeeRate::from_sat_per_vb(10.0),
+                change_policy,
+            };
+            match selector.run_bnb(metric, 10_000) {
+                Ok(_) => {}
+                Err(_) => selector
+                    .select_until_target_met(target)
+                    .context("selecting coins")?,
+            }
         }
-        _ => coin_selector.select_until_finished()?,
-    };
-    let (_, selection_meta) = selection.best_strategy();
-
-    // get the selected utxos
-    let selected_txos = selection.apply_selection(&candidates).collect::<Vec<_>>();
+        _ => selector
+            .select_until_target_met(target)
+            .context("selecting coins")?,
+    }
 
-    if let Some(drain_value) = selection_meta.drain_value {
-        change_output.value = Amount::from_sat(drain_value);
-        // if the selection tells us to use change and the change value is sufficient, we add it as an output
-        outputs.push(change_output)
+    // get the selected plan utxos
+    let selected: Vec<_> = selector.apply_selection(&plan_utxos).collect();
+
+    // if the selection tells us to use change and the change value is sufficient, we add it as an output
+    let mut change_info = Option::<ChangeInfo>::None;
+    let drain = selector.drain(target, change_policy);
+    if drain.value > min_drain_value {
+        change_output.value = Amount::from_sat(drain.value);
+        outputs.push(change_output);
+        change_info = Some(ChangeInfo {
+            change_keychain,
+            indexer: changeset,
+            index: change_index,
+        });
+        outputs.shuffle(&mut thread_rng());
     }
 
-    let mut transaction = Transaction {
+    let unsigned_tx = Transaction {
         version: transaction::Version::TWO,
-        // because the temporary planning module does not support timelocks, we can use the chain
-        // tip as the `lock_time` for anti-fee-sniping purposes
-        lock_time: absolute::LockTime::from_height(chain.get_chain_tip()?.height)
-            .expect("invalid height"),
-        input: selected_txos
+        lock_time: assets
+            .absolute_timelock
+            .unwrap_or(absolute::LockTime::from_height(
+                chain.get_chain_tip()?.height,
+            )?),
+        input: selected
             .iter()
-            .map(|(_, utxo)| TxIn {
+            .map(|(plan, utxo)| TxIn {
                 previous_output: utxo.outpoint,
-                sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
+                sequence: plan
+                    .relative_timelock
+                    .map_or(Sequence::ENABLE_RBF_NO_LOCKTIME, Sequence::from),
                 ..Default::default()
             })
             .collect(),
         output: outputs,
     };
 
-    let prevouts = selected_txos
-        .iter()
-        .map(|(_, utxo)| utxo.txout.clone())
-        .collect::<Vec<_>>();
-    let sighash_prevouts = Prevouts::All(&prevouts);
-
-    // first, set tx values for the plan so that we don't change them while signing
-    for (i, (plan, _)) in selected_txos.iter().enumerate() {
-        if let Some(sequence) = plan.required_sequence() {
-            transaction.input[i].sequence = sequence
-        }
-    }
-
-    // create a short lived transaction
-    let _sighash_tx = transaction.clone();
-    let mut sighash_cache = SighashCache::new(&_sighash_tx);
-
-    for (i, (plan, _)) in selected_txos.iter().enumerate() {
-        let requirements = plan.requirements();
-        let mut auth_data = bdk_tmp_plan::SatisfactionMaterial::default();
-        assert!(
-            !requirements.requires_hash_preimages(),
-            "can't have hash pre-images since we didn't provide any."
-        );
-        assert!(
-            requirements.signatures.sign_with_keymap(
-                i,
-                keymap,
-                &sighash_prevouts,
-                None,
-                None,
-                &mut sighash_cache,
-                &mut auth_data,
-                &Secp256k1::default(),
-            )?,
-            "we should have signed with this input."
-        );
-
-        match plan.try_complete(&auth_data) {
-            bdk_tmp_plan::PlanState::Complete {
-                final_script_sig,
-                final_script_witness,
-            } => {
-                if let Some(witness) = final_script_witness {
-                    transaction.input[i].witness = witness;
-                }
-
-                if let Some(script_sig) = final_script_sig {
-                    transaction.input[i].script_sig = script_sig;
-                }
-            }
-            bdk_tmp_plan::PlanState::Incomplete(_) => {
-                return Err(anyhow::anyhow!(
-                    "we weren't able to complete the plan with our keys."
-                ));
-            }
-        }
+    // update psbt with plan
+    let mut psbt = Psbt::from_unsigned_tx(unsigned_tx)?;
+    for (i, (plan, utxo)) in selected.iter().enumerate() {
+        let psbt_input = &mut psbt.inputs[i];
+        plan.update_psbt_input(psbt_input);
+        psbt_input.witness_utxo = Some(utxo.txout.clone());
     }
 
-    let change_info = if selection_meta.drain_value.is_some() {
-        Some(CreateTxChange {
-            index_changeset: changeset,
-            change_keychain: internal_keychain,
-            index: change_index,
-        })
-    } else {
-        None
-    };
-
-    Ok((transaction, change_info))
+    Ok((psbt, change_info))
 }
 
-// Alias the elements of `Result` of `planned_utxos`
-pub type PlannedUtxo<K, A> = (bdk_tmp_plan::Plan<K>, FullTxOut<A>);
+// Alias the elements of `planned_utxos`
+pub type PlanUtxo = (Plan, FullTxOut<ConfirmationBlockTime>);
 
-pub fn planned_utxos<A: Anchor, O: ChainOracle, K: Clone + bdk_tmp_plan::CanDerive>(
-    graph: &KeychainTxGraph<A>,
+pub fn planned_utxos<O: ChainOracle>(
+    graph: &KeychainTxGraph,
     chain: &O,
-    assets: &bdk_tmp_plan::Assets<K>,
-) -> Result<Vec<PlannedUtxo<K, A>>, O::Error> {
+    assets: &Assets,
+) -> Result<Vec<PlanUtxo>, O::Error> {
     let chain_tip = chain.get_chain_tip()?;
     let outpoints = graph.index.outpoints();
     graph
         .graph()
         .try_filter_chain_unspents(chain, chain_tip, outpoints.iter().cloned())
-        .filter_map(|r| -> Option<Result<PlannedUtxo<K, A>, _>> {
+        .filter_map(|r| -> Option<Result<PlanUtxo, _>> {
             let (k, i, full_txo) = match r {
                 Err(err) => return Some(Err(err)),
                 Ok(((k, i), full_txo)) => (k, i, full_txo),
@@ -438,33 +439,25 @@ pub fn planned_utxos<A: Anchor, O: ChainOracle, K: Clone + bdk_tmp_plan::CanDeri
                 .1
                 .at_derivation_index(i)
                 .expect("i can't be hardened");
-            let plan = bdk_tmp_plan::plan_satisfaction(&desc, assets)?;
+
+            let plan = desc.plan(assets).ok()?;
+
             Some(Ok((plan, full_txo)))
         })
         .collect()
 }
 
-pub fn handle_commands<CS: clap::Subcommand, S: clap::Args, A: Anchor, O: ChainOracle, C>(
-    graph: &Mutex<KeychainTxGraph<A>>,
-    db: &Mutex<Store<C>>,
-    chain: &Mutex<O>,
-    keymap: &BTreeMap<DescriptorPublicKey, DescriptorSecretKey>,
+pub fn handle_commands<CS: clap::Subcommand, S: clap::Args>(
+    graph: &Mutex<KeychainTxGraph>,
+    chain: &Mutex<LocalChain>,
+    db: &Mutex<Store<ChangeSet>>,
     network: Network,
     broadcast: impl FnOnce(S, &Transaction) -> anyhow::Result<()>,
     cmd: Commands<CS, S>,
-) -> anyhow::Result<()>
-where
-    O::Error: std::error::Error + Send + Sync + 'static,
-    C: Default
-        + Merge
-        + DeserializeOwned
-        + Serialize
-        + From<KeychainChangeSet<A>>
-        + Send
-        + Sync
-        + Debug,
-{
+) -> anyhow::Result<()> {
     match cmd {
+        Commands::Init { .. } => unreachable!("handled by init command"),
+        Commands::Generate { .. } => unreachable!("handled by generate command"),
         Commands::ChainSpecific(_) => unreachable!("example code should handle this!"),
         Commands::Address { addr_cmd } => {
             let graph = &mut *graph.lock().unwrap();
@@ -481,12 +474,11 @@ where
                     let ((spk_i, spk), index_changeset) =
                         spk_chooser(index, Keychain::External).expect("Must exist");
                     let db = &mut *db.lock().unwrap();
-                    db.append_changeset(&C::from((
-                        local_chain::ChangeSet::default(),
-                        indexed_tx_graph::ChangeSet::from(index_changeset),
-                    )))?;
-                    let addr = Address::from_script(spk.as_script(), network)
-                        .context("failed to derive address")?;
+                    db.append_changeset(&ChangeSet {
+                        indexer: index_changeset,
+                        ..Default::default()
+                    })?;
+                    let addr = Address::from_script(spk.as_script(), network)?;
                     println!("[address @ {}] {}", spk_i, addr);
                     Ok(())
                 }
@@ -604,23 +596,47 @@ where
                 }
             }
         }
-        Commands::Send {
-            value,
-            address,
-            coin_select,
-            chain_specific,
-        } => {
-            let chain = &*chain.lock().unwrap();
-            let address = address.require_network(network)?;
-            let (transaction, change_index) = {
-                let graph = &mut *graph.lock().unwrap();
-                // take mutable ref to construct tx -- it is only open for a short time while building it.
-                let (tx, change_info) =
-                    create_tx(graph, chain, keymap, coin_select, address, value)?;
-
-                if let Some(CreateTxChange {
-                    index_changeset,
+        Commands::Psbt { psbt_cmd } => match psbt_cmd {
+            PsbtCmd::New {
+                value,
+                address,
+                after,
+                older,
+                coin_select,
+                debug,
+            } => {
+                let address = address.require_network(network)?;
+
+                let (psbt, change_info) = {
+                    let mut graph = graph.lock().unwrap();
+                    let chain = chain.lock().unwrap();
+
+                    // collect assets we can sign for
+                    let mut assets = Assets::new();
+                    if let Some(n) = after {
+                        assets = assets.after(absolute::LockTime::from_consensus(n));
+                    }
+                    if let Some(n) = older {
+                        assets = assets.older(relative::LockTime::from_consensus(n)?);
+                    }
+                    for (_, desc) in graph.index.keychains() {
+                        match desc {
+                            Descriptor::Wpkh(wpkh) => {
+                                assets = assets.add(wpkh.clone().into_inner());
+                            }
+                            Descriptor::Tr(tr) => {
+                                assets = assets.add(tr.internal_key().clone());
+                            }
+                            _ => bail!("unsupported descriptor type"),
+                        }
+                    }
+
+                    create_tx(&mut graph, &*chain, &assets, coin_select, address, value)?
+                };
+
+                if let Some(ChangeInfo {
                     change_keychain,
+                    indexer,
                     index,
                 }) = change_info
                 {
@@ -629,119 +645,310 @@ where
                     // If we're unable to persist this, then we don't want to broadcast.
                     {
                         let db = &mut *db.lock().unwrap();
-                        db.append_changeset(&C::from((
-                            local_chain::ChangeSet::default(),
-                            indexed_tx_graph::ChangeSet::from(index_changeset),
-                        )))?;
+                        db.append_changeset(&ChangeSet {
+                            indexer,
+                            ..Default::default()
+                        })?;
                     }
 
                     // We don't want other callers/threads to use this address while we're using it
                     // but we also don't want to scan the tx we just created because it's not
                     // technically in the blockchain yet.
-                    graph.index.mark_used(change_keychain, index);
-                    (tx, Some((change_keychain, index)))
+                    graph
+                        .lock()
+                        .unwrap()
+                        .index
+                        .mark_used(change_keychain, index);
+                }
+
+                if debug {
+                    dbg!(psbt);
                 } else {
-                    (tx, None)
+                    // print base64 encoded psbt
+                    let fee = psbt.fee()?.to_sat();
+                    let mut obj = serde_json::Map::new();
+                    obj.insert("psbt".to_string(), json!(psbt.to_string()));
+                    obj.insert("fee".to_string(), json!(fee));
+                    println!("{}", serde_json::to_string_pretty(&obj)?);
+                };
+
+                Ok(())
+            }
+            PsbtCmd::Sign { psbt, descriptor } => {
+                let mut psbt = Psbt::from_str(&psbt.unwrap_or_default())?;
+
+                let desc_str = match descriptor {
+                    Some(s) => s,
+                    None => env::var("DESCRIPTOR").context("unable to sign")?,
+                };
+
+                let secp = Secp256k1::new();
+                let (_, keymap) = Descriptor::parse_descriptor(&secp, &desc_str)?;
+                if keymap.is_empty() {
+                    bail!("unable to sign")
                 }
-            };
 
-            match (broadcast)(chain_specific, &transaction) {
-                Ok(_) => {
-                    println!("Broadcasted Tx : {}", transaction.compute_txid());
+                // note: we're only looking at the first entry in the keymap
+                // the idea is to find something that impls `GetKey`
+                let sign_res = match keymap.iter().next().expect("not empty") {
+                    (DescriptorPublicKey::Single(single_pub), DescriptorSecretKey::Single(prv)) => {
+                        let pk = match single_pub.key {
+                            SinglePubKey::FullKey(pk) => pk,
+                            SinglePubKey::XOnly(_) => unimplemented!("single xonly pubkey"),
+                        };
+                        let keys: HashMap<PublicKey, PrivateKey> = [(pk, prv.key)].into();
+                        psbt.sign(&keys, &secp)
+                    }
+                    (_, DescriptorSecretKey::XPrv(k)) => psbt.sign(&k.xkey, &secp),
+                    _ => unimplemented!("multi xkey signer"),
+                };
 
-                    let keychain_changeset = graph.lock().unwrap().insert_tx(transaction);
+                let _ = sign_res
+                    .map_err(|errors| anyhow::anyhow!("failed to sign PSBT {:?}", errors))?;
 
-                    // We know the tx is at least unconfirmed now. Note if persisting here fails,
-                    // it's not a big deal since we can always find it again form
-                    // blockchain.
-                    db.lock().unwrap().append_changeset(&C::from((
-                        local_chain::ChangeSet::default(),
-                        keychain_changeset,
-                    )))?;
-                    Ok(())
-                }
-                Err(e) => {
-                    if let Some((keychain, index)) = change_index {
-                        // We failed to broadcast, so allow our change address to be used in the future
-                        graph.lock().unwrap().index.unmark_used(keychain, index);
+                let mut obj = serde_json::Map::new();
+                obj.insert("psbt".to_string(), json!(psbt.to_string()));
+                println!("{}", serde_json::to_string_pretty(&obj)?);
+
+                Ok(())
+            }
+            PsbtCmd::Extract {
+                try_broadcast,
+                chain_specific,
+                psbt,
+            } => {
+                let mut psbt = Psbt::from_str(&psbt)?;
+                psbt.finalize_mut(&Secp256k1::new())
+                    .map_err(|errors| anyhow::anyhow!("failed to finalize PSBT {errors:?}"))?;
+
+                let tx = psbt.extract_tx()?;
+
+                if try_broadcast {
+                    let mut graph = graph.lock().unwrap();
+
+                    match broadcast(chain_specific, &tx) {
+                        Ok(_) => {
+                            println!("Broadcasted Tx: {}", tx.compute_txid());
+
+                            let changeset = graph.insert_tx(tx);
+
+                            // We know the tx is at least unconfirmed now. Note if persisting here fails,
+                            // it's not a big deal since we can always find it again from the
+                            // blockchain.
+                            db.lock().unwrap().append_changeset(&ChangeSet {
+                                tx_graph: changeset.tx_graph,
+                                indexer: changeset.indexer,
+                                ..Default::default()
+                            })?;
+                        }
+                        Err(e) => {
+                            // We failed to broadcast, so allow our change address to be used in the future
+                            let (change_keychain, _) = graph
+                                .index
+                                .keychains()
+                                .last()
+                                .expect("must have a keychain");
+                            let change_index = tx.output.iter().find_map(|txout| {
+                                let spk = txout.script_pubkey.clone();
+                                match graph.index.index_of_spk(spk) {
+                                    Some(&(keychain, index)) if keychain == change_keychain => {
+                                        Some((keychain, index))
+                                    }
+                                    _ => None,
+                                }
+                            });
+                            if let Some((keychain, index)) = change_index {
+                                graph.index.unmark_used(keychain, index);
+                            }
+                            bail!(e);
+                        }
                     }
-                    Err(e)
+                } else {
+                    // encode raw tx hex
+                    let hex = consensus::serialize(&tx).to_lower_hex_string();
+                    let mut obj = serde_json::Map::new();
+                    obj.insert("tx".to_string(), json!(hex));
+                    println!("{}", serde_json::to_string_pretty(&obj)?);
                 }
+
+                Ok(())
             }
-        }
+        },
     }
 }
 
-/// The initial state returned by [`init`].
-pub struct Init<CS: clap::Subcommand, S: clap::Args, C>
-where
-    C: Default + Merge + Serialize + DeserializeOwned + Debug + Send + Sync + 'static,
-{
-    /// Arguments parsed by the cli.
+/// The initial state returned by [`init_or_load`].
+pub struct Init<CS: clap::Subcommand, S: clap::Args> {
+    /// CLI args
     pub args: Args<CS, S>,
-    /// Descriptor keymap.
-    pub keymap: KeyMap,
-    /// Keychain-txout index.
-    pub index: KeychainTxOutIndex<Keychain>,
-    /// Persistence backend.
-    pub db: Mutex<Store<C>>,
-    /// Initial changeset.
-    pub init_changeset: C,
+    /// Indexed graph
+    pub graph: Mutex<KeychainTxGraph>,
+    /// Local chain
+    pub chain: Mutex<LocalChain>,
+    /// Database
+    pub db: Mutex<Store<ChangeSet>>,
+    /// Network
+    pub network: Network,
 }
 
-/// Parses command line arguments and initializes all components, creating
-/// a file store with the given parameters, or loading one if it exists.
-pub fn init<CS: clap::Subcommand, S: clap::Args, C>(
+/// Loads from persistence or creates new
+pub fn init_or_load<CS: clap::Subcommand, S: clap::Args>(
     db_magic: &[u8],
-    db_default_path: &str,
-) -> anyhow::Result<Init<CS, S, C>>
-where
-    C: Default
-        + Merge
-        + Serialize
-        + DeserializeOwned
-        + Debug
-        + core::marker::Send
-        + core::marker::Sync
-        + 'static,
-{
-    if std::env::var("BDK_DB_PATH").is_err() {
-        std::env::set_var("BDK_DB_PATH", db_default_path);
-    }
+    db_path: &str,
+) -> anyhow::Result<Option<Init<CS, S>>> {
     let args = Args::<CS, S>::parse();
-    let secp = Secp256k1::default();
 
-    let mut index = KeychainTxOutIndex::<Keychain>::default();
-
-    // TODO: descriptors are already stored in the db, so we shouldn't re-insert
-    // them in the index here. However, the keymap is not stored in the database.
-    let (descriptor, mut keymap) =
-        Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &args.descriptor)?;
-    let _ = index.insert_descriptor(Keychain::External, descriptor)?;
+    match args.command {
+        // initialize new db
+        Commands::Init { .. } => initialize::<CS, S>(args, db_magic, db_path).map(|_| None),
+        // generate keys
+        Commands::Generate { network } => generate_bip86_helper(network).map(|_| None),
+        // try load
+        _ => {
+            let mut db =
+                Store::<ChangeSet>::open(db_magic, db_path).context("could not open file store")?;
+            let changeset = db.aggregate_changesets()?.expect("db must not be empty");
+
+            let network = changeset.network.expect("changeset network");
+
+            let chain = Mutex::new({
+                let (mut chain, _) =
+                    LocalChain::from_genesis_hash(constants::genesis_block(network).block_hash());
+                chain.apply_changeset(&changeset.local_chain)?;
+                chain
+            });
+
+            let graph = Mutex::new({
+                // insert descriptors and apply loaded changeset
+                let mut index = KeychainTxOutIndex::default();
+                if let Some(desc) = changeset.descriptor {
+                    index.insert_descriptor(Keychain::External, desc)?;
+                }
+                if let Some(change_desc) = changeset.change_descriptor {
+                    index.insert_descriptor(Keychain::Internal, change_desc)?;
+                }
+                let mut graph = KeychainTxGraph::new(index);
+                graph.apply_changeset(indexed_tx_graph::ChangeSet {
+                    tx_graph: changeset.tx_graph,
+                    indexer: changeset.indexer,
+                });
+                graph
+            });
+
+            let db = Mutex::new(db);
+
+            Ok(Some(Init {
+                args,
+                graph,
+                chain,
+                db,
+                network,
+            }))
+        }
+    }
+}
 
-    if let Some((internal_descriptor, internal_keymap)) = args
-        .change_descriptor
-        .as_ref()
-        .map(|desc_str| Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, desc_str))
-        .transpose()?
+/// Initialize db backend.
+fn initialize<CS, S>(args: Args<CS, S>, db_magic: &[u8], db_path: &str) -> anyhow::Result<()>
+where
+    CS: clap::Subcommand,
+    S: clap::Args,
+{
+    if let Commands::Init {
+        network,
+        descriptor,
+        change_descriptor,
+    } = args.command
     {
-        keymap.extend(internal_keymap);
-        let _ = index.insert_descriptor(Keychain::Internal, internal_descriptor)?;
+        let mut changeset = ChangeSet::default();
+
+        // parse descriptors
+        let secp = Secp256k1::new();
+        let mut index = KeychainTxOutIndex::default();
+        let (descriptor, _) =
+            Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &descriptor)?;
+        let _ = index.insert_descriptor(Keychain::External, descriptor.clone())?;
+        changeset.descriptor = Some(descriptor);
+
+        if let Some(desc) = change_descriptor {
+            let (change_descriptor, _) =
+                Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &desc)?;
+            let _ = index.insert_descriptor(Keychain::Internal, change_descriptor.clone())?;
+            changeset.change_descriptor = Some(change_descriptor);
+        }
+
+        // create new
+        let (_, chain_changeset) =
+            LocalChain::from_genesis_hash(constants::genesis_block(network).block_hash());
+        changeset.network = Some(network);
+        changeset.local_chain = chain_changeset;
+        let mut db = Store::<ChangeSet>::create_new(db_magic, db_path)?;
+        db.append_changeset(&changeset)?;
+        println!("New database {db_path}");
     }
 
-    let mut db_backend = match Store::<C>::open_or_create_new(db_magic, &args.db_path) {
-        Ok(db_backend) => db_backend,
-        // we cannot return `err` directly as it has lifetime `'m`
-        Err(err) => return Err(anyhow::anyhow!("failed to init db backend: {:?}", err)),
+    Ok(())
+}
+
+/// Generate BIP86 descriptors.
+fn generate_bip86_helper(network: impl Into<NetworkKind>) -> anyhow::Result<()> {
+    let secp = Secp256k1::new();
+    let mut seed = [0x00; 32];
+    thread_rng().fill_bytes(&mut seed);
+
+    let m = bip32::Xpriv::new_master(network, &seed)?;
+    let fp = m.fingerprint(&secp);
+    let path = if m.network.is_mainnet() {
+        "86h/0h/0h"
+    } else {
+        "86h/1h/0h"
     };
 
-    let init_changeset = db_backend.aggregate_changesets()?.unwrap_or_default();
+    let descriptors: Vec<String> = [0, 1]
+        .iter()
+        .map(|i| format!("tr([{fp}]{m}/{path}/{i}/*)"))
+        .collect();
+    let external_desc = &descriptors[0];
+    let internal_desc = &descriptors[1];
+    let (descriptor, keymap) =
+        <Descriptor<DescriptorPublicKey>>::parse_descriptor(&secp, external_desc)?;
+    let (internal_descriptor, internal_keymap) =
+        <Descriptor<DescriptorPublicKey>>::parse_descriptor(&secp, internal_desc)?;
+    println!("Public");
+    println!("{}", descriptor);
+    println!("{}", internal_descriptor);
+    println!("\nPrivate");
+    println!("{}", descriptor.to_string_with_secret(&keymap));
+    println!(
+        "{}",
+        internal_descriptor.to_string_with_secret(&internal_keymap)
+    );
+
+    Ok(())
+}
 
-    Ok(Init {
-        args,
-        keymap,
-        index,
-        db: Mutex::new(db_backend),
-        init_changeset,
-    })
+impl Merge for ChangeSet {
+    fn merge(&mut self, other: Self) {
+        if other.descriptor.is_some() {
+            self.descriptor = other.descriptor;
+        }
+        if other.change_descriptor.is_some() {
+            self.change_descriptor = other.change_descriptor;
+        }
+        if other.network.is_some() {
+            self.network = other.network;
+        }
+        Merge::merge(&mut self.local_chain, other.local_chain);
+        Merge::merge(&mut self.tx_graph, other.tx_graph);
+        Merge::merge(&mut self.indexer, other.indexer);
+    }
+
+    fn is_empty(&self) -> bool {
+        self.descriptor.is_none()
+            && self.change_descriptor.is_none()
+            && self.network.is_none()
+            && self.local_chain.is_empty()
+            && self.tx_graph.is_empty()
+            && self.indexer.is_empty()
+    }
 }
index 083a4fe463a9de5af372560746a9853f2b5afa1b..cda8c55264399492b43e37d9a3858502edfac547 100644 (file)
@@ -1,14 +1,9 @@
-use std::{
-    io::{self, Write},
-    sync::Mutex,
-};
+use std::io::{self, Write};
 
 use bdk_chain::{
-    bitcoin::{constants::genesis_block, Address, Network, Txid},
+    bitcoin::{Address, Network, Txid},
     collections::BTreeSet,
-    indexed_tx_graph::{self, IndexedTxGraph},
-    indexer::keychain_txout,
-    local_chain::{self, LocalChain},
+    indexed_tx_graph,
     spk_client::{FullScanRequest, SyncRequest},
     ConfirmationBlockTime, Merge,
 };
@@ -17,9 +12,10 @@ use bdk_electrum::{
     BdkElectrumClient,
 };
 use example_cli::{
+    self,
     anyhow::{self, Context},
     clap::{self, Parser, Subcommand},
-    Keychain,
+    ChangeSet, Keychain,
 };
 
 const DB_MAGIC: &[u8] = b"bdk_example_electrum";
@@ -98,46 +94,28 @@ pub struct ScanOptions {
     pub batch_size: usize,
 }
 
-type ChangeSet = (
-    local_chain::ChangeSet,
-    indexed_tx_graph::ChangeSet<ConfirmationBlockTime, keychain_txout::ChangeSet>,
-);
-
 fn main() -> anyhow::Result<()> {
     let example_cli::Init {
         args,
-        keymap,
-        index,
+        graph,
+        chain,
         db,
-        init_changeset,
-    } = example_cli::init::<ElectrumCommands, ElectrumArgs, ChangeSet>(DB_MAGIC, DB_PATH)?;
-
-    let (disk_local_chain, disk_tx_graph) = init_changeset;
-
-    let graph = Mutex::new({
-        let mut graph = IndexedTxGraph::new(index);
-        graph.apply_changeset(disk_tx_graph);
-        graph
-    });
-
-    let chain = Mutex::new({
-        let genesis_hash = genesis_block(args.network).block_hash();
-        let (mut chain, _) = LocalChain::from_genesis_hash(genesis_hash);
-        chain.apply_changeset(&disk_local_chain)?;
-        chain
-    });
+        network,
+    } = match example_cli::init_or_load::<ElectrumCommands, ElectrumArgs>(DB_MAGIC, DB_PATH)? {
+        Some(init) => init,
+        None => return Ok(()),
+    };
 
     let electrum_cmd = match &args.command {
         example_cli::Commands::ChainSpecific(electrum_cmd) => electrum_cmd,
         general_cmd => {
             return example_cli::handle_commands(
                 &graph,
-                &db,
                 &chain,
-                &keymap,
-                args.network,
+                &db,
+                network,
                 |electrum_args, tx| {
-                    let client = electrum_args.client(args.network)?;
+                    let client = electrum_args.client(network)?;
                     client.transaction_broadcast(tx)?;
                     Ok(())
                 },
@@ -146,7 +124,7 @@ fn main() -> anyhow::Result<()> {
         }
     };
 
-    let client = BdkElectrumClient::new(electrum_cmd.electrum_args().client(args.network)?);
+    let client = BdkElectrumClient::new(electrum_cmd.electrum_args().client(network)?);
 
     // Tell the electrum client about the txs we've already got locally so it doesn't re-download them
     client.populate_tx_cache(&*graph.lock().unwrap());
@@ -244,7 +222,7 @@ fn main() -> anyhow::Result<()> {
                     request.chain_spks(unused_spks.into_iter().map(move |((k, spk_i), spk)| {
                         eprint!(
                             "Checking if address {} {}:{} has been used",
-                            Address::from_script(&spk, args.network).unwrap(),
+                            Address::from_script(&spk, network).unwrap(),
                             k,
                             spk_i,
                         );
@@ -345,7 +323,12 @@ fn main() -> anyhow::Result<()> {
         }
         indexed_tx_graph_changeset.merge(graph.apply_update(graph_update));
 
-        (chain_changeset, indexed_tx_graph_changeset)
+        ChangeSet {
+            local_chain: chain_changeset,
+            tx_graph: indexed_tx_graph_changeset.tx_graph,
+            indexer: indexed_tx_graph_changeset.indexer,
+            ..Default::default()
+        }
     };
 
     let mut db = db.lock().unwrap();
index af64226899eccaee1673c34ac2f8a137e00f1faf..608e58d11e8842cf1bc3cc807410a803771539d1 100644 (file)
@@ -1,40 +1,29 @@
 use std::{
     collections::BTreeSet,
     io::{self, Write},
-    sync::Mutex,
 };
 
 use bdk_chain::{
-    bitcoin::{constants::genesis_block, Address, Network, Txid},
-    indexed_tx_graph::{self, IndexedTxGraph},
-    indexer::keychain_txout,
-    local_chain::{self, LocalChain},
+    bitcoin::{Address, Network, Txid},
     spk_client::{FullScanRequest, SyncRequest},
-    ConfirmationBlockTime, Merge,
+    Merge,
 };
-
 use bdk_esplora::{esplora_client, EsploraExt};
-
 use example_cli::{
     anyhow::{self, Context},
     clap::{self, Parser, Subcommand},
-    Keychain,
+    ChangeSet, Keychain,
 };
 
 const DB_MAGIC: &[u8] = b"bdk_example_esplora";
-const DB_PATH: &str = "bdk_example_esplora.db";
-
-type ChangeSet = (
-    local_chain::ChangeSet,
-    indexed_tx_graph::ChangeSet<ConfirmationBlockTime, keychain_txout::ChangeSet>,
-);
+const DB_PATH: &str = ".bdk_example_esplora.db";
 
 #[derive(Subcommand, Debug, Clone)]
 enum EsploraCommands {
     /// Scans the addresses in the wallet using the esplora API.
     Scan {
         /// When a gap this large has been found for a keychain, it will stop.
-        #[clap(long, default_value = "5")]
+        #[clap(long, short = 'g', default_value = "10")]
         stop_gap: usize,
         #[clap(flatten)]
         scan_options: ScanOptions,
@@ -73,8 +62,8 @@ impl EsploraCommands {
 
 #[derive(clap::Args, Debug, Clone)]
 pub struct EsploraArgs {
-    /// The esplora url endpoint to connect to e.g. `<https://blockstream.info/api>`
-    /// If not provided it'll be set to a default for the network provided
+    /// The esplora url endpoint to connect to.
+    #[clap(long, short = 'u', env = "ESPLORA_SERVER")]
     esplora_url: Option<String>,
 }
 
@@ -103,29 +92,14 @@ pub struct ScanOptions {
 fn main() -> anyhow::Result<()> {
     let example_cli::Init {
         args,
-        keymap,
-        index,
+        graph,
+        chain,
         db,
-        init_changeset,
-    } = example_cli::init::<EsploraCommands, EsploraArgs, ChangeSet>(DB_MAGIC, DB_PATH)?;
-
-    let genesis_hash = genesis_block(args.network).block_hash();
-
-    let (init_chain_changeset, init_indexed_tx_graph_changeset) = init_changeset;
-
-    // Construct `IndexedTxGraph` and `LocalChain` with our initial changeset. They are wrapped in
-    // `Mutex` to display how they can be used in a multithreaded context. Technically the mutexes
-    // aren't strictly needed here.
-    let graph = Mutex::new({
-        let mut graph = IndexedTxGraph::new(index);
-        graph.apply_changeset(init_indexed_tx_graph_changeset);
-        graph
-    });
-    let chain = Mutex::new({
-        let (mut chain, _) = LocalChain::from_genesis_hash(genesis_hash);
-        chain.apply_changeset(&init_chain_changeset)?;
-        chain
-    });
+        network,
+    } = match example_cli::init_or_load::<EsploraCommands, EsploraArgs>(DB_MAGIC, DB_PATH)? {
+        Some(init) => init,
+        None => return Ok(()),
+    };
 
     let esplora_cmd = match &args.command {
         // These are commands that are handled by this example (sync, scan).
@@ -134,12 +108,11 @@ fn main() -> anyhow::Result<()> {
         general_cmd => {
             return example_cli::handle_commands(
                 &graph,
-                &db,
                 &chain,
-                &keymap,
-                args.network,
+                &db,
+                network,
                 |esplora_args, tx| {
-                    let client = esplora_args.client(args.network)?;
+                    let client = esplora_args.client(network)?;
                     client
                         .broadcast(tx)
                         .map(|_| ())
@@ -150,7 +123,7 @@ fn main() -> anyhow::Result<()> {
         }
     };
 
-    let client = esplora_cmd.esplora_args().client(args.network)?;
+    let client = esplora_cmd.esplora_args().client(network)?;
     // Prepare the `IndexedTxGraph` and `LocalChain` updates based on whether we are scanning or
     // syncing.
     //
@@ -264,7 +237,7 @@ fn main() -> anyhow::Result<()> {
                         request.chain_spks(unused_spks.into_iter().map(move |((k, i), spk)| {
                             eprint!(
                                 "Checking if address {} {}:{} has been used",
-                                Address::from_script(&spk, args.network).unwrap(),
+                                Address::from_script(&spk, network).unwrap(),
                                 k,
                                 i,
                             );
@@ -361,6 +334,11 @@ fn main() -> anyhow::Result<()> {
 
     // We persist the changes
     let mut db = db.lock().unwrap();
-    db.append_changeset(&(local_chain_changeset, indexed_tx_graph_changeset))?;
+    db.append_changeset(&ChangeSet {
+        local_chain: local_chain_changeset,
+        tx_graph: indexed_tx_graph_changeset.tx_graph,
+        indexer: indexed_tx_graph_changeset.indexer,
+        ..Default::default()
+    })?;
     Ok(())
 }
diff --git a/nursery/coin_select/Cargo.toml b/nursery/coin_select/Cargo.toml
deleted file mode 100644 (file)
index 0830ad9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name = "bdk_coin_select"
-version = "0.0.1"
-authors = [ "LLFourn <lloyd.fourn@gmail.com>" ]
-
-[dependencies]
-bdk_chain = { path = "../../crates/chain" }
-
-[features]
-default = ["std"]
-std = []
diff --git a/nursery/coin_select/src/bnb.rs b/nursery/coin_select/src/bnb.rs
deleted file mode 100644 (file)
index d355894..0000000
+++ /dev/null
@@ -1,645 +0,0 @@
-use super::*;
-
-/// Strategy in which we should branch.
-pub enum BranchStrategy {
-    /// We continue exploring subtrees of this node, starting with the inclusion branch.
-    Continue,
-    /// We continue exploring ONLY the omission branch of this node, skipping the inclusion branch.
-    SkipInclusion,
-    /// We skip both the inclusion and omission branches of this node.
-    SkipBoth,
-}
-
-impl BranchStrategy {
-    pub fn will_continue(&self) -> bool {
-        matches!(self, Self::Continue | Self::SkipInclusion)
-    }
-}
-
-/// Closure to decide the branching strategy, alongside a score (if the current selection is a
-/// candidate solution).
-pub type DecideStrategy<'c, S> = dyn Fn(&Bnb<'c, S>) -> (BranchStrategy, Option<S>);
-
-/// [`Bnb`] represents the current state of the BnB algorithm.
-pub struct Bnb<'c, S> {
-    pub pool: Vec<(usize, &'c WeightedValue)>,
-    pub pool_pos: usize,
-    pub best_score: S,
-
-    pub selection: CoinSelector<'c>,
-    pub rem_abs: u64,
-    pub rem_eff: i64,
-}
-
-impl<'c, S: Ord> Bnb<'c, S> {
-    /// Creates a new [`Bnb`].
-    pub fn new(selector: CoinSelector<'c>, pool: Vec<(usize, &'c WeightedValue)>, max: S) -> Self {
-        let (rem_abs, rem_eff) = pool.iter().fold((0, 0), |(abs, eff), (_, c)| {
-            (
-                abs + c.value,
-                eff + c.effective_value(selector.opts.target_feerate),
-            )
-        });
-
-        Self {
-            pool,
-            pool_pos: 0,
-            best_score: max,
-            selection: selector,
-            rem_abs,
-            rem_eff,
-        }
-    }
-
-    /// Turns our [`Bnb`] state into an iterator.
-    ///
-    /// `strategy` should assess our current selection/node and determine the branching strategy and
-    /// whether this selection is a candidate solution (if so, return the selection score).
-    pub fn into_iter<'f>(self, strategy: &'f DecideStrategy<'c, S>) -> BnbIter<'c, 'f, S> {
-        BnbIter {
-            state: self,
-            done: false,
-            strategy,
-        }
-    }
-
-    /// Attempt to backtrack to the previously selected node's omission branch, return false
-    /// otherwise (no more solutions).
-    pub fn backtrack(&mut self) -> bool {
-        (0..self.pool_pos).rev().any(|pos| {
-            let (index, candidate) = self.pool[pos];
-
-            if self.selection.is_selected(index) {
-                // deselect the last `pos`, so the next round will check the omission branch
-                self.pool_pos = pos;
-                self.selection.deselect(index);
-                true
-            } else {
-                self.rem_abs += candidate.value;
-                self.rem_eff += candidate.effective_value(self.selection.opts.target_feerate);
-                false
-            }
-        })
-    }
-
-    /// Continue down this branch and skip the inclusion branch if specified.
-    pub fn forward(&mut self, skip: bool) {
-        let (index, candidate) = self.pool[self.pool_pos];
-        self.rem_abs -= candidate.value;
-        self.rem_eff -= candidate.effective_value(self.selection.opts.target_feerate);
-
-        if !skip {
-            self.selection.select(index);
-        }
-    }
-
-    /// Compare the advertised score with the current best. The new best will be the smaller value. Return true
-    /// if best is replaced.
-    pub fn advertise_new_score(&mut self, score: S) -> bool {
-        if score <= self.best_score {
-            self.best_score = score;
-            return true;
-        }
-        false
-    }
-}
-
-pub struct BnbIter<'c, 'f, S> {
-    state: Bnb<'c, S>,
-    done: bool,
-
-    /// Check our current selection (node) and returns the branching strategy alongside a score
-    /// (if the current selection is a candidate solution).
-    strategy: &'f DecideStrategy<'c, S>,
-}
-
-impl<'c, 'f, S: Ord + Copy + Display> Iterator for BnbIter<'c, 'f, S> {
-    type Item = Option<CoinSelector<'c>>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.done {
-            return None;
-        }
-
-        let (strategy, score) = (self.strategy)(&self.state);
-
-        let mut found_best = Option::<CoinSelector>::None;
-
-        if let Some(score) = score {
-            if self.state.advertise_new_score(score) {
-                found_best = Some(self.state.selection.clone());
-            }
-        }
-
-        debug_assert!(
-            !strategy.will_continue() || self.state.pool_pos < self.state.pool.len(),
-            "Faulty strategy implementation! Strategy suggested that we continue traversing, however, we have already reached the end of the candidates pool! pool_len={}, pool_pos={}",
-            self.state.pool.len(), self.state.pool_pos,
-        );
-
-        match strategy {
-            BranchStrategy::Continue => {
-                self.state.forward(false);
-            }
-            BranchStrategy::SkipInclusion => {
-                self.state.forward(true);
-            }
-            BranchStrategy::SkipBoth => {
-                if !self.state.backtrack() {
-                    self.done = true;
-                }
-            }
-        };
-
-        // increment selection pool position for next round
-        self.state.pool_pos += 1;
-
-        if found_best.is_some() || !self.done {
-            Some(found_best)
-        } else {
-            // we have traversed all branches
-            None
-        }
-    }
-}
-
-/// Determines how we should limit rounds of branch and bound.
-pub enum BnbLimit {
-    Rounds(usize),
-    #[cfg(feature = "std")]
-    Duration(core::time::Duration),
-}
-
-impl From<usize> for BnbLimit {
-    fn from(v: usize) -> Self {
-        Self::Rounds(v)
-    }
-}
-
-#[cfg(feature = "std")]
-impl From<core::time::Duration> for BnbLimit {
-    fn from(v: core::time::Duration) -> Self {
-        Self::Duration(v)
-    }
-}
-
-/// This is a variation of the Branch and Bound Coin Selection algorithm designed by Murch (as seen
-/// in Bitcoin Core).
-///
-/// The differences are as follows:
-/// * In addition to working with effective values, we also work with absolute values.
-///   This way, we can use bounds of the absolute values to enforce `min_absolute_fee` (which is used by
-///   RBF), and `max_extra_target` (which can be used to increase the possible solution set, given
-///   that the sender is okay with sending extra to the receiver).
-///
-/// Murch's Master Thesis: <https://murch.one/wp-content/uploads/2016/11/erhardt2016coinselection.pdf>
-/// Bitcoin Core Implementation: <https://github.com/bitcoin/bitcoin/blob/23.x/src/wallet/coinselection.cpp#L65>
-///
-/// TODO: Another optimization we could do is figure out candidates with the smallest waste, and
-/// if we find a result with waste equal to this, we can just break.
-pub fn coin_select_bnb<L>(limit: L, selector: CoinSelector) -> Option<CoinSelector>
-where
-    L: Into<BnbLimit>,
-{
-    let opts = selector.opts;
-
-    // prepare the pool of candidates to select from:
-    // * filter out candidates with negative/zero effective values
-    // * sort candidates by descending effective value
-    let pool = {
-        let mut pool = selector
-            .unselected()
-            .filter(|(_, c)| c.effective_value(opts.target_feerate) > 0)
-            .collect::<Vec<_>>();
-        pool.sort_unstable_by(|(_, a), (_, b)| {
-            let a = a.effective_value(opts.target_feerate);
-            let b = b.effective_value(opts.target_feerate);
-            b.cmp(&a)
-        });
-        pool
-    };
-
-    let feerate_decreases = opts.target_feerate > opts.long_term_feerate();
-
-    let target_abs = opts.target_value.unwrap_or(0) + opts.min_absolute_fee;
-    let target_eff = selector.effective_target();
-
-    let upper_bound_abs = target_abs + (opts.drain_weight as f32 * opts.target_feerate) as u64;
-    let upper_bound_eff = target_eff + opts.drain_waste();
-
-    let strategy = move |bnb: &Bnb<i64>| -> (BranchStrategy, Option<i64>) {
-        let selected_abs = bnb.selection.selected_absolute_value();
-        let selected_eff = bnb.selection.selected_effective_value();
-
-        // backtrack if the remaining value is not enough to reach the target
-        if selected_abs + bnb.rem_abs < target_abs || selected_eff + bnb.rem_eff < target_eff {
-            return (BranchStrategy::SkipBoth, None);
-        }
-
-        // backtrack if the selected value has already surpassed upper bounds
-        if selected_abs > upper_bound_abs && selected_eff > upper_bound_eff {
-            return (BranchStrategy::SkipBoth, None);
-        }
-
-        let selected_waste = bnb.selection.selected_waste();
-
-        // when feerate decreases, waste without excess is guaranteed to increase with each
-        // selection. So if we have already surpassed the best score, we can backtrack.
-        if feerate_decreases && selected_waste > bnb.best_score {
-            return (BranchStrategy::SkipBoth, None);
-        }
-
-        // solution?
-        if selected_abs >= target_abs && selected_eff >= target_eff {
-            let waste = selected_waste + bnb.selection.current_excess();
-            return (BranchStrategy::SkipBoth, Some(waste));
-        }
-
-        // early bailout optimization:
-        // If the candidate at the previous position is NOT selected and has the same weight and
-        // value as the current candidate, we can skip selecting the current candidate.
-        if bnb.pool_pos > 0 && !bnb.selection.is_empty() {
-            let (_, candidate) = bnb.pool[bnb.pool_pos];
-            let (prev_index, prev_candidate) = bnb.pool[bnb.pool_pos - 1];
-
-            if !bnb.selection.is_selected(prev_index)
-                && candidate.value == prev_candidate.value
-                && candidate.weight == prev_candidate.weight
-            {
-                return (BranchStrategy::SkipInclusion, None);
-            }
-        }
-
-        // check out the inclusion branch first
-        (BranchStrategy::Continue, None)
-    };
-
-    // determine the sum of absolute and effective values for the current selection
-    let (selected_abs, selected_eff) = selector.selected().fold((0, 0), |(abs, eff), (_, c)| {
-        (
-            abs + c.value,
-            eff + c.effective_value(selector.opts.target_feerate),
-        )
-    });
-
-    let bnb = Bnb::new(selector, pool, i64::MAX);
-
-    // not enough to select anyway
-    if selected_abs + bnb.rem_abs < target_abs || selected_eff + bnb.rem_eff < target_eff {
-        return None;
-    }
-
-    match limit.into() {
-        BnbLimit::Rounds(rounds) => {
-            bnb.into_iter(&strategy)
-                .take(rounds)
-                .reduce(|b, c| if c.is_some() { c } else { b })
-        }
-        #[cfg(feature = "std")]
-        BnbLimit::Duration(duration) => {
-            let start = std::time::SystemTime::now();
-            bnb.into_iter(&strategy)
-                .take_while(|_| start.elapsed().expect("failed to get system time") <= duration)
-                .reduce(|b, c| if c.is_some() { c } else { b })
-        }
-    }?
-}
-
-// #[cfg(all(test, feature = "miniscript"))]
-// mod test {
-//     use bitcoin::secp256k1::Secp256k1;
-//
-//     use crate::coin_select::{evaluate_cs::evaluate, ExcessStrategyKind};
-//
-//     use super::{
-//         coin_select_bnb,
-//         evaluate_cs::{Evaluation, EvaluationError},
-//         tester::Tester,
-//         CoinSelector, CoinSelectorOpt, Vec, WeightedValue,
-//     };
-//
-//     fn tester() -> Tester {
-//         const DESC_STR: &str = "tr(xprv9uBuvtdjghkz8D1qzsSXS9Vs64mqrUnXqzNccj2xcvnCHPpXKYE1U2Gbh9CDHk8UPyF2VuXpVkDA7fk5ZP4Hd9KnhUmTscKmhee9Dp5sBMK)";
-//         Tester::new(&Secp256k1::default(), DESC_STR)
-//     }
-//
-//     fn evaluate_bnb(
-//         initial_selector: CoinSelector,
-//         max_tries: usize,
-//     ) -> Result<Evaluation, EvaluationError> {
-//         evaluate(initial_selector, |cs| {
-//             coin_select_bnb(max_tries, cs.clone()).map_or(false, |new_cs| {
-//                 *cs = new_cs;
-//                 true
-//             })
-//         })
-//     }
-//
-//     #[test]
-//     fn not_enough_coins() {
-//         let t = tester();
-//         let candidates: Vec<WeightedValue> = vec![
-//             t.gen_candidate(0, 100_000).into(),
-//             t.gen_candidate(1, 100_000).into(),
-//         ];
-//         let opts = t.gen_opts(200_000);
-//         let selector = CoinSelector::new(&candidates, &opts);
-//         assert!(!coin_select_bnb(10_000, selector).is_some());
-//     }
-//
-//     #[test]
-//     fn exactly_enough_coins_preselected() {
-//         let t = tester();
-//         let candidates: Vec<WeightedValue> = vec![
-//             t.gen_candidate(0, 100_000).into(), // to preselect
-//             t.gen_candidate(1, 100_000).into(), // to preselect
-//             t.gen_candidate(2, 100_000).into(),
-//         ];
-//         let opts = CoinSelectorOpt {
-//             target_feerate: 0.0,
-//             ..t.gen_opts(200_000)
-//         };
-//         let selector = {
-//             let mut selector = CoinSelector::new(&candidates, &opts);
-//             selector.select(0); // preselect
-//             selector.select(1); // preselect
-//             selector
-//         };
-//
-//         let evaluation = evaluate_bnb(selector, 10_000).expect("eval failed");
-//         println!("{}", evaluation);
-//         assert_eq!(evaluation.solution.selected, (0..=1).collect());
-//         assert_eq!(evaluation.solution.excess_strategies.len(), 1);
-//         assert_eq!(
-//             evaluation.feerate_offset(ExcessStrategyKind::ToFee).floor(),
-//             0.0
-//         );
-//     }
-//
-//     /// `cost_of_change` acts as the upper-bound in Bnb; we check whether these boundaries are
-//     /// enforced in code
-//     #[test]
-//     fn cost_of_change() {
-//         let t = tester();
-//         let candidates: Vec<WeightedValue> = vec![
-//             t.gen_candidate(0, 200_000).into(),
-//             t.gen_candidate(1, 200_000).into(),
-//             t.gen_candidate(2, 200_000).into(),
-//         ];
-//
-//         // lowest and highest possible `recipient_value` opts for derived `drain_waste`, assuming
-//         // that we want 2 candidates selected
-//         let (lowest_opts, highest_opts) = {
-//             let opts = t.gen_opts(0);
-//
-//             let fee_from_inputs =
-//                 (candidates[0].weight as f32 * opts.target_feerate).ceil() as u64 * 2;
-//             let fee_from_template =
-//                 ((opts.base_weight + 2) as f32 * opts.target_feerate).ceil() as u64;
-//
-//             let lowest_opts = CoinSelectorOpt {
-//                 target_value: Some(
-//                     400_000 - fee_from_inputs - fee_from_template - opts.drain_waste() as u64,
-//                 ),
-//                 ..opts
-//             };
-//
-//             let highest_opts = CoinSelectorOpt {
-//                 target_value: Some(400_000 - fee_from_inputs - fee_from_template),
-//                 ..opts
-//             };
-//
-//             (lowest_opts, highest_opts)
-//         };
-//
-//         // test lowest possible target we can select
-//         let lowest_eval = evaluate_bnb(CoinSelector::new(&candidates, &lowest_opts), 10_000);
-//         assert!(lowest_eval.is_ok());
-//         let lowest_eval = lowest_eval.unwrap();
-//         println!("LB {}", lowest_eval);
-//         assert_eq!(lowest_eval.solution.selected.len(), 2);
-//         assert_eq!(lowest_eval.solution.excess_strategies.len(), 1);
-//         assert_eq!(
-//             lowest_eval
-//                 .feerate_offset(ExcessStrategyKind::ToFee)
-//                 .floor(),
-//             0.0
-//         );
-//
-//         // test the highest possible target we can select
-//         let highest_eval = evaluate_bnb(CoinSelector::new(&candidates, &highest_opts), 10_000);
-//         assert!(highest_eval.is_ok());
-//         let highest_eval = highest_eval.unwrap();
-//         println!("UB {}", highest_eval);
-//         assert_eq!(highest_eval.solution.selected.len(), 2);
-//         assert_eq!(highest_eval.solution.excess_strategies.len(), 1);
-//         assert_eq!(
-//             highest_eval
-//                 .feerate_offset(ExcessStrategyKind::ToFee)
-//                 .floor(),
-//             0.0
-//         );
-//
-//         // test lower out of bounds
-//         let loob_opts = CoinSelectorOpt {
-//             target_value: lowest_opts.target_value.map(|v| v - 1),
-//             ..lowest_opts
-//         };
-//         let loob_eval = evaluate_bnb(CoinSelector::new(&candidates, &loob_opts), 10_000);
-//         assert!(loob_eval.is_err());
-//         println!("Lower OOB: {}", loob_eval.unwrap_err());
-//
-//         // test upper out of bounds
-//         let uoob_opts = CoinSelectorOpt {
-//             target_value: highest_opts.target_value.map(|v| v + 1),
-//             ..highest_opts
-//         };
-//         let uoob_eval = evaluate_bnb(CoinSelector::new(&candidates, &uoob_opts), 10_000);
-//         assert!(uoob_eval.is_err());
-//         println!("Upper OOB: {}", uoob_eval.unwrap_err());
-//     }
-//
-//     #[test]
-//     fn try_select() {
-//         let t = tester();
-//         let candidates: Vec<WeightedValue> = vec![
-//             t.gen_candidate(0, 300_000).into(),
-//             t.gen_candidate(1, 300_000).into(),
-//             t.gen_candidate(2, 300_000).into(),
-//             t.gen_candidate(3, 200_000).into(),
-//             t.gen_candidate(4, 200_000).into(),
-//         ];
-//         let make_opts = |v: u64| -> CoinSelectorOpt {
-//             CoinSelectorOpt {
-//                 target_feerate: 0.0,
-//                 ..t.gen_opts(v)
-//             }
-//         };
-//
-//         let test_cases = vec![
-//             (make_opts(100_000), false, 0),
-//             (make_opts(200_000), true, 1),
-//             (make_opts(300_000), true, 1),
-//             (make_opts(500_000), true, 2),
-//             (make_opts(1_000_000), true, 4),
-//             (make_opts(1_200_000), false, 0),
-//             (make_opts(1_300_000), true, 5),
-//             (make_opts(1_400_000), false, 0),
-//         ];
-//
-//         for (opts, expect_solution, expect_selected) in test_cases {
-//             let res = evaluate_bnb(CoinSelector::new(&candidates, &opts), 10_000);
-//             assert_eq!(res.is_ok(), expect_solution);
-//
-//             match res {
-//                 Ok(eval) => {
-//                     println!("{}", eval);
-//                     assert_eq!(eval.feerate_offset(ExcessStrategyKind::ToFee), 0.0);
-//                     assert_eq!(eval.solution.selected.len(), expect_selected as _);
-//                 }
-//                 Err(err) => println!("expected failure: {}", err),
-//             }
-//         }
-//     }
-//
-//     #[test]
-//     fn early_bailout_optimization() {
-//         let t = tester();
-//
-//         // target: 300_000
-//         // candidates: 2x of 125_000, 1000x of 100_000, 1x of 50_000
-//         // expected solution: 2x 125_000, 1x 50_000
-//         // set bnb max tries: 1100, should succeed
-//         let candidates = {
-//             let mut candidates: Vec<WeightedValue> = vec![
-//                 t.gen_candidate(0, 125_000).into(),
-//                 t.gen_candidate(1, 125_000).into(),
-//                 t.gen_candidate(2, 50_000).into(),
-//             ];
-//             (3..3 + 1000_u32)
-//                 .for_each(|index| candidates.push(t.gen_candidate(index, 100_000).into()));
-//             candidates
-//         };
-//         let opts = CoinSelectorOpt {
-//             target_feerate: 0.0,
-//             ..t.gen_opts(300_000)
-//         };
-//
-//         let result = evaluate_bnb(CoinSelector::new(&candidates, &opts), 1100);
-//         assert!(result.is_ok());
-//
-//         let eval = result.unwrap();
-//         println!("{}", eval);
-//         assert_eq!(eval.solution.selected, (0..=2).collect());
-//     }
-//
-//     #[test]
-//     fn should_exhaust_iteration() {
-//         static MAX_TRIES: usize = 1000;
-//         let t = tester();
-//         let candidates = (0..MAX_TRIES + 1)
-//             .map(|index| t.gen_candidate(index as _, 10_000).into())
-//             .collect::<Vec<WeightedValue>>();
-//         let opts = t.gen_opts(10_001 * MAX_TRIES as u64);
-//         let result = evaluate_bnb(CoinSelector::new(&candidates, &opts), MAX_TRIES);
-//         assert!(result.is_err());
-//         println!("error as expected: {}", result.unwrap_err());
-//     }
-//
-//     /// Solution should have fee >= min_absolute_fee (or no solution at all)
-//     #[test]
-//     fn min_absolute_fee() {
-//         let t = tester();
-//         let candidates = {
-//             let mut candidates = Vec::new();
-//             t.gen_weighted_values(&mut candidates, 5, 10_000);
-//             t.gen_weighted_values(&mut candidates, 5, 20_000);
-//             t.gen_weighted_values(&mut candidates, 5, 30_000);
-//             t.gen_weighted_values(&mut candidates, 10, 10_300);
-//             t.gen_weighted_values(&mut candidates, 10, 10_500);
-//             t.gen_weighted_values(&mut candidates, 10, 10_700);
-//             t.gen_weighted_values(&mut candidates, 10, 10_900);
-//             t.gen_weighted_values(&mut candidates, 10, 11_000);
-//             t.gen_weighted_values(&mut candidates, 10, 12_000);
-//             t.gen_weighted_values(&mut candidates, 10, 13_000);
-//             candidates
-//         };
-//         let mut opts = CoinSelectorOpt {
-//             min_absolute_fee: 1,
-//             ..t.gen_opts(100_000)
-//         };
-//
-//         (1..=120_u64).for_each(|fee_factor| {
-//             opts.min_absolute_fee = fee_factor * 31;
-//
-//             let result = evaluate_bnb(CoinSelector::new(&candidates, &opts), 21_000);
-//             match result {
-//                 Ok(result) => {
-//                     println!("Solution {}", result);
-//                     let fee = result.solution.excess_strategies[&ExcessStrategyKind::ToFee].fee;
-//                     assert!(fee >= opts.min_absolute_fee);
-//                     assert_eq!(result.solution.excess_strategies.len(), 1);
-//                 }
-//                 Err(err) => {
-//                     println!("No Solution: {}", err);
-//                 }
-//             }
-//         });
-//     }
-//
-//     /// For a decreasing feerate (long-term feerate is lower than effective feerate), we should
-//     /// select less. For increasing feerate (long-term feerate is higher than effective feerate), we
-//     /// should select more.
-//     #[test]
-//     fn feerate_difference() {
-//         let t = tester();
-//         let candidates = {
-//             let mut candidates = Vec::new();
-//             t.gen_weighted_values(&mut candidates, 10, 2_000);
-//             t.gen_weighted_values(&mut candidates, 10, 5_000);
-//             t.gen_weighted_values(&mut candidates, 10, 20_000);
-//             candidates
-//         };
-//
-//         let decreasing_feerate_opts = CoinSelectorOpt {
-//             target_feerate: 1.25,
-//             long_term_feerate: Some(0.25),
-//             ..t.gen_opts(100_000)
-//         };
-//
-//         let increasing_feerate_opts = CoinSelectorOpt {
-//             target_feerate: 0.25,
-//             long_term_feerate: Some(1.25),
-//             ..t.gen_opts(100_000)
-//         };
-//
-//         let decreasing_res = evaluate_bnb(
-//             CoinSelector::new(&candidates, &decreasing_feerate_opts),
-//             21_000,
-//         )
-//         .expect("no result");
-//         let decreasing_len = decreasing_res.solution.selected.len();
-//
-//         let increasing_res = evaluate_bnb(
-//             CoinSelector::new(&candidates, &increasing_feerate_opts),
-//             21_000,
-//         )
-//         .expect("no result");
-//         let increasing_len = increasing_res.solution.selected.len();
-//
-//         println!("decreasing_len: {}", decreasing_len);
-//         println!("increasing_len: {}", increasing_len);
-//         assert!(decreasing_len < increasing_len);
-//     }
-//
-//     /// TODO: UNIMPLEMENTED TESTS:
-//     /// * Excess strategies:
-//     ///     * We should always have `ExcessStrategy::ToFee`.
-//     ///     * We should only have `ExcessStrategy::ToRecipient` when `max_extra_target > 0`.
-//     ///     * We should only have `ExcessStrategy::ToDrain` when `drain_value >= min_drain_value`.
-//     /// * Fuzz
-//     ///     * Solution feerate should never be lower than target feerate
-//     ///     * Solution fee should never be lower than `min_absolute_fee`.
-//     ///     * Preselected should always remain selected
-//     fn _todo() {}
-// }
diff --git a/nursery/coin_select/src/coin_selector.rs b/nursery/coin_select/src/coin_selector.rs
deleted file mode 100644 (file)
index 729f9b2..0000000
+++ /dev/null
@@ -1,617 +0,0 @@
-use super::*;
-
-/// A [`WeightedValue`] represents an input candidate for [`CoinSelector`]. This can either be a
-/// single UTXO, or a group of UTXOs that should be spent together.
-#[derive(Debug, Clone, Copy)]
-pub struct WeightedValue {
-    /// Total value of the UTXO(s) that this [`WeightedValue`] represents.
-    pub value: u64,
-    /// Total weight of including this/these UTXO(s).
-    /// `txin` fields: `prevout`, `nSequence`, `scriptSigLen`, `scriptSig`, `scriptWitnessLen`,
-    /// `scriptWitness` should all be included.
-    pub weight: u32,
-    /// The total number of inputs; so we can calculate extra `varint` weight due to `vin` length changes.
-    pub input_count: usize,
-    /// Whether this [`WeightedValue`] contains at least one segwit spend.
-    pub is_segwit: bool,
-}
-
-impl WeightedValue {
-    /// Create a new [`WeightedValue`] that represents a single input.
-    ///
-    /// `satisfaction_weight` is the weight of `scriptSigLen + scriptSig + scriptWitnessLen +
-    /// scriptWitness`.
-    pub fn new(value: u64, satisfaction_weight: u32, is_segwit: bool) -> WeightedValue {
-        let weight = TXIN_BASE_WEIGHT + satisfaction_weight;
-        WeightedValue {
-            value,
-            weight,
-            input_count: 1,
-            is_segwit,
-        }
-    }
-
-    /// Effective value of this input candidate: `actual_value - input_weight * feerate (sats/wu)`.
-    pub fn effective_value(&self, effective_feerate: f32) -> i64 {
-        // We prefer undershooting the candidate's effective value (so we over-estimate the fee of a
-        // candidate). If we overshoot the candidate's effective value, it may be possible to find a
-        // solution which does not meet the target feerate.
-        self.value as i64 - (self.weight as f32 * effective_feerate).ceil() as i64
-    }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct CoinSelectorOpt {
-    /// The value we need to select.
-    /// If the value is `None`, then the selection will be complete if it can pay for the drain
-    /// output and satisfy the other constraints (e.g., minimum fees).
-    pub target_value: Option<u64>,
-    /// Additional leeway for the target value.
-    pub max_extra_target: u64, // TODO: Maybe out of scope here?
-
-    /// The feerate we should try and achieve in sats per weight unit.
-    pub target_feerate: f32,
-    /// The feerate
-    pub long_term_feerate: Option<f32>, // TODO: Maybe out of scope? (waste)
-    /// The minimum absolute fee. I.e., needed for RBF.
-    pub min_absolute_fee: u64,
-
-    /// The weight of the template transaction, including fixed fields and outputs.
-    pub base_weight: u32,
-    /// Additional weight if we include the drain (change) output.
-    pub drain_weight: u32,
-    /// Weight of spending the drain (change) output in the future.
-    pub spend_drain_weight: u32, // TODO: Maybe out of scope? (waste)
-
-    /// Minimum value allowed for a drain (change) output.
-    pub min_drain_value: u64,
-}
-
-impl CoinSelectorOpt {
-    fn from_weights(base_weight: u32, drain_weight: u32, spend_drain_weight: u32) -> Self {
-        // 0.25 sats/wu == 1 sat/vb
-        let target_feerate = 0.25_f32;
-
-        // set `min_drain_value` to dust limit
-        let min_drain_value =
-            3 * ((drain_weight + spend_drain_weight) as f32 * target_feerate) as u64;
-
-        Self {
-            target_value: None,
-            max_extra_target: 0,
-            target_feerate,
-            long_term_feerate: None,
-            min_absolute_fee: 0,
-            base_weight,
-            drain_weight,
-            spend_drain_weight,
-            min_drain_value,
-        }
-    }
-
-    pub fn fund_outputs(
-        txouts: &[TxOut],
-        drain_output: &TxOut,
-        drain_satisfaction_weight: u32,
-    ) -> Self {
-        let mut tx = Transaction {
-            input: vec![],
-            version: transaction::Version::ONE,
-            lock_time: absolute::LockTime::ZERO,
-            output: txouts.to_vec(),
-        };
-        let base_weight = tx.weight();
-        // Calculating drain_weight like this instead of using .weight()
-        // allows us to take into account the output len varint increase that
-        // might happen when adding a new output
-        let drain_weight = {
-            tx.output.push(drain_output.clone());
-            tx.weight() - base_weight
-        };
-        Self {
-            target_value: if txouts.is_empty() {
-                None
-            } else {
-                Some(txouts.iter().map(|txout| txout.value.to_sat()).sum())
-            },
-            ..Self::from_weights(
-                base_weight.to_wu() as u32,
-                drain_weight.to_wu() as u32,
-                TXIN_BASE_WEIGHT + drain_satisfaction_weight,
-            )
-        }
-    }
-
-    pub fn long_term_feerate(&self) -> f32 {
-        self.long_term_feerate.unwrap_or(self.target_feerate)
-    }
-
-    pub fn drain_waste(&self) -> i64 {
-        (self.drain_weight as f32 * self.target_feerate
-            + self.spend_drain_weight as f32 * self.long_term_feerate()) as i64
-    }
-}
-
-/// [`CoinSelector`] selects and deselects from a set of candidates.
-#[derive(Debug, Clone)]
-pub struct CoinSelector<'a> {
-    pub opts: &'a CoinSelectorOpt,
-    pub candidates: &'a Vec<WeightedValue>,
-    selected: BTreeSet<usize>,
-}
-
-impl<'a> CoinSelector<'a> {
-    pub fn candidate(&self, index: usize) -> &WeightedValue {
-        &self.candidates[index]
-    }
-
-    pub fn new(candidates: &'a Vec<WeightedValue>, opts: &'a CoinSelectorOpt) -> Self {
-        Self {
-            candidates,
-            selected: Default::default(),
-            opts,
-        }
-    }
-
-    pub fn select(&mut self, index: usize) -> bool {
-        assert!(index < self.candidates.len());
-        self.selected.insert(index)
-    }
-
-    pub fn deselect(&mut self, index: usize) -> bool {
-        self.selected.remove(&index)
-    }
-
-    pub fn is_selected(&self, index: usize) -> bool {
-        self.selected.contains(&index)
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.selected.is_empty()
-    }
-
-    /// Weight sum of all selected inputs.
-    pub fn selected_weight(&self) -> u32 {
-        self.selected
-            .iter()
-            .map(|&index| self.candidates[index].weight)
-            .sum()
-    }
-
-    /// Effective value sum of all selected inputs.
-    pub fn selected_effective_value(&self) -> i64 {
-        self.selected
-            .iter()
-            .map(|&index| self.candidates[index].effective_value(self.opts.target_feerate))
-            .sum()
-    }
-
-    /// Absolute value sum of all selected inputs.
-    pub fn selected_absolute_value(&self) -> u64 {
-        self.selected
-            .iter()
-            .map(|&index| self.candidates[index].value)
-            .sum()
-    }
-
-    /// Waste sum of all selected inputs.
-    pub fn selected_waste(&self) -> i64 {
-        (self.selected_weight() as f32 * (self.opts.target_feerate - self.opts.long_term_feerate()))
-            as i64
-    }
-
-    /// Current weight of template tx + selected inputs.
-    pub fn current_weight(&self) -> u32 {
-        let witness_header_extra_weight = self
-            .selected()
-            .find(|(_, wv)| wv.is_segwit)
-            .map(|_| 2)
-            .unwrap_or(0);
-        let vin_count_varint_extra_weight = {
-            let input_count = self.selected().map(|(_, wv)| wv.input_count).sum::<usize>();
-            (varint_size(input_count) - 1) * 4
-        };
-        self.opts.base_weight
-            + self.selected_weight()
-            + witness_header_extra_weight
-            + vin_count_varint_extra_weight
-    }
-
-    /// Current excess.
-    pub fn current_excess(&self) -> i64 {
-        self.selected_effective_value() - self.effective_target()
-    }
-
-    /// This is the effective target value.
-    pub fn effective_target(&self) -> i64 {
-        let (has_segwit, max_input_count) = self
-            .candidates
-            .iter()
-            .fold((false, 0_usize), |(is_segwit, input_count), c| {
-                (is_segwit || c.is_segwit, input_count + c.input_count)
-            });
-
-        let effective_base_weight = self.opts.base_weight
-            + if has_segwit { 2_u32 } else { 0_u32 }
-            + (varint_size(max_input_count) - 1) * 4;
-
-        self.opts.target_value.unwrap_or(0) as i64
-            + (effective_base_weight as f32 * self.opts.target_feerate).ceil() as i64
-    }
-
-    pub fn selected_count(&self) -> usize {
-        self.selected.len()
-    }
-
-    pub fn selected(&self) -> impl Iterator<Item = (usize, &'a WeightedValue)> + '_ {
-        self.selected
-            .iter()
-            .map(move |&index| (index, &self.candidates[index]))
-    }
-
-    pub fn unselected(&self) -> impl Iterator<Item = (usize, &'a WeightedValue)> + '_ {
-        self.candidates
-            .iter()
-            .enumerate()
-            .filter(move |(index, _)| !self.selected.contains(index))
-    }
-
-    pub fn selected_indexes(&self) -> impl Iterator<Item = usize> + '_ {
-        self.selected.iter().cloned()
-    }
-
-    pub fn unselected_indexes(&self) -> impl Iterator<Item = usize> + '_ {
-        (0..self.candidates.len()).filter(move |index| !self.selected.contains(index))
-    }
-
-    pub fn all_selected(&self) -> bool {
-        self.selected.len() == self.candidates.len()
-    }
-
-    pub fn select_all(&mut self) {
-        self.selected = (0..self.candidates.len()).collect();
-    }
-
-    pub fn select_until_finished(&mut self) -> Result<Selection, SelectionError> {
-        let mut selection = self.finish();
-
-        if selection.is_ok() {
-            return selection;
-        }
-
-        let unselected = self.unselected_indexes().collect::<Vec<_>>();
-
-        for index in unselected {
-            self.select(index);
-            selection = self.finish();
-
-            if selection.is_ok() {
-                break;
-            }
-        }
-
-        selection
-    }
-
-    pub fn finish(&self) -> Result<Selection, SelectionError> {
-        let weight_without_drain = self.current_weight();
-        let weight_with_drain = weight_without_drain + self.opts.drain_weight;
-
-        let fee_without_drain =
-            (weight_without_drain as f32 * self.opts.target_feerate).ceil() as u64;
-        let fee_with_drain = (weight_with_drain as f32 * self.opts.target_feerate).ceil() as u64;
-
-        let inputs_minus_outputs = {
-            let target_value = self.opts.target_value.unwrap_or(0);
-            let selected = self.selected_absolute_value();
-
-            // find the largest unsatisfied constraint (if any), and return the error of that constraint
-            // "selected" should always be greater than or equal to these selected values
-            [
-                (
-                    SelectionConstraint::TargetValue,
-                    target_value.saturating_sub(selected),
-                ),
-                (
-                    SelectionConstraint::TargetFee,
-                    (target_value + fee_without_drain).saturating_sub(selected),
-                ),
-                (
-                    SelectionConstraint::MinAbsoluteFee,
-                    (target_value + self.opts.min_absolute_fee).saturating_sub(selected),
-                ),
-                (
-                    SelectionConstraint::MinDrainValue,
-                    // when we have no target value (hence no recipient txouts), we need to ensure
-                    // the selected amount can satisfy requirements for a drain output (so we at least have one txout)
-                    if self.opts.target_value.is_none() {
-                        (fee_with_drain + self.opts.min_drain_value).saturating_sub(selected)
-                    } else {
-                        0
-                    },
-                ),
-            ]
-            .iter()
-            .filter(|&(_, v)| v > &0)
-            .max_by_key(|&(_, v)| v)
-            .map_or(Ok(()), |(constraint, missing)| {
-                Err(SelectionError {
-                    selected,
-                    missing: *missing,
-                    constraint: *constraint,
-                })
-            })?;
-
-            selected - target_value
-        };
-
-        let fee_without_drain = fee_without_drain.max(self.opts.min_absolute_fee);
-        let fee_with_drain = fee_with_drain.max(self.opts.min_absolute_fee);
-
-        let excess_without_drain = inputs_minus_outputs - fee_without_drain;
-        let input_waste = self.selected_waste();
-
-        // begin preparing excess strategies for final selection
-        let mut excess_strategies = HashMap::new();
-
-        // only allow `ToFee` and `ToRecipient` excess strategies when we have a `target_value`,
-        // otherwise, we will result in a result with no txouts, or attempt to add value to an output
-        // that does not exist.
-        if self.opts.target_value.is_some() {
-            // no drain, excess to fee
-            excess_strategies.insert(
-                ExcessStrategyKind::ToFee,
-                ExcessStrategy {
-                    recipient_value: self.opts.target_value,
-                    drain_value: None,
-                    fee: fee_without_drain + excess_without_drain,
-                    weight: weight_without_drain,
-                    waste: input_waste + excess_without_drain as i64,
-                },
-            );
-
-            // no drain, send the excess to the recipient
-            // if `excess == 0`, this result will be the same as the previous, so don't consider it
-            // if `max_extra_target == 0`, there is no leeway for this strategy
-            if excess_without_drain > 0 && self.opts.max_extra_target > 0 {
-                let extra_recipient_value =
-                    core::cmp::min(self.opts.max_extra_target, excess_without_drain);
-                let extra_fee = excess_without_drain - extra_recipient_value;
-                excess_strategies.insert(
-                    ExcessStrategyKind::ToRecipient,
-                    ExcessStrategy {
-                        recipient_value: self.opts.target_value.map(|v| v + extra_recipient_value),
-                        drain_value: None,
-                        fee: fee_without_drain + extra_fee,
-                        weight: weight_without_drain,
-                        waste: input_waste + extra_fee as i64,
-                    },
-                );
-            }
-        }
-
-        // with drain
-        if fee_with_drain >= self.opts.min_absolute_fee
-            && inputs_minus_outputs >= fee_with_drain + self.opts.min_drain_value
-        {
-            excess_strategies.insert(
-                ExcessStrategyKind::ToDrain,
-                ExcessStrategy {
-                    recipient_value: self.opts.target_value,
-                    drain_value: Some(inputs_minus_outputs.saturating_sub(fee_with_drain)),
-                    fee: fee_with_drain,
-                    weight: weight_with_drain,
-                    waste: input_waste + self.opts.drain_waste(),
-                },
-            );
-        }
-
-        debug_assert!(
-            !excess_strategies.is_empty(),
-            "should have at least one excess strategy."
-        );
-
-        Ok(Selection {
-            selected: self.selected.clone(),
-            excess: excess_without_drain,
-            excess_strategies,
-        })
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct SelectionError {
-    selected: u64,
-    missing: u64,
-    constraint: SelectionConstraint,
-}
-
-impl core::fmt::Display for SelectionError {
-    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
-        let SelectionError {
-            selected,
-            missing,
-            constraint,
-        } = self;
-        write!(
-            f,
-            "insufficient coins selected; selected={}, missing={}, unsatisfied_constraint={:?}",
-            selected, missing, constraint
-        )
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for SelectionError {}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum SelectionConstraint {
-    /// The target is not met
-    TargetValue,
-    /// The target fee (given the feerate) is not met
-    TargetFee,
-    /// Min absolute fee is not met
-    MinAbsoluteFee,
-    /// Min drain value is not met
-    MinDrainValue,
-}
-
-impl core::fmt::Display for SelectionConstraint {
-    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
-        match self {
-            SelectionConstraint::TargetValue => core::write!(f, "target_value"),
-            SelectionConstraint::TargetFee => core::write!(f, "target_fee"),
-            SelectionConstraint::MinAbsoluteFee => core::write!(f, "min_absolute_fee"),
-            SelectionConstraint::MinDrainValue => core::write!(f, "min_drain_value"),
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct Selection {
-    pub selected: BTreeSet<usize>,
-    pub excess: u64,
-    pub excess_strategies: HashMap<ExcessStrategyKind, ExcessStrategy>,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, core::hash::Hash)]
-pub enum ExcessStrategyKind {
-    ToFee,
-    ToRecipient,
-    ToDrain,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct ExcessStrategy {
-    pub recipient_value: Option<u64>,
-    pub drain_value: Option<u64>,
-    pub fee: u64,
-    pub weight: u32,
-    pub waste: i64,
-}
-
-impl Selection {
-    pub fn apply_selection<'a, T>(
-        &'a self,
-        candidates: &'a [T],
-    ) -> impl Iterator<Item = &'a T> + 'a {
-        self.selected.iter().map(move |i| &candidates[*i])
-    }
-
-    /// Returns the [`ExcessStrategy`] that results in the least waste.
-    pub fn best_strategy(&self) -> (&ExcessStrategyKind, &ExcessStrategy) {
-        self.excess_strategies
-            .iter()
-            .min_by_key(|&(_, a)| a.waste)
-            .expect("selection has no excess strategy")
-    }
-}
-
-impl core::fmt::Display for ExcessStrategyKind {
-    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
-        match self {
-            ExcessStrategyKind::ToFee => core::write!(f, "to_fee"),
-            ExcessStrategyKind::ToRecipient => core::write!(f, "to_recipient"),
-            ExcessStrategyKind::ToDrain => core::write!(f, "to_drain"),
-        }
-    }
-}
-
-impl ExcessStrategy {
-    /// Returns feerate in sats/wu.
-    pub fn feerate(&self) -> f32 {
-        self.fee as f32 / self.weight as f32
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use crate::{ExcessStrategyKind, SelectionConstraint};
-
-    use super::{CoinSelector, CoinSelectorOpt, WeightedValue};
-
-    /// Ensure `target_value` is respected. Can't have any disrespect.
-    #[test]
-    fn target_value_respected() {
-        let target_value = 1000_u64;
-
-        let candidates = (500..1500_u64)
-            .map(|value| WeightedValue {
-                value,
-                weight: 100,
-                input_count: 1,
-                is_segwit: false,
-            })
-            .collect::<super::Vec<_>>();
-
-        let opts = CoinSelectorOpt {
-            target_value: Some(target_value),
-            max_extra_target: 0,
-            target_feerate: 0.00,
-            long_term_feerate: None,
-            min_absolute_fee: 0,
-            base_weight: 10,
-            drain_weight: 10,
-            spend_drain_weight: 10,
-            min_drain_value: 10,
-        };
-
-        for (index, v) in candidates.iter().enumerate() {
-            let mut selector = CoinSelector::new(&candidates, &opts);
-            assert!(selector.select(index));
-
-            let res = selector.finish();
-            if v.value < opts.target_value.unwrap_or(0) {
-                let err = res.expect_err("should have failed");
-                assert_eq!(err.selected, v.value);
-                assert_eq!(err.missing, target_value - v.value);
-                assert_eq!(err.constraint, SelectionConstraint::MinAbsoluteFee);
-            } else {
-                let sel = res.expect("should have succeeded");
-                assert_eq!(sel.excess, v.value - opts.target_value.unwrap_or(0));
-            }
-        }
-    }
-
-    #[test]
-    fn drain_all() {
-        let candidates = (0..100)
-            .map(|_| WeightedValue {
-                value: 666,
-                weight: 166,
-                input_count: 1,
-                is_segwit: false,
-            })
-            .collect::<super::Vec<_>>();
-
-        let opts = CoinSelectorOpt {
-            target_value: None,
-            max_extra_target: 0,
-            target_feerate: 0.25,
-            long_term_feerate: None,
-            min_absolute_fee: 0,
-            base_weight: 10,
-            drain_weight: 100,
-            spend_drain_weight: 66,
-            min_drain_value: 1000,
-        };
-
-        let selection = CoinSelector::new(&candidates, &opts)
-            .select_until_finished()
-            .expect("should succeed");
-
-        assert!(selection.selected.len() > 1);
-        assert_eq!(selection.excess_strategies.len(), 1);
-
-        let (kind, strategy) = selection.best_strategy();
-        assert_eq!(*kind, ExcessStrategyKind::ToDrain);
-        assert!(strategy.recipient_value.is_none());
-        assert!(strategy.drain_value.is_some());
-    }
-
-    /// TODO: Tests to add:
-    /// * `finish` should ensure at least `target_value` is selected.
-    /// * actual feerate should be equal or higher than `target_feerate`.
-    /// * actual drain value should be equal to or higher than `min_drain_value` (or else no drain).
-    fn _todo() {}
-}
diff --git a/nursery/coin_select/src/lib.rs b/nursery/coin_select/src/lib.rs
deleted file mode 100644 (file)
index 3b8ae12..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#![no_std]
-
-#[cfg(feature = "std")]
-extern crate std;
-
-#[macro_use]
-extern crate alloc;
-extern crate bdk_chain;
-
-use alloc::vec::Vec;
-use bdk_chain::{
-    bitcoin,
-    collections::{BTreeSet, HashMap},
-};
-use bitcoin::{absolute, transaction, Transaction, TxOut};
-use core::fmt::{Debug, Display};
-
-mod coin_selector;
-pub use coin_selector::*;
-
-mod bnb;
-pub use bnb::*;
-
-/// Txin "base" fields include `outpoint` (32+4) and `nSequence` (4). This does not include
-/// `scriptSigLen` or `scriptSig`.
-pub const TXIN_BASE_WEIGHT: u32 = (32 + 4 + 4) * 4;
-
-/// Helper to calculate varint size. `v` is the value the varint represents.
-// Shamelessly copied from
-// https://github.com/rust-bitcoin/rust-miniscript/blob/d5615acda1a7fdc4041a11c1736af139b8c7ebe8/src/util.rs#L8
-pub(crate) fn varint_size(v: usize) -> u32 {
-    bitcoin::VarInt(v as u64).size() as u32
-}
diff --git a/nursery/tmp_plan/Cargo.toml b/nursery/tmp_plan/Cargo.toml
deleted file mode 100644 (file)
index a97e5cb..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "bdk_tmp_plan"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-bdk_chain = {  path = "../../crates/chain", features = ["miniscript"] }
-
-[features]
-default = ["std"]
-std = []
diff --git a/nursery/tmp_plan/README.md b/nursery/tmp_plan/README.md
deleted file mode 100644 (file)
index 70cc100..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-# Temporary planning module
-
-A temporary place to hold the planning module until https://github.com/rust-bitcoin/rust-miniscript/pull/481 is merged and released
diff --git a/nursery/tmp_plan/src/lib.rs b/nursery/tmp_plan/src/lib.rs
deleted file mode 100644 (file)
index 08c28d8..0000000
+++ /dev/null
@@ -1,441 +0,0 @@
-#![allow(unused)]
-#![allow(missing_docs)]
-#![allow(clippy::all)] // FIXME
-//! A spending plan or *plan* for short is a representation of a particular spending path on a
-//! descriptor. This allows us to analayze a choice of spending path without producing any
-//! signatures or other witness data for it.
-//!
-//! To make a plan you provide the descriptor with "assets" like which keys you are able to use, hash
-//! pre-images you have access to, the current block height etc.
-//!
-//! Once you've got a plan it can tell you its expected satisfaction weight which can be useful for
-//! doing coin selection. Furthermore it provides which subset of those keys and hash pre-images you
-//! will actually need as well as what locktime or sequence number you need to set.
-//!
-//! Once you've obstained signatures, hash pre-images etc required by the plan, it can create a
-//! witness/script_sig for the input.
-use bdk_chain::{bitcoin, collections::*, miniscript};
-use bitcoin::{
-    absolute,
-    bip32::{DerivationPath, Fingerprint, KeySource},
-    ecdsa,
-    hashes::{hash160, ripemd160, sha256},
-    secp256k1::Secp256k1,
-    taproot::{self, LeafVersion, TapLeafHash},
-    transaction::Sequence,
-    ScriptBuf, TxIn, Witness, WitnessVersion,
-};
-use miniscript::{
-    descriptor::{InnerXKey, Tr},
-    hash256, DefiniteDescriptorKey, Descriptor, DescriptorPublicKey, ScriptContext, ToPublicKey,
-};
-
-pub(crate) fn varint_len(v: usize) -> usize {
-    bitcoin::VarInt(v as u64).size() as usize
-}
-
-mod plan_impls;
-mod requirements;
-mod template;
-pub use requirements::*;
-pub use template::PlanKey;
-use template::TemplateItem;
-
-#[derive(Clone, Debug)]
-enum TrSpend {
-    KeySpend,
-    LeafSpend {
-        script: ScriptBuf,
-        leaf_version: LeafVersion,
-    },
-}
-
-#[derive(Clone, Debug)]
-enum Target {
-    Legacy,
-    Segwitv0 {
-        script_code: ScriptBuf,
-    },
-    Segwitv1 {
-        tr: Tr<DefiniteDescriptorKey>,
-        tr_plan: TrSpend,
-    },
-}
-
-impl Target {}
-
-#[derive(Clone, Debug)]
-/// A plan represents a particular spending path for a descriptor.
-///
-/// See the module level documentation for more info.
-pub struct Plan<AK> {
-    template: Vec<TemplateItem<AK>>,
-    target: Target,
-    set_locktime: Option<absolute::LockTime>,
-    set_sequence: Option<Sequence>,
-}
-
-impl Default for Target {
-    fn default() -> Self {
-        Target::Legacy
-    }
-}
-
-#[derive(Clone, Debug, Default)]
-/// Signatures and hash pre-images that can be used to complete a plan.
-pub struct SatisfactionMaterial {
-    /// Schnorr signautres under their keys
-    pub schnorr_sigs: BTreeMap<DefiniteDescriptorKey, taproot::Signature>,
-    /// ECDSA signatures under their keys
-    pub ecdsa_sigs: BTreeMap<DefiniteDescriptorKey, ecdsa::Signature>,
-    /// SHA256 pre-images under their images
-    pub sha256_preimages: BTreeMap<sha256::Hash, Vec<u8>>,
-    /// hash160 pre-images under their images
-    pub hash160_preimages: BTreeMap<hash160::Hash, Vec<u8>>,
-    /// hash256 pre-images under their images
-    pub hash256_preimages: BTreeMap<hash256::Hash, Vec<u8>>,
-    /// ripemd160 pre-images under their images
-    pub ripemd160_preimages: BTreeMap<ripemd160::Hash, Vec<u8>>,
-}
-
-impl<Ak> Plan<Ak>
-where
-    Ak: Clone,
-{
-    /// The expected satisfaction weight for the plan if it is completed.
-    pub fn expected_weight(&self) -> usize {
-        let script_sig_size = match self.target {
-            Target::Legacy => unimplemented!(), // self
-            // .template
-            // .iter()
-            // .map(|step| {
-            //     let size = step.expected_size();
-            //     size + push_opcode_size(size)
-            // })
-            // .sum()
-            Target::Segwitv0 { .. } | Target::Segwitv1 { .. } => 1,
-        };
-        let witness_elem_sizes: Option<Vec<usize>> = match &self.target {
-            Target::Legacy => None,
-            Target::Segwitv0 { .. } => Some(
-                self.template
-                    .iter()
-                    .map(|step| step.expected_size())
-                    .collect(),
-            ),
-            Target::Segwitv1 { tr, tr_plan } => {
-                let mut witness_elems = self
-                    .template
-                    .iter()
-                    .map(|step| step.expected_size())
-                    .collect::<Vec<_>>();
-
-                if let TrSpend::LeafSpend {
-                    script,
-                    leaf_version,
-                } = tr_plan
-                {
-                    let control_block = tr
-                        .spend_info()
-                        .control_block(&(script.clone(), *leaf_version))
-                        .expect("must exist");
-                    witness_elems.push(script.len());
-                    witness_elems.push(control_block.size());
-                }
-
-                Some(witness_elems)
-            }
-        };
-
-        let witness_size: usize = match witness_elem_sizes {
-            Some(elems) => {
-                varint_len(elems.len())
-                    + elems
-                        .into_iter()
-                        .map(|elem| varint_len(elem) + elem)
-                        .sum::<usize>()
-            }
-            None => 0,
-        };
-
-        script_sig_size * 4 + witness_size
-    }
-
-    pub fn requirements(&self) -> Requirements<Ak> {
-        match self.try_complete(&SatisfactionMaterial::default()) {
-            PlanState::Complete { .. } => Requirements::default(),
-            PlanState::Incomplete(requirements) => requirements,
-        }
-    }
-
-    pub fn try_complete(&self, auth_data: &SatisfactionMaterial) -> PlanState<Ak> {
-        let unsatisfied_items = self
-            .template
-            .iter()
-            .filter(|step| match step {
-                TemplateItem::Sign(key) => {
-                    !auth_data.schnorr_sigs.contains_key(&key.descriptor_key)
-                }
-                TemplateItem::Hash160(image) => !auth_data.hash160_preimages.contains_key(image),
-                TemplateItem::Hash256(image) => !auth_data.hash256_preimages.contains_key(image),
-                TemplateItem::Sha256(image) => !auth_data.sha256_preimages.contains_key(image),
-                TemplateItem::Ripemd160(image) => {
-                    !auth_data.ripemd160_preimages.contains_key(image)
-                }
-                TemplateItem::Pk { .. } | TemplateItem::One | TemplateItem::Zero => false,
-            })
-            .collect::<Vec<_>>();
-
-        if unsatisfied_items.is_empty() {
-            let mut witness = self
-                .template
-                .iter()
-                .flat_map(|step| step.to_witness_stack(&auth_data))
-                .collect::<Vec<_>>();
-            match &self.target {
-                Target::Segwitv0 { .. } => todo!(),
-                Target::Legacy => todo!(),
-                Target::Segwitv1 {
-                    tr_plan: TrSpend::KeySpend,
-                    ..
-                } => PlanState::Complete {
-                    final_script_sig: None,
-                    final_script_witness: Some(Witness::from(witness)),
-                },
-                Target::Segwitv1 {
-                    tr,
-                    tr_plan:
-                        TrSpend::LeafSpend {
-                            script,
-                            leaf_version,
-                        },
-                } => {
-                    let spend_info = tr.spend_info();
-                    let control_block = spend_info
-                        .control_block(&(script.clone(), *leaf_version))
-                        .expect("must exist");
-                    witness.push(script.clone().into_bytes());
-                    witness.push(control_block.serialize());
-
-                    PlanState::Complete {
-                        final_script_sig: None,
-                        final_script_witness: Some(Witness::from(witness)),
-                    }
-                }
-            }
-        } else {
-            let mut requirements = Requirements::default();
-
-            match &self.target {
-                Target::Legacy => {
-                    todo!()
-                }
-                Target::Segwitv0 { .. } => {
-                    todo!()
-                }
-                Target::Segwitv1 { tr, tr_plan } => {
-                    let spend_info = tr.spend_info();
-                    match tr_plan {
-                        TrSpend::KeySpend => match &self.template[..] {
-                            [TemplateItem::Sign(ref plan_key)] => {
-                                requirements.signatures = RequiredSignatures::TapKey {
-                                    merkle_root: spend_info.merkle_root(),
-                                    plan_key: plan_key.clone(),
-                                };
-                            }
-                            _ => unreachable!("tapkey spend will always have only one sign step"),
-                        },
-                        TrSpend::LeafSpend {
-                            script,
-                            leaf_version,
-                        } => {
-                            let leaf_hash = TapLeafHash::from_script(&script, *leaf_version);
-                            requirements.signatures = RequiredSignatures::TapScript {
-                                leaf_hash,
-                                plan_keys: vec![],
-                            }
-                        }
-                    }
-                }
-            }
-
-            let required_signatures = match requirements.signatures {
-                RequiredSignatures::Legacy { .. } => todo!(),
-                RequiredSignatures::Segwitv0 { .. } => todo!(),
-                RequiredSignatures::TapKey { .. } => return PlanState::Incomplete(requirements),
-                RequiredSignatures::TapScript {
-                    plan_keys: ref mut keys,
-                    ..
-                } => keys,
-            };
-
-            for step in unsatisfied_items {
-                match step {
-                    TemplateItem::Sign(plan_key) => {
-                        required_signatures.push(plan_key.clone());
-                    }
-                    TemplateItem::Hash160(image) => {
-                        requirements.hash160_images.insert(image.clone());
-                    }
-                    TemplateItem::Hash256(image) => {
-                        requirements.hash256_images.insert(image.clone());
-                    }
-                    TemplateItem::Sha256(image) => {
-                        requirements.sha256_images.insert(image.clone());
-                    }
-                    TemplateItem::Ripemd160(image) => {
-                        requirements.ripemd160_images.insert(image.clone());
-                    }
-                    TemplateItem::Pk { .. } | TemplateItem::One | TemplateItem::Zero => { /* no requirements */
-                    }
-                }
-            }
-
-            PlanState::Incomplete(requirements)
-        }
-    }
-
-    /// Witness version for the plan
-    pub fn witness_version(&self) -> Option<WitnessVersion> {
-        match self.target {
-            Target::Legacy => None,
-            Target::Segwitv0 { .. } => Some(WitnessVersion::V0),
-            Target::Segwitv1 { .. } => Some(WitnessVersion::V1),
-        }
-    }
-
-    /// The minimum required locktime height or time on the transaction using the plan.
-    pub fn required_locktime(&self) -> Option<absolute::LockTime> {
-        self.set_locktime.clone()
-    }
-
-    /// The minimum required sequence (height or time) on the input to satisfy the plan
-    pub fn required_sequence(&self) -> Option<Sequence> {
-        self.set_sequence.clone()
-    }
-
-    /// The minimum required transaction version required on the transaction using the plan.
-    pub fn min_version(&self) -> Option<u32> {
-        if let Some(_) = self.set_sequence {
-            Some(2)
-        } else {
-            Some(1)
-        }
-    }
-}
-
-/// The returned value from [`Plan::try_complete`].
-pub enum PlanState<Ak> {
-    /// The plan is complete
-    Complete {
-        /// The script sig that should be set on the input
-        final_script_sig: Option<ScriptBuf>,
-        /// The witness that should be set on the input
-        final_script_witness: Option<Witness>,
-    },
-    Incomplete(Requirements<Ak>),
-}
-
-#[derive(Clone, Debug)]
-pub struct Assets<K> {
-    pub keys: Vec<K>,
-    pub txo_age: Option<Sequence>,
-    pub max_locktime: Option<absolute::LockTime>,
-    pub sha256: Vec<sha256::Hash>,
-    pub hash256: Vec<hash256::Hash>,
-    pub ripemd160: Vec<ripemd160::Hash>,
-    pub hash160: Vec<hash160::Hash>,
-}
-
-impl<K> Default for Assets<K> {
-    fn default() -> Self {
-        Self {
-            keys: Default::default(),
-            txo_age: Default::default(),
-            max_locktime: Default::default(),
-            sha256: Default::default(),
-            hash256: Default::default(),
-            ripemd160: Default::default(),
-            hash160: Default::default(),
-        }
-    }
-}
-
-pub trait CanDerive {
-    fn can_derive(&self, key: &DefiniteDescriptorKey) -> Option<DerivationPath>;
-}
-
-impl CanDerive for KeySource {
-    fn can_derive(&self, key: &DefiniteDescriptorKey) -> Option<DerivationPath> {
-        match DescriptorPublicKey::from(key.clone()) {
-            DescriptorPublicKey::Single(single_pub) => {
-                path_to_child(self, single_pub.origin.as_ref()?, None)
-            }
-            DescriptorPublicKey::XPub(dxk) => {
-                let origin = dxk.origin.clone().unwrap_or_else(|| {
-                    let secp = Secp256k1::signing_only();
-                    (dxk.xkey.xkey_fingerprint(&secp), DerivationPath::master())
-                });
-
-                path_to_child(self, &origin, Some(&dxk.derivation_path))
-            }
-            DescriptorPublicKey::MultiXPub(_) => {
-                // This crate will be replaced by
-                // https://github.com/rust-bitcoin/rust-miniscript/pull/481 anyways
-                todo!();
-            }
-        }
-    }
-}
-
-impl CanDerive for DescriptorPublicKey {
-    fn can_derive(&self, key: &DefiniteDescriptorKey) -> Option<DerivationPath> {
-        match (self, DescriptorPublicKey::from(key.clone())) {
-            (parent, child) if parent == &child => Some(DerivationPath::master()),
-            (DescriptorPublicKey::XPub(parent), _) => {
-                let origin = parent.origin.clone().unwrap_or_else(|| {
-                    let secp = Secp256k1::signing_only();
-                    (
-                        parent.xkey.xkey_fingerprint(&secp),
-                        DerivationPath::master(),
-                    )
-                });
-                KeySource::from(origin).can_derive(key)
-            }
-            _ => None,
-        }
-    }
-}
-
-fn path_to_child(
-    parent: &KeySource,
-    child_origin: &(Fingerprint, DerivationPath),
-    child_derivation: Option<&DerivationPath>,
-) -> Option<DerivationPath> {
-    if parent.0 == child_origin.0 {
-        let mut remaining_derivation =
-            DerivationPath::from(child_origin.1[..].strip_prefix(&parent.1[..])?);
-        remaining_derivation =
-            remaining_derivation.extend(child_derivation.unwrap_or(&DerivationPath::master()));
-        Some(remaining_derivation)
-    } else {
-        None
-    }
-}
-
-pub fn plan_satisfaction<Ak>(
-    desc: &Descriptor<DefiniteDescriptorKey>,
-    assets: &Assets<Ak>,
-) -> Option<Plan<Ak>>
-where
-    Ak: CanDerive + Clone,
-{
-    match desc {
-        Descriptor::Bare(_) => todo!(),
-        Descriptor::Pkh(_) => todo!(),
-        Descriptor::Wpkh(_) => todo!(),
-        Descriptor::Sh(_) => todo!(),
-        Descriptor::Wsh(_) => todo!(),
-        Descriptor::Tr(tr) => crate::plan_impls::plan_satisfaction_tr(tr, assets),
-    }
-}
diff --git a/nursery/tmp_plan/src/plan_impls.rs b/nursery/tmp_plan/src/plan_impls.rs
deleted file mode 100644 (file)
index 8bbcb5e..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-use bdk_chain::{bitcoin, miniscript};
-use bitcoin::locktime::absolute;
-use miniscript::Terminal;
-
-use super::*;
-
-impl<Ak> TermPlan<Ak> {
-    fn combine(self, other: Self) -> Option<Self> {
-        let min_locktime = {
-            match (self.min_locktime, other.min_locktime) {
-                (Some(lhs), Some(rhs)) => {
-                    if lhs.is_same_unit(rhs) {
-                        Some(if lhs.to_consensus_u32() > rhs.to_consensus_u32() {
-                            lhs
-                        } else {
-                            rhs
-                        })
-                    } else {
-                        return None;
-                    }
-                }
-                _ => self.min_locktime.or(other.min_locktime),
-            }
-        };
-
-        let min_sequence = {
-            match (self.min_sequence, other.min_sequence) {
-                (Some(lhs), Some(rhs)) => {
-                    if lhs.is_height_locked() == rhs.is_height_locked() {
-                        Some(if lhs.to_consensus_u32() > rhs.to_consensus_u32() {
-                            lhs
-                        } else {
-                            rhs
-                        })
-                    } else {
-                        return None;
-                    }
-                }
-                _ => self.min_sequence.or(other.min_sequence),
-            }
-        };
-
-        let mut template = self.template;
-        template.extend(other.template);
-
-        Some(Self {
-            min_locktime,
-            min_sequence,
-            template,
-        })
-    }
-
-    pub(crate) fn expected_size(&self) -> usize {
-        self.template.iter().map(|step| step.expected_size()).sum()
-    }
-}
-
-// impl crate::descriptor::Pkh<DefiniteDescriptorKey> {
-//     pub(crate) fn plan_satisfaction<Ak>(&self, assets: &Assets<Ak>) -> Option<Plan<Ak>>
-//     where
-//         Ak: CanDerive + Clone,
-//     {
-//         let (asset_key, derivation_hint) = assets.keys.iter().find_map(|asset_key| {
-//             let derivation_hint = asset_key.can_derive(self.as_inner())?;
-//             Some((asset_key, derivation_hint))
-//         })?;
-
-//         Some(Plan {
-//             template: vec![TemplateItem::Sign(PlanKey {
-//                 asset_key: asset_key.clone(),
-//                 descriptor_key: self.as_inner().clone(),
-//                 derivation_hint,
-//             })],
-//             target: Target::Legacy,
-//             set_locktime: None,
-//             set_sequence: None,
-//         })
-//     }
-// }
-
-// impl crate::descriptor::Wpkh<DefiniteDescriptorKey> {
-//     pub(crate) fn plan_satisfaction<Ak>(&self, assets: &Assets<Ak>) -> Option<Plan<Ak>>
-//     where
-//         Ak: CanDerive + Clone,
-//     {
-//         let (asset_key, derivation_hint) = assets.keys.iter().find_map(|asset_key| {
-//             let derivation_hint = asset_key.can_derive(self.as_inner())?;
-//             Some((asset_key, derivation_hint))
-//         })?;
-
-//         Some(Plan {
-//             template: vec![TemplateItem::Sign(PlanKey {
-//                 asset_key: asset_key.clone(),
-//                 descriptor_key: self.as_inner().clone(),
-//                 derivation_hint,
-//             })],
-//             target: Target::Segwitv0,
-//             set_locktime: None,
-//             set_sequence: None,
-//         })
-//     }
-// }
-
-pub(crate) fn plan_satisfaction_tr<Ak>(
-    tr: &miniscript::descriptor::Tr<DefiniteDescriptorKey>,
-    assets: &Assets<Ak>,
-) -> Option<Plan<Ak>>
-where
-    Ak: CanDerive + Clone,
-{
-    let key_path_spend = assets.keys.iter().find_map(|asset_key| {
-        let derivation_hint = asset_key.can_derive(tr.internal_key())?;
-        Some((asset_key, derivation_hint))
-    });
-
-    if let Some((asset_key, derivation_hint)) = key_path_spend {
-        return Some(Plan {
-            template: vec![TemplateItem::Sign(PlanKey {
-                asset_key: asset_key.clone(),
-                descriptor_key: tr.internal_key().clone(),
-                derivation_hint,
-            })],
-            target: Target::Segwitv1 {
-                tr: tr.clone(),
-                tr_plan: TrSpend::KeySpend,
-            },
-            set_locktime: None,
-            set_sequence: None,
-        });
-    }
-
-    let mut plans = tr
-        .iter_scripts()
-        .filter_map(|(_, ms)| Some((ms, (plan_steps(&ms.node, assets)?))))
-        .collect::<Vec<_>>();
-
-    plans.sort_by_cached_key(|(_, plan)| plan.expected_size());
-
-    let (script, best_plan) = plans.into_iter().next()?;
-
-    Some(Plan {
-        target: Target::Segwitv1 {
-            tr: tr.clone(),
-            tr_plan: TrSpend::LeafSpend {
-                script: script.encode(),
-                leaf_version: LeafVersion::TapScript,
-            },
-        },
-        set_locktime: best_plan.min_locktime.clone(),
-        set_sequence: best_plan.min_sequence.clone(),
-        template: best_plan.template,
-    })
-}
-
-#[derive(Debug)]
-struct TermPlan<Ak> {
-    pub min_locktime: Option<absolute::LockTime>,
-    pub min_sequence: Option<Sequence>,
-    pub template: Vec<TemplateItem<Ak>>,
-}
-
-impl<Ak> TermPlan<Ak> {
-    fn new(template: Vec<TemplateItem<Ak>>) -> Self {
-        TermPlan {
-            template,
-            ..Default::default()
-        }
-    }
-}
-
-impl<Ak> Default for TermPlan<Ak> {
-    fn default() -> Self {
-        Self {
-            min_locktime: Default::default(),
-            min_sequence: Default::default(),
-            template: Default::default(),
-        }
-    }
-}
-
-fn plan_steps<Ak: Clone + CanDerive, Ctx: ScriptContext>(
-    term: &Terminal<DefiniteDescriptorKey, Ctx>,
-    assets: &Assets<Ak>,
-) -> Option<TermPlan<Ak>> {
-    match term {
-        Terminal::True => Some(TermPlan::new(vec![])),
-        Terminal::False => return None,
-        Terminal::PkH(key) => {
-            let (asset_key, derivation_hint) = assets
-                .keys
-                .iter()
-                .find_map(|asset_key| Some((asset_key, asset_key.can_derive(key)?)))?;
-            Some(TermPlan::new(vec![
-                TemplateItem::Sign(PlanKey {
-                    asset_key: asset_key.clone(),
-                    derivation_hint,
-                    descriptor_key: key.clone(),
-                }),
-                TemplateItem::Pk { key: key.clone() },
-            ]))
-        }
-        Terminal::PkK(key) => {
-            let (asset_key, derivation_hint) = assets
-                .keys
-                .iter()
-                .find_map(|asset_key| Some((asset_key, asset_key.can_derive(key)?)))?;
-            Some(TermPlan::new(vec![TemplateItem::Sign(PlanKey {
-                asset_key: asset_key.clone(),
-                derivation_hint,
-                descriptor_key: key.clone(),
-            })]))
-        }
-        Terminal::RawPkH(_pk_hash) => {
-            /* TODO */
-            None
-        }
-        Terminal::After(locktime) => {
-            let max_locktime = assets.max_locktime?;
-            let locktime = absolute::LockTime::from(*locktime);
-            let (height, time) = match max_locktime {
-                absolute::LockTime::Blocks(height) => {
-                    (height, absolute::Time::from_consensus(0).unwrap())
-                }
-                absolute::LockTime::Seconds(seconds) => (absolute::Height::ZERO, seconds),
-            };
-            if max_locktime.is_satisfied_by(height, time) {
-                Some(TermPlan {
-                    min_locktime: Some(locktime),
-                    ..Default::default()
-                })
-            } else {
-                None
-            }
-        }
-        Terminal::Older(older) => {
-            // FIXME: older should be a height or time not a sequence.
-            let max_sequence = assets.txo_age?;
-            //TODO: this whole thing is probably wrong but upstream should provide a way of
-            // doing it properly.
-            if max_sequence.is_height_locked() == older.is_height_locked() {
-                if max_sequence.to_consensus_u32() >= older.to_consensus_u32() {
-                    Some(TermPlan {
-                        min_sequence: Some((*older).into()),
-                        ..Default::default()
-                    })
-                } else {
-                    None
-                }
-            } else {
-                None
-            }
-        }
-        Terminal::Sha256(image) => {
-            if assets.sha256.contains(&image) {
-                Some(TermPlan::new(vec![TemplateItem::Sha256(image.clone())]))
-            } else {
-                None
-            }
-        }
-        Terminal::Hash256(image) => {
-            if assets.hash256.contains(image) {
-                Some(TermPlan::new(vec![TemplateItem::Hash256(image.clone())]))
-            } else {
-                None
-            }
-        }
-        Terminal::Ripemd160(image) => {
-            if assets.ripemd160.contains(&image) {
-                Some(TermPlan::new(vec![TemplateItem::Ripemd160(image.clone())]))
-            } else {
-                None
-            }
-        }
-        Terminal::Hash160(image) => {
-            if assets.hash160.contains(&image) {
-                Some(TermPlan::new(vec![TemplateItem::Hash160(image.clone())]))
-            } else {
-                None
-            }
-        }
-        Terminal::Alt(ms)
-        | Terminal::Swap(ms)
-        | Terminal::Check(ms)
-        | Terminal::Verify(ms)
-        | Terminal::NonZero(ms)
-        | Terminal::ZeroNotEqual(ms) => plan_steps(&ms.node, assets),
-        Terminal::DupIf(ms) => {
-            let mut plan = plan_steps(&ms.node, assets)?;
-            plan.template.push(TemplateItem::One);
-            Some(plan)
-        }
-        Terminal::AndV(l, r) | Terminal::AndB(l, r) => {
-            let lhs = plan_steps(&l.node, assets)?;
-            let rhs = plan_steps(&r.node, assets)?;
-            lhs.combine(rhs)
-        }
-        Terminal::AndOr(_, _, _) => todo!(),
-        Terminal::OrB(_, _) => todo!(),
-        Terminal::OrD(_, _) => todo!(),
-        Terminal::OrC(_, _) => todo!(),
-        Terminal::OrI(lhs, rhs) => {
-            let lplan = plan_steps(&lhs.node, assets).map(|mut plan| {
-                plan.template.push(TemplateItem::One);
-                plan
-            });
-            let rplan = plan_steps(&rhs.node, assets).map(|mut plan| {
-                plan.template.push(TemplateItem::Zero);
-                plan
-            });
-            match (lplan, rplan) {
-                (Some(lplan), Some(rplan)) => {
-                    if lplan.expected_size() <= rplan.expected_size() {
-                        Some(lplan)
-                    } else {
-                        Some(rplan)
-                    }
-                }
-                (lplan, rplan) => lplan.or(rplan),
-            }
-        }
-        Terminal::Thresh(_) => todo!(),
-        Terminal::Multi(_) => todo!(),
-        Terminal::MultiA(_) => todo!(),
-    }
-}
diff --git a/nursery/tmp_plan/src/requirements.rs b/nursery/tmp_plan/src/requirements.rs
deleted file mode 100644 (file)
index 0d0097d..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-use bdk_chain::{bitcoin, collections::*, miniscript};
-use core::ops::Deref;
-
-use bitcoin::{
-    bip32,
-    hashes::{hash160, ripemd160, sha256, Hash},
-    key::XOnlyPublicKey,
-    secp256k1::{Keypair, Message, PublicKey, Signing, Verification},
-    sighash,
-    sighash::{EcdsaSighashType, Prevouts, SighashCache, TapSighashType},
-    taproot, Transaction, TxOut,
-};
-
-use super::*;
-use miniscript::{
-    descriptor::{DescriptorSecretKey, KeyMap},
-    hash256,
-};
-
-#[derive(Clone, Debug)]
-/// Signatures and hash pre-images that must be provided to complete the plan.
-pub struct Requirements<Ak> {
-    /// required signatures
-    pub signatures: RequiredSignatures<Ak>,
-    /// required sha256 pre-images
-    pub sha256_images: HashSet<sha256::Hash>,
-    /// required hash160 pre-images
-    pub hash160_images: HashSet<hash160::Hash>,
-    /// required hash256 pre-images
-    pub hash256_images: HashSet<hash256::Hash>,
-    /// required ripemd160 pre-images
-    pub ripemd160_images: HashSet<ripemd160::Hash>,
-}
-
-impl<Ak> Default for RequiredSignatures<Ak> {
-    fn default() -> Self {
-        RequiredSignatures::Legacy {
-            keys: Default::default(),
-        }
-    }
-}
-
-impl<Ak> Default for Requirements<Ak> {
-    fn default() -> Self {
-        Self {
-            signatures: Default::default(),
-            sha256_images: Default::default(),
-            hash160_images: Default::default(),
-            hash256_images: Default::default(),
-            ripemd160_images: Default::default(),
-        }
-    }
-}
-
-impl<Ak> Requirements<Ak> {
-    /// Whether any hash pre-images are required in the plan
-    pub fn requires_hash_preimages(&self) -> bool {
-        !(self.sha256_images.is_empty()
-            && self.hash160_images.is_empty()
-            && self.hash256_images.is_empty()
-            && self.ripemd160_images.is_empty())
-    }
-}
-
-/// The signatures required to complete the plan
-#[derive(Clone, Debug)]
-pub enum RequiredSignatures<Ak> {
-    /// Legacy ECDSA signatures are required
-    Legacy { keys: Vec<PlanKey<Ak>> },
-    /// Segwitv0 ECDSA signatures are required
-    Segwitv0 { keys: Vec<PlanKey<Ak>> },
-    /// A Taproot key spend signature is required
-    TapKey {
-        /// the internal key
-        plan_key: PlanKey<Ak>,
-        /// The merkle root of the taproot output
-        merkle_root: Option<taproot::TapNodeHash>,
-    },
-    /// Taproot script path signatures are required
-    TapScript {
-        /// The leaf hash of the script being used
-        leaf_hash: TapLeafHash,
-        /// The keys in the script that require signatures
-        plan_keys: Vec<PlanKey<Ak>>,
-    },
-}
-
-#[derive(Clone, Debug)]
-pub enum SigningError {
-    SigHashP2wpkh(sighash::P2wpkhError),
-    SigHashTaproot(sighash::TaprootError),
-    DerivationError(bip32::Error),
-}
-
-impl From<sighash::TaprootError> for SigningError {
-    fn from(v: sighash::TaprootError) -> Self {
-        Self::SigHashTaproot(v)
-    }
-}
-
-impl From<sighash::P2wpkhError> for SigningError {
-    fn from(v: sighash::P2wpkhError) -> Self {
-        Self::SigHashP2wpkh(v)
-    }
-}
-
-impl core::fmt::Display for SigningError {
-    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
-        match self {
-            SigningError::SigHashP2wpkh(e) => e.fmt(f),
-            SigningError::SigHashTaproot(e) => e.fmt(f),
-            SigningError::DerivationError(e) => e.fmt(f),
-        }
-    }
-}
-
-impl From<bip32::Error> for SigningError {
-    fn from(e: bip32::Error) -> Self {
-        Self::DerivationError(e)
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for SigningError {}
-
-impl RequiredSignatures<DescriptorPublicKey> {
-    pub fn sign_with_keymap<T: core::borrow::Borrow<Transaction>>(
-        &self,
-        input_index: usize,
-        keymap: &KeyMap,
-        prevouts: &Prevouts<'_, impl core::borrow::Borrow<TxOut>>,
-        schnorr_sighashty: Option<TapSighashType>,
-        _ecdsa_sighashty: Option<EcdsaSighashType>,
-        sighash_cache: &mut SighashCache<T>,
-        auth_data: &mut SatisfactionMaterial,
-        secp: &Secp256k1<impl Signing + Verification>,
-    ) -> Result<bool, SigningError> {
-        match self {
-            RequiredSignatures::Legacy { .. } | RequiredSignatures::Segwitv0 { .. } => todo!(),
-            RequiredSignatures::TapKey {
-                plan_key,
-                merkle_root,
-            } => {
-                let schnorr_sighashty = schnorr_sighashty.unwrap_or(TapSighashType::Default);
-                let sighash = sighash_cache.taproot_key_spend_signature_hash(
-                    input_index,
-                    prevouts,
-                    schnorr_sighashty,
-                )?;
-                let secret_key = match keymap.get(&plan_key.asset_key) {
-                    Some(secret_key) => secret_key,
-                    None => return Ok(false),
-                };
-                let secret_key = match secret_key {
-                    DescriptorSecretKey::Single(single) => single.key.inner,
-                    DescriptorSecretKey::XPrv(xprv) => {
-                        xprv.xkey
-                            .derive_priv(&secp, &plan_key.derivation_hint)?
-                            .private_key
-                    }
-                    DescriptorSecretKey::MultiXPrv(_) => {
-                        // This crate will be replaced by
-                        // https://github.com/rust-bitcoin/rust-miniscript/pull/481 anyways
-                        todo!();
-                    }
-                };
-
-                let pubkey = PublicKey::from_secret_key(&secp, &secret_key);
-                let x_only_pubkey = XOnlyPublicKey::from(pubkey);
-
-                let tweak =
-                    taproot::TapTweakHash::from_key_and_tweak(x_only_pubkey, merkle_root.clone());
-                let keypair = Keypair::from_secret_key(&secp, &secret_key.clone())
-                    .add_xonly_tweak(&secp, &tweak.to_scalar())
-                    .unwrap();
-
-                let msg = Message::from_digest(sighash.to_byte_array());
-                let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair);
-
-                let bitcoin_sig = taproot::Signature {
-                    signature: sig,
-                    sighash_type: schnorr_sighashty,
-                };
-
-                auth_data
-                    .schnorr_sigs
-                    .insert(plan_key.descriptor_key.clone(), bitcoin_sig);
-                Ok(true)
-            }
-            RequiredSignatures::TapScript {
-                leaf_hash,
-                plan_keys,
-            } => {
-                let sighash_type = schnorr_sighashty.unwrap_or(TapSighashType::Default);
-                let sighash = sighash_cache.taproot_script_spend_signature_hash(
-                    input_index,
-                    prevouts,
-                    *leaf_hash,
-                    sighash_type,
-                )?;
-
-                let mut modified = false;
-
-                for plan_key in plan_keys {
-                    if let Some(secret_key) = keymap.get(&plan_key.asset_key) {
-                        let secret_key = match secret_key {
-                            DescriptorSecretKey::Single(single) => single.key.inner,
-                            DescriptorSecretKey::XPrv(xprv) => {
-                                xprv.xkey
-                                    .derive_priv(&secp, &plan_key.derivation_hint)?
-                                    .private_key
-                            }
-                            DescriptorSecretKey::MultiXPrv(_) => {
-                                // This crate will be replaced by
-                                // https://github.com/rust-bitcoin/rust-miniscript/pull/481 anyways
-                                todo!();
-                            }
-                        };
-                        let keypair = Keypair::from_secret_key(&secp, &secret_key.clone());
-                        let msg = Message::from_digest(sighash.to_byte_array());
-                        let signature = secp.sign_schnorr_no_aux_rand(&msg, &keypair);
-                        let bitcoin_sig = taproot::Signature {
-                            signature,
-                            sighash_type,
-                        };
-
-                        auth_data
-                            .schnorr_sigs
-                            .insert(plan_key.descriptor_key.clone(), bitcoin_sig);
-                        modified = true;
-                    }
-                }
-                Ok(modified)
-            }
-        }
-    }
-}
diff --git a/nursery/tmp_plan/src/template.rs b/nursery/tmp_plan/src/template.rs
deleted file mode 100644 (file)
index 72d5141..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-use bdk_chain::{bitcoin, miniscript};
-use bitcoin::{
-    bip32::DerivationPath,
-    hashes::{hash160, ripemd160, sha256},
-};
-
-use super::*;
-use crate::{hash256, varint_len, DefiniteDescriptorKey};
-
-#[derive(Clone, Debug)]
-pub(crate) enum TemplateItem<Ak> {
-    Sign(PlanKey<Ak>),
-    Pk { key: DefiniteDescriptorKey },
-    One,
-    Zero,
-    Sha256(sha256::Hash),
-    Hash256(hash256::Hash),
-    Ripemd160(ripemd160::Hash),
-    Hash160(hash160::Hash),
-}
-
-/// A plan key contains the asset key originally provided along with key in the descriptor it
-/// purports to be able to derive for along with a "hint" on how to derive it.
-#[derive(Clone, Debug)]
-pub struct PlanKey<Ak> {
-    /// The key the planner will sign with
-    pub asset_key: Ak,
-    /// A hint from how to get from the asset key to the concrete key we need to sign with.
-    pub derivation_hint: DerivationPath,
-    /// The key that was in the descriptor that we are satisfying with the signature from the asset
-    /// key.
-    pub descriptor_key: DefiniteDescriptorKey,
-}
-
-impl<Ak> TemplateItem<Ak> {
-    pub fn expected_size(&self) -> usize {
-        match self {
-            TemplateItem::Sign { .. } => 64, /* size of sig TODO: take into consideration sighash flag */
-            TemplateItem::Pk { .. } => 32,
-            TemplateItem::One => varint_len(1),
-            TemplateItem::Zero => 0, /* zero means an empty witness element */
-            // I'm not sure if it should be 32 here (it's a 20 byte hash) but that's what other
-            // parts of the code were doing.
-            TemplateItem::Hash160(_) | TemplateItem::Ripemd160(_) => 32,
-            TemplateItem::Sha256(_) | TemplateItem::Hash256(_) => 32,
-        }
-    }
-
-    // this can only be called if we are sure that auth_data has what we need
-    pub(super) fn to_witness_stack(&self, auth_data: &SatisfactionMaterial) -> Vec<Vec<u8>> {
-        match self {
-            TemplateItem::Sign(plan_key) => {
-                vec![auth_data
-                    .schnorr_sigs
-                    .get(&plan_key.descriptor_key)
-                    .unwrap()
-                    .to_vec()]
-            }
-            TemplateItem::One => vec![vec![1]],
-            TemplateItem::Zero => vec![vec![]],
-            TemplateItem::Sha256(image) => {
-                vec![auth_data.sha256_preimages.get(image).unwrap().to_vec()]
-            }
-            TemplateItem::Hash160(image) => {
-                vec![auth_data.hash160_preimages.get(image).unwrap().to_vec()]
-            }
-            TemplateItem::Ripemd160(image) => {
-                vec![auth_data.ripemd160_preimages.get(image).unwrap().to_vec()]
-            }
-            TemplateItem::Hash256(image) => {
-                vec![auth_data.hash256_preimages.get(image).unwrap().to_vec()]
-            }
-            TemplateItem::Pk { key } => vec![key.to_public_key().to_bytes()],
-        }
-    }
-}