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:
志宇 2023-05-13 23:28:03 +08:00
parent a78967e51b
commit 50425e979b
No known key found for this signature in database
GPG Key ID: F6345C9837C2BDE8
5 changed files with 142 additions and 167 deletions

View File

@ -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();

View File

@ -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.

View File

@ -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> {

View File

@ -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,
)) ))
} }

View File

@ -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,
} }
}; };