Introduce keychain::LocalChangeSet
This corresponds to `keychain::KeychainChangeSet` but for the redesigned structures with `LocalChain`. This structure is now used in `Wallet` as well as the examples.
This commit is contained in:
parent
a78967e51b
commit
50425e979b
@ -22,11 +22,11 @@ use alloc::{
|
|||||||
pub use bdk_chain::keychain::Balance;
|
pub use bdk_chain::keychain::Balance;
|
||||||
use bdk_chain::{
|
use bdk_chain::{
|
||||||
indexed_tx_graph::{IndexedAdditions, IndexedTxGraph},
|
indexed_tx_graph::{IndexedAdditions, IndexedTxGraph},
|
||||||
keychain::{DerivationAdditions, KeychainTxOutIndex, LocalUpdate},
|
keychain::{KeychainTxOutIndex, LocalChangeSet, LocalUpdate},
|
||||||
local_chain::{self, LocalChain, UpdateNotConnectedError},
|
local_chain::{self, LocalChain, UpdateNotConnectedError},
|
||||||
tx_graph::{CanonicalTx, TxGraph},
|
tx_graph::{CanonicalTx, TxGraph},
|
||||||
Anchor, Append, BlockId, ConfirmationTime, ConfirmationTimeAnchor, FullTxOut, ObservedAs,
|
Append, BlockId, ConfirmationTime, ConfirmationTimeAnchor, FullTxOut, ObservedAs, Persist,
|
||||||
Persist, PersistBackend,
|
PersistBackend,
|
||||||
};
|
};
|
||||||
use bitcoin::consensus::encode::serialize;
|
use bitcoin::consensus::encode::serialize;
|
||||||
use bitcoin::secp256k1::Secp256k1;
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
@ -96,67 +96,8 @@ pub struct Wallet<D = ()> {
|
|||||||
/// The update to a [`Wallet`] used in [`Wallet::apply_update`]. This is usually returned from blockchain data sources.
|
/// The update to a [`Wallet`] used in [`Wallet::apply_update`]. This is usually returned from blockchain data sources.
|
||||||
pub type Update = LocalUpdate<KeychainKind, ConfirmationTimeAnchor>;
|
pub type Update = LocalUpdate<KeychainKind, ConfirmationTimeAnchor>;
|
||||||
|
|
||||||
/// The changeset produced internally by applying an update.
|
// /// The changeset produced internally by applying an update.
|
||||||
#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
|
pub(crate) type ChangeSet = LocalChangeSet<KeychainKind, ConfirmationTimeAnchor>;
|
||||||
#[serde(bound(
|
|
||||||
deserialize = "A: Ord + serde::Deserialize<'de>, K: Ord + serde::Deserialize<'de>",
|
|
||||||
serialize = "A: Ord + serde::Serialize, K: Ord + serde::Serialize"
|
|
||||||
))]
|
|
||||||
pub struct ChangeSet<K = KeychainKind, A = ConfirmationTimeAnchor> {
|
|
||||||
pub chain_changeset: local_chain::ChangeSet,
|
|
||||||
pub indexed_additions: IndexedAdditions<A, DerivationAdditions<K>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, A> Default for ChangeSet<K, A> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
chain_changeset: Default::default(),
|
|
||||||
indexed_additions: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: Ord, A: Anchor> Append for ChangeSet<K, A> {
|
|
||||||
fn append(&mut self, other: Self) {
|
|
||||||
Append::append(&mut self.chain_changeset, other.chain_changeset);
|
|
||||||
Append::append(&mut self.indexed_additions, other.indexed_additions);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.chain_changeset.is_empty() && self.indexed_additions.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, A> From<IndexedAdditions<A, DerivationAdditions<K>>> for ChangeSet<K, A> {
|
|
||||||
fn from(indexed_additions: IndexedAdditions<A, DerivationAdditions<K>>) -> Self {
|
|
||||||
Self {
|
|
||||||
indexed_additions,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, A> From<DerivationAdditions<K>> for ChangeSet<K, A> {
|
|
||||||
fn from(index_additions: DerivationAdditions<K>) -> Self {
|
|
||||||
Self {
|
|
||||||
indexed_additions: IndexedAdditions {
|
|
||||||
index_additions,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, A> From<local_chain::ChangeSet> for ChangeSet<K, A> {
|
|
||||||
fn from(chain_changeset: local_chain::ChangeSet) -> Self {
|
|
||||||
Self {
|
|
||||||
chain_changeset,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The address index selection strategy to use to derived an address from the wallet's external
|
/// The address index selection strategy to use to derived an address from the wallet's external
|
||||||
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
|
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -356,10 +297,11 @@ impl<D> Wallet<D> {
|
|||||||
let txout_index = &mut self.indexed_graph.index;
|
let txout_index = &mut self.indexed_graph.index;
|
||||||
let (index, spk) = match address_index {
|
let (index, spk) = match address_index {
|
||||||
AddressIndex::New => {
|
AddressIndex::New => {
|
||||||
let ((index, spk), changeset) = txout_index.reveal_next_spk(&keychain);
|
let ((index, spk), index_additions) = txout_index.reveal_next_spk(&keychain);
|
||||||
let spk = spk.clone();
|
let spk = spk.clone();
|
||||||
|
|
||||||
self.persist.stage(changeset.into());
|
self.persist
|
||||||
|
.stage(ChangeSet::from(IndexedAdditions::from(index_additions)));
|
||||||
self.persist.commit().expect("TODO");
|
self.persist.commit().expect("TODO");
|
||||||
(index, spk)
|
(index, spk)
|
||||||
}
|
}
|
||||||
@ -931,11 +873,12 @@ impl<D> Wallet<D> {
|
|||||||
Some(ref drain_recipient) => drain_recipient.clone(),
|
Some(ref drain_recipient) => drain_recipient.clone(),
|
||||||
None => {
|
None => {
|
||||||
let change_keychain = self.map_keychain(KeychainKind::Internal);
|
let change_keychain = self.map_keychain(KeychainKind::Internal);
|
||||||
let ((index, spk), changeset) =
|
let ((index, spk), index_additions) =
|
||||||
self.indexed_graph.index.next_unused_spk(&change_keychain);
|
self.indexed_graph.index.next_unused_spk(&change_keychain);
|
||||||
let spk = spk.clone();
|
let spk = spk.clone();
|
||||||
self.indexed_graph.index.mark_used(&change_keychain, index);
|
self.indexed_graph.index.mark_used(&change_keychain, index);
|
||||||
self.persist.stage(changeset.into());
|
self.persist
|
||||||
|
.stage(ChangeSet::from(IndexedAdditions::from(index_additions)));
|
||||||
self.persist.commit().expect("TODO");
|
self.persist.commit().expect("TODO");
|
||||||
spk
|
spk
|
||||||
}
|
}
|
||||||
@ -1751,11 +1694,11 @@ impl<D> Wallet<D> {
|
|||||||
D: PersistBackend<ChangeSet>,
|
D: PersistBackend<ChangeSet>,
|
||||||
{
|
{
|
||||||
let mut changeset: ChangeSet = self.chain.apply_update(update.chain)?.into();
|
let mut changeset: ChangeSet = self.chain.apply_update(update.chain)?.into();
|
||||||
let (_, derivation_additions) = self
|
let (_, index_additions) = self
|
||||||
.indexed_graph
|
.indexed_graph
|
||||||
.index
|
.index
|
||||||
.reveal_to_target_multi(&update.keychain);
|
.reveal_to_target_multi(&update.keychain);
|
||||||
changeset.append(derivation_additions.into());
|
changeset.append(ChangeSet::from(IndexedAdditions::from(index_additions)));
|
||||||
changeset.append(self.indexed_graph.apply_update(update.graph).into());
|
changeset.append(self.indexed_graph.apply_update(update.graph).into());
|
||||||
|
|
||||||
let changed = !changeset.is_empty();
|
let changed = !changeset.is_empty();
|
||||||
|
@ -2,6 +2,7 @@ use alloc::vec::Vec;
|
|||||||
use bitcoin::{OutPoint, Transaction, TxOut};
|
use bitcoin::{OutPoint, Transaction, TxOut};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
keychain::DerivationAdditions,
|
||||||
tx_graph::{Additions, TxGraph},
|
tx_graph::{Additions, TxGraph},
|
||||||
Anchor, Append,
|
Anchor, Append,
|
||||||
};
|
};
|
||||||
@ -212,6 +213,15 @@ impl<A, IA: Default> From<Additions<A>> for IndexedAdditions<A, IA> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<A, K> From<DerivationAdditions<K>> for IndexedAdditions<A, DerivationAdditions<K>> {
|
||||||
|
fn from(index_additions: DerivationAdditions<K>) -> Self {
|
||||||
|
Self {
|
||||||
|
graph_additions: Default::default(),
|
||||||
|
index_additions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a structure that can index transaction data.
|
/// Represents a structure that can index transaction data.
|
||||||
pub trait Indexer {
|
pub trait Indexer {
|
||||||
/// The resultant "additions" when new transaction data is indexed.
|
/// The resultant "additions" when new transaction data is indexed.
|
||||||
|
@ -18,10 +18,11 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
chain_graph::{self, ChainGraph},
|
chain_graph::{self, ChainGraph},
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
local_chain::LocalChain,
|
indexed_tx_graph::IndexedAdditions,
|
||||||
|
local_chain::{self, LocalChain},
|
||||||
sparse_chain::ChainPosition,
|
sparse_chain::ChainPosition,
|
||||||
tx_graph::TxGraph,
|
tx_graph::TxGraph,
|
||||||
Append, ForEachTxOut,
|
Anchor, Append, ForEachTxOut,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "miniscript")]
|
#[cfg(feature = "miniscript")]
|
||||||
@ -125,6 +126,67 @@ impl<K, A> Default for LocalUpdate<K, A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A structure that records the corresponding changes as result of applying an [`LocalUpdate`].
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(
|
||||||
|
crate = "serde_crate",
|
||||||
|
bound(
|
||||||
|
deserialize = "K: Ord + serde::Deserialize<'de>, A: Ord + serde::Deserialize<'de>",
|
||||||
|
serialize = "K: Ord + serde::Serialize, A: Ord + serde::Serialize",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub struct LocalChangeSet<K, A> {
|
||||||
|
/// Changes to the [`LocalChain`].
|
||||||
|
pub chain_changeset: local_chain::ChangeSet,
|
||||||
|
|
||||||
|
/// Additions to [`IndexedTxGraph`].
|
||||||
|
///
|
||||||
|
/// [`IndexedTxGraph`]: crate::indexed_tx_graph::IndexedTxGraph
|
||||||
|
pub indexed_additions: IndexedAdditions<A, DerivationAdditions<K>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, A> Default for LocalChangeSet<K, A> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
chain_changeset: Default::default(),
|
||||||
|
indexed_additions: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Ord, A: Anchor> Append for LocalChangeSet<K, A> {
|
||||||
|
fn append(&mut self, other: Self) {
|
||||||
|
Append::append(&mut self.chain_changeset, other.chain_changeset);
|
||||||
|
Append::append(&mut self.indexed_additions, other.indexed_additions);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.chain_changeset.is_empty() && self.indexed_additions.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, A> From<local_chain::ChangeSet> for LocalChangeSet<K, A> {
|
||||||
|
fn from(chain_changeset: local_chain::ChangeSet) -> Self {
|
||||||
|
Self {
|
||||||
|
chain_changeset,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, A> From<IndexedAdditions<A, DerivationAdditions<K>>> for LocalChangeSet<K, A> {
|
||||||
|
fn from(indexed_additions: IndexedAdditions<A, DerivationAdditions<K>>) -> Self {
|
||||||
|
Self {
|
||||||
|
indexed_additions,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// An update that includes the last active indexes of each keychain.
|
/// An update that includes the last active indexes of each keychain.
|
||||||
pub struct KeychainScan<K, P> {
|
pub struct KeychainScan<K, P> {
|
||||||
|
@ -26,42 +26,13 @@ pub use clap;
|
|||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
pub type KeychainTxGraph<A> = IndexedTxGraph<A, KeychainTxOutIndex<Keychain>>;
|
pub type KeychainTxGraph<A> = IndexedTxGraph<A, KeychainTxOutIndex<Keychain>>;
|
||||||
pub type Database<'m, A, X> = Persist<Store<'m, ChangeSet<A, X>>, ChangeSet<A, X>>;
|
pub type KeychainAdditions<A> = IndexedAdditions<A, DerivationAdditions<Keychain>>;
|
||||||
|
pub type Database<'m, C> = Persist<Store<'m, C>, C>;
|
||||||
#[derive(Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
||||||
#[serde(bound(
|
|
||||||
deserialize = "A: Ord + serde::Deserialize<'de>, X: serde::Deserialize<'de>",
|
|
||||||
serialize = "A: Ord + serde::Serialize, X: serde::Serialize",
|
|
||||||
))]
|
|
||||||
pub struct ChangeSet<A, X> {
|
|
||||||
pub indexed_additions: IndexedAdditions<A, DerivationAdditions<Keychain>>,
|
|
||||||
pub extension: X,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A, X: Default> Default for ChangeSet<A, X> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
indexed_additions: Default::default(),
|
|
||||||
extension: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Anchor, X: Append> Append for ChangeSet<A, X> {
|
|
||||||
fn append(&mut self, other: Self) {
|
|
||||||
Append::append(&mut self.indexed_additions, other.indexed_additions);
|
|
||||||
Append::append(&mut self.extension, other.extension)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.indexed_additions.is_empty() && self.extension.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(author, version, about, long_about = None)]
|
#[clap(author, version, about, long_about = None)]
|
||||||
#[clap(propagate_version = true)]
|
#[clap(propagate_version = true)]
|
||||||
pub struct Args<C: clap::Subcommand> {
|
pub struct Args<S: clap::Subcommand> {
|
||||||
#[clap(env = "DESCRIPTOR")]
|
#[clap(env = "DESCRIPTOR")]
|
||||||
pub descriptor: String,
|
pub descriptor: String,
|
||||||
#[clap(env = "CHANGE_DESCRIPTOR")]
|
#[clap(env = "CHANGE_DESCRIPTOR")]
|
||||||
@ -77,14 +48,14 @@ pub struct Args<C: clap::Subcommand> {
|
|||||||
pub cp_limit: usize,
|
pub cp_limit: usize,
|
||||||
|
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
pub command: Commands<C>,
|
pub command: Commands<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::almost_swapped)]
|
#[allow(clippy::almost_swapped)]
|
||||||
#[derive(Subcommand, Debug, Clone)]
|
#[derive(Subcommand, Debug, Clone)]
|
||||||
pub enum Commands<C: clap::Subcommand> {
|
pub enum Commands<S: clap::Subcommand> {
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
ChainSpecific(C),
|
ChainSpecific(S),
|
||||||
/// Address generation and inspection.
|
/// Address generation and inspection.
|
||||||
Address {
|
Address {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
@ -210,14 +181,14 @@ impl core::fmt::Display for Keychain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_address_cmd<A, X>(
|
pub fn run_address_cmd<A, C>(
|
||||||
graph: &mut KeychainTxGraph<A>,
|
graph: &mut KeychainTxGraph<A>,
|
||||||
db: &Mutex<Database<'_, A, X>>,
|
db: &Mutex<Database<C>>,
|
||||||
network: Network,
|
network: Network,
|
||||||
cmd: AddressCmd,
|
cmd: AddressCmd,
|
||||||
) -> anyhow::Result<()>
|
) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
ChangeSet<A, X>: Default + Append + DeserializeOwned + Serialize,
|
C: Default + Append + DeserializeOwned + Serialize + From<KeychainAdditions<A>>,
|
||||||
{
|
{
|
||||||
let index = &mut graph.index;
|
let index = &mut graph.index;
|
||||||
|
|
||||||
@ -231,13 +202,7 @@ where
|
|||||||
|
|
||||||
let ((spk_i, spk), index_additions) = spk_chooser(index, &Keychain::External);
|
let ((spk_i, spk), index_additions) = spk_chooser(index, &Keychain::External);
|
||||||
let db = &mut *db.lock().unwrap();
|
let db = &mut *db.lock().unwrap();
|
||||||
db.stage(ChangeSet {
|
db.stage(C::from(KeychainAdditions::from(index_additions)));
|
||||||
indexed_additions: IndexedAdditions {
|
|
||||||
index_additions,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
db.commit()?;
|
db.commit()?;
|
||||||
let addr = Address::from_script(spk, network).context("failed to derive address")?;
|
let addr = Address::from_script(spk, network).context("failed to derive address")?;
|
||||||
println!("[address @ {}] {}", spk_i, addr);
|
println!("[address @ {}] {}", spk_i, addr);
|
||||||
@ -351,9 +316,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn run_send_cmd<A: Anchor, O: ChainOracle, X>(
|
pub fn run_send_cmd<A: Anchor, O: ChainOracle, C>(
|
||||||
graph: &Mutex<KeychainTxGraph<A>>,
|
graph: &Mutex<KeychainTxGraph<A>>,
|
||||||
db: &Mutex<Database<'_, A, X>>,
|
db: &Mutex<Database<'_, C>>,
|
||||||
chain: &O,
|
chain: &O,
|
||||||
keymap: &HashMap<DescriptorPublicKey, DescriptorSecretKey>,
|
keymap: &HashMap<DescriptorPublicKey, DescriptorSecretKey>,
|
||||||
cs_algorithm: CoinSelectionAlgo,
|
cs_algorithm: CoinSelectionAlgo,
|
||||||
@ -363,7 +328,7 @@ pub fn run_send_cmd<A: Anchor, O: ChainOracle, X>(
|
|||||||
) -> anyhow::Result<()>
|
) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
O::Error: std::error::Error + Send + Sync + 'static,
|
O::Error: std::error::Error + Send + Sync + 'static,
|
||||||
ChangeSet<A, X>: Default + Append + DeserializeOwned + Serialize,
|
C: Default + Append + DeserializeOwned + Serialize + From<KeychainAdditions<A>>,
|
||||||
{
|
{
|
||||||
let (transaction, change_index) = {
|
let (transaction, change_index) = {
|
||||||
let graph = &mut *graph.lock().unwrap();
|
let graph = &mut *graph.lock().unwrap();
|
||||||
@ -374,13 +339,9 @@ where
|
|||||||
// We must first persist to disk the fact that we've got a new address from the
|
// We must first persist to disk the fact that we've got a new address from the
|
||||||
// change keychain so future scans will find the tx we're about to broadcast.
|
// change keychain so future scans will find the tx we're about to broadcast.
|
||||||
// If we're unable to persist this, then we don't want to broadcast.
|
// If we're unable to persist this, then we don't want to broadcast.
|
||||||
db.lock().unwrap().stage(ChangeSet {
|
db.lock()
|
||||||
indexed_additions: IndexedAdditions {
|
.unwrap()
|
||||||
index_additions,
|
.stage(C::from(KeychainAdditions::from(index_additions)));
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
// We don't want other callers/threads to use this address while we're using it
|
// 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
|
// but we also don't want to scan the tx we just created because it's not
|
||||||
@ -396,15 +357,12 @@ where
|
|||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
println!("Broadcasted Tx : {}", transaction.txid());
|
println!("Broadcasted Tx : {}", transaction.txid());
|
||||||
|
|
||||||
let indexed_additions = graph.lock().unwrap().insert_tx(&transaction, None, None);
|
let keychain_additions = graph.lock().unwrap().insert_tx(&transaction, None, None);
|
||||||
|
|
||||||
// We know the tx is at least unconfirmed now. Note if persisting here fails,
|
// 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
|
// it's not a big deal since we can always find it again form
|
||||||
// blockchain.
|
// blockchain.
|
||||||
db.lock().unwrap().stage(ChangeSet {
|
db.lock().unwrap().stage(C::from(keychain_additions));
|
||||||
indexed_additions,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -659,18 +617,18 @@ pub fn planned_utxos<A: Anchor, O: ChainOracle, K: Clone + bdk_tmp_plan::CanDeri
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_commands<C: clap::Subcommand, A: Anchor, O: ChainOracle, X>(
|
pub fn handle_commands<S: clap::Subcommand, A: Anchor, O: ChainOracle, C>(
|
||||||
graph: &Mutex<KeychainTxGraph<A>>,
|
graph: &Mutex<KeychainTxGraph<A>>,
|
||||||
db: &Mutex<Database<A, X>>,
|
db: &Mutex<Database<C>>,
|
||||||
chain: &Mutex<O>,
|
chain: &Mutex<O>,
|
||||||
keymap: &HashMap<DescriptorPublicKey, DescriptorSecretKey>,
|
keymap: &HashMap<DescriptorPublicKey, DescriptorSecretKey>,
|
||||||
network: Network,
|
network: Network,
|
||||||
broadcast: impl FnOnce(&Transaction) -> anyhow::Result<()>,
|
broadcast: impl FnOnce(&Transaction) -> anyhow::Result<()>,
|
||||||
cmd: Commands<C>,
|
cmd: Commands<S>,
|
||||||
) -> anyhow::Result<()>
|
) -> anyhow::Result<()>
|
||||||
where
|
where
|
||||||
O::Error: std::error::Error + Send + Sync + 'static,
|
O::Error: std::error::Error + Send + Sync + 'static,
|
||||||
ChangeSet<A, X>: Default + Append + DeserializeOwned + Serialize,
|
C: Default + Append + DeserializeOwned + Serialize + From<KeychainAdditions<A>>,
|
||||||
{
|
{
|
||||||
match cmd {
|
match cmd {
|
||||||
Commands::ChainSpecific(_) => unreachable!("example code should handle this!"),
|
Commands::ChainSpecific(_) => unreachable!("example code should handle this!"),
|
||||||
@ -708,9 +666,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare_index<C: clap::Subcommand, SC: secp256k1::Signing>(
|
pub fn prepare_index<S: clap::Subcommand, CTX: secp256k1::Signing>(
|
||||||
args: &Args<C>,
|
args: &Args<S>,
|
||||||
secp: &Secp256k1<SC>,
|
secp: &Secp256k1<CTX>,
|
||||||
) -> anyhow::Result<(KeychainTxOutIndex<Keychain>, KeyMap)> {
|
) -> anyhow::Result<(KeychainTxOutIndex<Keychain>, KeyMap)> {
|
||||||
let mut index = KeychainTxOutIndex::<Keychain>::default();
|
let mut index = KeychainTxOutIndex::<Keychain>::default();
|
||||||
|
|
||||||
@ -732,18 +690,18 @@ pub fn prepare_index<C: clap::Subcommand, SC: secp256k1::Signing>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn init<'m, S: clap::Subcommand, A: Anchor, X>(
|
pub fn init<'m, S: clap::Subcommand, C>(
|
||||||
db_magic: &'m [u8],
|
db_magic: &'m [u8],
|
||||||
db_default_path: &str,
|
db_default_path: &str,
|
||||||
) -> anyhow::Result<(
|
) -> anyhow::Result<(
|
||||||
Args<S>,
|
Args<S>,
|
||||||
KeyMap,
|
KeyMap,
|
||||||
Mutex<KeychainTxGraph<A>>,
|
KeychainTxOutIndex<Keychain>,
|
||||||
Mutex<Database<'m, A, X>>,
|
Mutex<Database<'m, C>>,
|
||||||
X,
|
C,
|
||||||
)>
|
)>
|
||||||
where
|
where
|
||||||
ChangeSet<A, X>: Default + Append + Serialize + DeserializeOwned,
|
C: Default + Append + Serialize + DeserializeOwned,
|
||||||
{
|
{
|
||||||
if std::env::var("BDK_DB_PATH").is_err() {
|
if std::env::var("BDK_DB_PATH").is_err() {
|
||||||
std::env::set_var("BDK_DB_PATH", db_default_path);
|
std::env::set_var("BDK_DB_PATH", db_default_path);
|
||||||
@ -752,25 +710,19 @@ where
|
|||||||
let secp = Secp256k1::default();
|
let secp = Secp256k1::default();
|
||||||
let (index, keymap) = prepare_index(&args, &secp)?;
|
let (index, keymap) = prepare_index(&args, &secp)?;
|
||||||
|
|
||||||
let mut indexed_graph = IndexedTxGraph::<A, KeychainTxOutIndex<Keychain>>::new(index);
|
let mut db_backend = match Store::<'m, C>::new_from_path(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)),
|
||||||
|
};
|
||||||
|
|
||||||
let mut db_backend =
|
let init_changeset = db_backend.load_from_persistence()?;
|
||||||
match Store::<'m, ChangeSet<A, X>>::new_from_path(db_magic, args.db_path.as_path()) {
|
|
||||||
Ok(db_backend) => db_backend,
|
|
||||||
Err(err) => return Err(anyhow::anyhow!("failed to init db backend: {:?}", err)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let ChangeSet {
|
|
||||||
indexed_additions,
|
|
||||||
extension,
|
|
||||||
} = db_backend.load_from_persistence()?;
|
|
||||||
indexed_graph.apply_additions(indexed_additions);
|
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
args,
|
args,
|
||||||
keymap,
|
keymap,
|
||||||
Mutex::new(indexed_graph),
|
index,
|
||||||
Mutex::new(Database::new(db_backend)),
|
Mutex::new(Database::new(db_backend)),
|
||||||
extension,
|
init_changeset,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,9 @@ use std::{
|
|||||||
|
|
||||||
use bdk_chain::{
|
use bdk_chain::{
|
||||||
bitcoin::{Address, BlockHash, Network, OutPoint, Txid},
|
bitcoin::{Address, BlockHash, Network, OutPoint, Txid},
|
||||||
indexed_tx_graph::IndexedAdditions,
|
indexed_tx_graph::{IndexedAdditions, IndexedTxGraph},
|
||||||
local_chain::{self, LocalChain},
|
keychain::LocalChangeSet,
|
||||||
|
local_chain::LocalChain,
|
||||||
Append, ConfirmationHeightAnchor,
|
Append, ConfirmationHeightAnchor,
|
||||||
};
|
};
|
||||||
use bdk_electrum::{
|
use bdk_electrum::{
|
||||||
@ -17,6 +18,7 @@ use bdk_electrum::{
|
|||||||
use example_cli::{
|
use example_cli::{
|
||||||
anyhow::{self, Context},
|
anyhow::{self, Context},
|
||||||
clap::{self, Parser, Subcommand},
|
clap::{self, Parser, Subcommand},
|
||||||
|
Keychain,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DB_MAGIC: &[u8] = b"bdk_example_electrum";
|
const DB_MAGIC: &[u8] = b"bdk_example_electrum";
|
||||||
@ -59,15 +61,21 @@ pub struct ScanOptions {
|
|||||||
pub batch_size: usize,
|
pub batch_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChangeSet = LocalChangeSet<Keychain, ConfirmationHeightAnchor>;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let (args, keymap, graph, db, chain_changeset) =
|
let (args, keymap, index, db, init_changeset) =
|
||||||
example_cli::init::<ElectrumCommands, ConfirmationHeightAnchor, local_chain::ChangeSet>(
|
example_cli::init::<ElectrumCommands, ChangeSet>(DB_MAGIC, DB_PATH)?;
|
||||||
DB_MAGIC, DB_PATH,
|
|
||||||
)?;
|
let graph = Mutex::new({
|
||||||
|
let mut graph = IndexedTxGraph::new(index);
|
||||||
|
graph.apply_additions(init_changeset.indexed_additions);
|
||||||
|
graph
|
||||||
|
});
|
||||||
|
|
||||||
let chain = Mutex::new({
|
let chain = Mutex::new({
|
||||||
let mut chain = LocalChain::default();
|
let mut chain = LocalChain::default();
|
||||||
chain.apply_changeset(chain_changeset);
|
chain.apply_changeset(init_changeset.chain_changeset);
|
||||||
chain
|
chain
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -302,9 +310,9 @@ fn main() -> anyhow::Result<()> {
|
|||||||
additions
|
additions
|
||||||
};
|
};
|
||||||
|
|
||||||
example_cli::ChangeSet {
|
ChangeSet {
|
||||||
indexed_additions,
|
indexed_additions,
|
||||||
extension: chain_changeset,
|
chain_changeset,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user