diff --git a/Cargo.toml b/Cargo.toml index 0edd8c32..a3aa967e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ electrum-client = { version = "0.1.0-beta.5", optional = true } [features] minimal = [] compiler = ["miniscript/compiler"] -default = ["sled", "electrum-client"] +default = ["key-value-db", "electrum"] electrum = ["electrum-client"] key-value-db = ["sled"] diff --git a/examples/repl.rs b/examples/repl.rs index 82c41f30..66598514 100644 --- a/examples/repl.rs +++ b/examples/repl.rs @@ -23,6 +23,7 @@ use bitcoin::util::psbt::PartiallySignedTransaction; use bitcoin::{Address, Network, OutPoint}; use magical_bitcoin_wallet::bitcoin; +use magical_bitcoin_wallet::blockchain::ElectrumBlockchain; use magical_bitcoin_wallet::sled; use magical_bitcoin_wallet::types::ScriptType; use magical_bitcoin_wallet::{Client, Wallet}; @@ -255,7 +256,14 @@ fn main() { debug!("database opened successfully"); let client = Client::new(matches.value_of("server").unwrap()).unwrap(); - let wallet = Wallet::new(descriptor, change_descriptor, network, tree, client).unwrap(); + let wallet = Wallet::new( + descriptor, + change_descriptor, + network, + tree, + ElectrumBlockchain::from(client), + ) + .unwrap(); // TODO: print errors in a nice way let handle_matches = |matches: ArgMatches<'_>| { diff --git a/src/blockchain/electrum.rs b/src/blockchain/electrum.rs new file mode 100644 index 00000000..592953e2 --- /dev/null +++ b/src/blockchain/electrum.rs @@ -0,0 +1,333 @@ +use std::cmp; +use std::collections::{HashSet, VecDeque}; +use std::convert::TryFrom; +use std::io::{Read, Write}; + +#[allow(unused_imports)] +use log::{debug, error, info, trace}; + +use bitcoin::{Address, Network, OutPoint, Script, Transaction, Txid}; + +use electrum_client::types::*; +use electrum_client::Client; + +use super::*; +use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils}; +use crate::error::Error; +use crate::types::{ScriptType, TransactionDetails, UTXO}; +use crate::wallet::utils::ChunksIterator; + +pub struct ElectrumBlockchain(Option>); + +impl std::convert::From> for ElectrumBlockchain { + fn from(client: Client) -> Self { + ElectrumBlockchain(Some(client)) + } +} + +impl Blockchain for ElectrumBlockchain { + fn offline() -> Self { + ElectrumBlockchain(None) + } +} + +impl OnlineBlockchain for ElectrumBlockchain { + fn get_capabilities(&self) -> HashSet { + vec![Capability::FullHistory, Capability::GetAnyTx] + .into_iter() + .collect() + } + + fn setup( + &mut self, + stop_gap: Option, + database: &mut D, + _progress_update: P, + ) -> Result<(), Error> { + // TODO: progress + + let stop_gap = stop_gap.unwrap_or(20); + let batch_query_size = 20; + + // check unconfirmed tx, delete so they are retrieved later + let mut del_batch = database.begin_batch(); + for tx in database.iter_txs(false)? { + if tx.height.is_none() { + del_batch.del_tx(&tx.txid, false)?; + } + } + database.commit_batch(del_batch)?; + + // maximum derivation index for a change address that we've seen during sync + let mut change_max_deriv = 0; + + let mut already_checked: HashSet