Merge bitcoindevkit/bdk#1084: Enhance bdk chain structures

1ff806c67f fix(chain)!: rm weird `From` impl (志宇)
d43ae0231f refactor: improve docs, cleanup unnecessary types and improve code (Vladimir Fomene)
4104206980 feat: impl Append for lots of tuples (LLFourn)
c56728ff13 refactor: Remove `scan` and `scan_txout` from SpkTxoutIndex and KeychainTxoutIndex (Vladimir Fomene)
32c40ac939 feat(electrum)!: change signature of `ElectrumExt` (志宇)
a28748c339 refactor: Implement Default for WalletUpdate (Vladimir Fomene)
f42f8b8ff1 refactor: Allow for no chain update (Vladimir Fomene)
68572bfd2e refactor: move WalletChangeset to wallet module (Vladimir Fomene)
2392e50fd9 refactor: Move WalletUpdate to wallet module (Vladimir Fomene)
7c12dc9942 refactor: Remove ForEachTxout trait (Vladimir Fomene)
6bcbb93233 refactor: Edit ElectrumExt not to use WalletUpdate (Vladimir Fomene)

Pull request description:

  ### Description

  Fixes #1061

  ### Changelog notice

  - Move WalletUpdate to the wallet module
  - Remove ForEachTxout trait completely
  - Refactor ElectrumExt to not use WalletUpdate.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

ACKs for top commit:
  evanlinjin:
    ACK 1ff806c67f

Tree-SHA512: 05349713af9d2efa14a522ceaabb7513bb437d786adf2f93055765589a67e4eb68bda36ff415aeba07816c4d30988d4d55bac018e7697019270a219105ed65a2
This commit is contained in:
志宇
2023-09-15 09:24:05 +08:00
17 changed files with 325 additions and 393 deletions

View File

@@ -12,6 +12,7 @@ use bdk_chain::{
},
indexed_tx_graph::{self, IndexedTxGraph},
keychain::{self, KeychainTxOutIndex},
local_chain,
miniscript::{
descriptor::{DescriptorSecretKey, KeyMap},
Descriptor, DescriptorPublicKey,
@@ -24,7 +25,10 @@ pub use clap;
use clap::{Parser, Subcommand};
pub type KeychainTxGraph<A> = IndexedTxGraph<A, KeychainTxOutIndex<Keychain>>;
pub type KeychainChangeSet<A> = indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<Keychain>>;
pub type KeychainChangeSet<A> = (
local_chain::ChangeSet,
indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<Keychain>>,
);
pub type Database<'m, C> = Persist<Store<'m, C>, C>;
#[derive(Parser)]
@@ -200,7 +204,10 @@ where
let ((spk_i, spk), index_changeset) = spk_chooser(index, &Keychain::External);
let db = &mut *db.lock().unwrap();
db.stage(C::from(KeychainChangeSet::from(index_changeset)));
db.stage(C::from((
local_chain::ChangeSet::default(),
indexed_tx_graph::ChangeSet::from(index_changeset),
)));
db.commit()?;
let addr = Address::from_script(spk, network).context("failed to derive address")?;
println!("[address @ {}] {}", spk_i, addr);
@@ -353,7 +360,10 @@ where
// If we're unable to persist this, then we don't want to broadcast.
{
let db = &mut *db.lock().unwrap();
db.stage(C::from(KeychainChangeSet::from(index_changeset)));
db.stage(C::from((
local_chain::ChangeSet::default(),
indexed_tx_graph::ChangeSet::from(index_changeset),
)));
db.commit()?;
}
@@ -376,7 +386,10 @@ where
// 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().stage(C::from(keychain_changeset));
db.lock().unwrap().stage(C::from((
local_chain::ChangeSet::default(),
keychain_changeset,
)));
Ok(())
}
Err(e) => {

View File

@@ -7,8 +7,8 @@ use std::{
use bdk_chain::{
bitcoin::{Address, Network, OutPoint, ScriptBuf, Txid},
indexed_tx_graph::{self, IndexedTxGraph},
keychain::WalletChangeSet,
local_chain::LocalChain,
keychain,
local_chain::{self, LocalChain},
Append, ConfirmationHeightAnchor,
};
use bdk_electrum::{
@@ -60,19 +60,22 @@ pub struct ScanOptions {
pub batch_size: usize,
}
type ChangeSet = WalletChangeSet<Keychain, ConfirmationHeightAnchor>;
type ChangeSet = (
local_chain::ChangeSet,
indexed_tx_graph::ChangeSet<ConfirmationHeightAnchor, keychain::ChangeSet<Keychain>>,
);
fn main() -> anyhow::Result<()> {
let (args, keymap, index, db, init_changeset) =
let (args, keymap, index, db, (disk_local_chain, disk_tx_graph)) =
example_cli::init::<ElectrumCommands, ChangeSet>(DB_MAGIC, DB_PATH)?;
let graph = Mutex::new({
let mut graph = IndexedTxGraph::new(index);
graph.apply_changeset(init_changeset.indexed_tx_graph);
graph.apply_changeset(disk_tx_graph);
graph
});
let chain = Mutex::new(LocalChain::from_changeset(init_changeset.chain));
let chain = Mutex::new(LocalChain::from_changeset(disk_local_chain));
let electrum_url = match args.network {
Network::Bitcoin => "ssl://electrum.blockstream.info:50002",
@@ -248,20 +251,24 @@ fn main() -> anyhow::Result<()> {
// drop lock on graph and chain
drop((graph, chain));
let update = client
let electrum_update = client
.scan_without_keychain(tip, spks, txids, outpoints, scan_options.batch_size)
.context("scanning the blockchain")?;
ElectrumUpdate {
graph_update: update.graph_update,
new_tip: update.new_tip,
keychain_update: BTreeMap::new(),
}
(electrum_update, BTreeMap::new())
}
};
let (
ElectrumUpdate {
chain_update,
relevant_txids,
},
keychain_update,
) = response;
let missing_txids = {
let graph = &*graph.lock().unwrap();
response.missing_full_txs(graph.graph())
relevant_txids.missing_full_txs(graph.graph())
};
let now = std::time::UNIX_EPOCH
@@ -269,32 +276,27 @@ fn main() -> anyhow::Result<()> {
.expect("must get time")
.as_secs();
let final_update = response.finalize(&client, Some(now), missing_txids)?;
let graph_update = relevant_txids.into_tx_graph(&client, Some(now), missing_txids)?;
let db_changeset = {
let mut chain = chain.lock().unwrap();
let mut graph = graph.lock().unwrap();
let chain = chain.apply_update(final_update.chain)?;
let chain = chain.apply_update(chain_update)?;
let indexed_tx_graph = {
let mut changeset =
indexed_tx_graph::ChangeSet::<ConfirmationHeightAnchor, _>::default();
let (_, indexer) = graph
.index
.reveal_to_target_multi(&final_update.last_active_indices);
let (_, indexer) = graph.index.reveal_to_target_multi(&keychain_update);
changeset.append(indexed_tx_graph::ChangeSet {
indexer,
..Default::default()
});
changeset.append(graph.apply_update(final_update.graph));
changeset.append(graph.apply_update(graph_update));
changeset
};
ChangeSet {
indexed_tx_graph,
chain,
}
(chain, indexed_tx_graph)
};
let mut db = db.lock().unwrap();

View File

@@ -6,9 +6,9 @@ use std::{
use bdk_chain::{
bitcoin::{Address, Network, OutPoint, ScriptBuf, Txid},
indexed_tx_graph::IndexedTxGraph,
keychain::WalletChangeSet,
local_chain::{CheckPoint, LocalChain},
indexed_tx_graph::{self, IndexedTxGraph},
keychain,
local_chain::{self, CheckPoint, LocalChain},
Append, ConfirmationTimeAnchor,
};
@@ -23,6 +23,11 @@ use example_cli::{
const DB_MAGIC: &[u8] = b"bdk_example_esplora";
const DB_PATH: &str = ".bdk_esplora_example.db";
type ChangeSet = (
local_chain::ChangeSet,
indexed_tx_graph::ChangeSet<ConfirmationTimeAnchor, keychain::ChangeSet<Keychain>>,
);
#[derive(Subcommand, Debug, Clone)]
enum EsploraCommands {
/// Scans the addresses in the wallet using the esplora API.
@@ -60,22 +65,22 @@ pub struct ScanOptions {
}
fn main() -> anyhow::Result<()> {
let (args, keymap, index, db, init_changeset) = example_cli::init::<
EsploraCommands,
WalletChangeSet<Keychain, ConfirmationTimeAnchor>,
>(DB_MAGIC, DB_PATH)?;
let (args, keymap, index, db, init_changeset) =
example_cli::init::<EsploraCommands, ChangeSet>(DB_MAGIC, DB_PATH)?;
let (init_chain_changeset, init_indexed_tx_graph_changeset) = init_changeset;
// Contruct `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_changeset.indexed_tx_graph);
graph.apply_changeset(init_indexed_tx_graph_changeset);
graph
});
let chain = Mutex::new({
let mut chain = LocalChain::default();
chain.apply_changeset(&init_changeset.chain);
chain.apply_changeset(&init_chain_changeset);
chain
});
@@ -303,18 +308,17 @@ fn main() -> anyhow::Result<()> {
println!("missing block heights: {:?}", missing_block_heights);
// Here, we actually fetch the missing blocks and create a `local_chain::Update`.
let chain_update = client
.update_local_chain(tip, missing_block_heights)
.context("scanning for blocks")?;
println!("new tip: {}", chain_update.tip.height());
let chain_changeset = {
let chain_update = client
.update_local_chain(tip, missing_block_heights)
.context("scanning for blocks")?;
println!("new tip: {}", chain_update.tip.height());
chain.lock().unwrap().apply_update(chain_update)?
};
// We persist the changes
let mut db = db.lock().unwrap();
db.stage(WalletChangeSet {
chain: chain.lock().unwrap().apply_update(chain_update)?,
indexed_tx_graph: indexed_tx_graph_changeset,
});
db.stage((chain_changeset, indexed_tx_graph_changeset));
db.commit()?;
Ok(())
}

View File

@@ -7,10 +7,13 @@ use std::io::Write;
use std::str::FromStr;
use bdk::bitcoin::Address;
use bdk::wallet::Update;
use bdk::SignOptions;
use bdk::{bitcoin::Network, Wallet};
use bdk_electrum::electrum_client::{self, ElectrumApi};
use bdk_electrum::ElectrumExt;
use bdk_electrum::{
electrum_client::{self, ElectrumApi},
ElectrumExt, ElectrumUpdate,
};
use bdk_file_store::Store;
fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -52,14 +55,25 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
})
.collect();
let electrum_update = client.scan(prev_tip, keychain_spks, None, None, STOP_GAP, BATCH_SIZE)?;
let (
ElectrumUpdate {
chain_update,
relevant_txids,
},
keychain_update,
) = client.scan(prev_tip, keychain_spks, None, None, STOP_GAP, BATCH_SIZE)?;
println!();
let missing = electrum_update.missing_full_txs(wallet.as_ref());
let update = electrum_update.finalize_as_confirmation_time(&client, None, missing)?;
let missing = relevant_txids.missing_full_txs(wallet.as_ref());
let graph_update = relevant_txids.into_confirmation_time_tx_graph(&client, None, missing)?;
wallet.apply_update(update)?;
let wallet_update = Update {
last_active_indices: keychain_update,
graph: graph_update,
chain: Some(chain_update),
};
wallet.apply_update(wallet_update)?;
wallet.commit()?;
let balance = wallet.get_balance();

View File

@@ -2,8 +2,7 @@ use std::{io::Write, str::FromStr};
use bdk::{
bitcoin::{Address, Network},
chain::keychain::WalletUpdate,
wallet::AddressIndex,
wallet::{AddressIndex, Update},
SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraAsyncExt};
@@ -59,10 +58,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.await?;
let missing_heights = wallet.tx_graph().missing_heights(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights).await?;
let update = WalletUpdate {
let update = Update {
last_active_indices,
graph: update_graph,
..WalletUpdate::new(chain_update)
chain: Some(chain_update),
};
wallet.apply_update(update)?;
wallet.commit()?;

View File

@@ -7,8 +7,7 @@ use std::{io::Write, str::FromStr};
use bdk::{
bitcoin::{Address, Network},
chain::keychain::WalletUpdate,
wallet::AddressIndex,
wallet::{AddressIndex, Update},
SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraExt};
@@ -58,10 +57,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
client.scan_txs_with_keychains(keychain_spks, None, None, STOP_GAP, PARALLEL_REQUESTS)?;
let missing_heights = wallet.tx_graph().missing_heights(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights)?;
let update = WalletUpdate {
let update = Update {
last_active_indices,
graph: update_graph,
..WalletUpdate::new(chain_update)
chain: Some(chain_update),
};
wallet.apply_update(update)?;