maybe_await!(impl_inner_method!(self, get_capabilities))
}
- fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
- maybe_await!(impl_inner_method!(self, get_tx, txid))
- }
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
maybe_await!(impl_inner_method!(self, broadcast, tx))
}
}
}
+#[maybe_async]
+impl GetTx for AnyBlockchain {
+ fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
+ maybe_await!(impl_inner_method!(self, get_tx, txid))
+ }
+}
+
#[maybe_async]
impl WalletSync for AnyBlockchain {
fn wallet_sync<D: BatchDatabase>(
mod store;
mod sync;
-use super::{Blockchain, Capability, ConfigurableBlockchain, GetHeight, Progress, WalletSync};
+use crate::blockchain::*;
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
use crate::error::Error;
use crate::types::{KeychainKind, LocalUtxo, TransactionDetails};
vec![Capability::FullHistory].into_iter().collect()
}
- fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
- Ok(self.peers[0]
- .get_mempool()
- .get_tx(&Inventory::Transaction(*txid)))
- }
-
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
self.peers[0].broadcast_tx(tx.clone())?;
}
}
+impl GetTx for CompactFiltersBlockchain {
+ fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
+ Ok(self.peers[0]
+ .get_mempool()
+ .get_tx(&Inventory::Transaction(*txid)))
+ }
+}
+
impl WalletSync for CompactFiltersBlockchain {
#[allow(clippy::mutex_atomic)] // Mutex is easier to understand than a CAS loop.
fn wallet_setup<D: BatchDatabase>(
.collect()
}
- fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
- Ok(self.client.transaction_get(txid).map(Option::Some)?)
- }
-
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
Ok(self.client.transaction_broadcast(tx).map(|_| ())?)
}
}
}
+impl GetTx for ElectrumBlockchain {
+ fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
+ Ok(self.client.transaction_get(txid).map(Option::Some)?)
+ }
+}
+
impl WalletSync for ElectrumBlockchain {
fn wallet_setup<D: BatchDatabase>(
&self,
.collect()
}
- fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
- Ok(await_or_block!(self.url_client._get_tx(txid))?)
- }
-
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
Ok(await_or_block!(self.url_client._broadcast(tx))?)
}
}
}
+#[maybe_async]
+impl GetTx for EsploraBlockchain {
+ fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
+ Ok(await_or_block!(self.url_client._get_tx(txid))?)
+ }
+}
+
#[maybe_async]
impl WalletSync for EsploraBlockchain {
fn wallet_setup<D: BatchDatabase>(
.collect()
}
- fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
- Ok(self.url_client._get_tx(txid)?)
- }
-
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
let _txid = self.url_client._broadcast(tx)?;
Ok(())
}
}
+impl GetTx for EsploraBlockchain {
+ fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
+ Ok(self.url_client._get_tx(txid)?)
+ }
+}
+
impl WalletSync for EsploraBlockchain {
fn wallet_setup<D: BatchDatabase>(
&self,
/// Trait that defines the actions that must be supported by a blockchain backend
#[maybe_async]
-pub trait Blockchain: WalletSync + GetHeight {
+pub trait Blockchain: WalletSync + GetHeight + GetTx {
/// Return the set of [`Capability`] supported by this backend
fn get_capabilities(&self) -> HashSet<Capability>;
- /// Fetch a transaction from the blockchain given its txid
- fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
/// Broadcast a transaction
fn broadcast(&self, tx: &Transaction) -> Result<(), Error>;
/// Estimate the fee rate required to confirm a transaction in a given `target` of blocks
fn get_height(&self) -> Result<u32, Error>;
}
+#[maybe_async]
+/// Trait for getting a transaction by txid
+pub trait GetTx {
+ /// Fetch a transaction given its txid
+ fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
+}
+
/// Trait for blockchains that can sync by updating the database directly.
#[maybe_async]
pub trait WalletSync {
maybe_await!(self.deref().get_capabilities())
}
- fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
- maybe_await!(self.deref().get_tx(txid))
- }
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
maybe_await!(self.deref().broadcast(tx))
}
}
}
+#[maybe_async]
+impl<T: GetTx> GetTx for Arc<T> {
+ fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
+ maybe_await!(self.deref().get_tx(txid))
+ }
+}
+
#[maybe_async]
impl<T: GetHeight> GetHeight for Arc<T> {
fn get_height(&self) -> Result<u32, Error> {
use crate::bitcoin::consensus::deserialize;
use crate::bitcoin::{Address, Network, OutPoint, Transaction, TxOut, Txid};
-use crate::blockchain::{
- Blockchain, Capability, ConfigurableBlockchain, GetHeight, Progress, WalletSync,
-};
+use crate::blockchain::*;
use crate::database::{BatchDatabase, DatabaseUtils};
use crate::{BlockTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails};
use bitcoincore_rpc::json::{
self.capabilities.clone()
}
- fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
- Ok(Some(self.client.get_raw_transaction(txid, None)?))
- }
-
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
Ok(self.client.send_raw_transaction(tx).map(|_| ())?)
}
}
}
+impl GetTx for RpcBlockchain {
+ fn get_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
+ Ok(Some(self.client.get_raw_transaction(txid, None)?))
+ }
+}
+
impl GetHeight for RpcBlockchain {
fn get_height(&self) -> Result<u32, Error> {
Ok(self.client.get_blockchain_info().map(|i| i.blocks as u32)?)
// 2.
// Core (#2) -> Us (#4)
- let (wallet, _, mut test_client) = init_single_sig();
+ let (wallet, blockchain, _, mut test_client) = init_single_sig();
let bdk_address = wallet.get_address(AddressIndex::New).unwrap().address;
let core_address = test_client.get_new_address(None, None).unwrap();
let tx = testutils! {
let txid_1 = test_client.receive(tx);
let tx_1: Transaction = deserialize(&test_client.get_transaction(&txid_1, None).unwrap().hex).unwrap();
let vout_1 = tx_1.output.into_iter().position(|o| o.script_pubkey == core_address.script_pubkey()).unwrap() as u32;
- wallet.sync(noop_progress(), None).unwrap();
+ wallet.sync(&blockchain, SyncOptions::default()).unwrap();
let tx_1 = wallet.list_transactions(false).unwrap().into_iter().find(|tx| tx.txid == txid_1).unwrap();
assert_eq!(tx_1.received, 50_000);
assert_eq!(tx_1.sent, 0);
};
let txid_2 = test_client.receive(tx);
- wallet.sync(noop_progress(), None).unwrap();
+ wallet.sync(&blockchain, SyncOptions::default()).unwrap();
let tx_2 = wallet.list_transactions(false).unwrap().into_iter().find(|tx| tx.txid == txid_2).unwrap();
assert_eq!(tx_2.received, 10_000);
assert_eq!(tx_2.sent, 0);
where
D: BatchDatabase,
{
-
#[deprecated = "Just use Wallet::new -- all wallets are offline now!"]
/// Create a new "offline" wallet
pub fn new_offline<E: IntoWalletDescriptor>(
use bitcoin::consensus::serialize;
use bitcoin::{OutPoint, Transaction, Txid};
-use crate::blockchain::Blockchain;
+use crate::blockchain::GetTx;
use crate::database::Database;
use crate::error::Error;
/// Depending on the [capabilities](crate::blockchain::Blockchain::get_capabilities) of the
/// [`Blockchain`] backend, the method could fail when called with old "historical" transactions or
/// with unconfirmed transactions that have been evicted from the backend's memory.
-pub fn verify_tx<D: Database, B: Blockchain>(
+pub fn verify_tx<D: Database, B: GetTx>(
tx: &Transaction,
database: &D,
blockchain: &B,
#[cfg(test)]
mod test {
- use std::collections::HashSet;
-
+ use super::*;
+ use crate::database::{BatchOperations, MemoryDatabase};
use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::hex::FromHex;
use bitcoin::{Transaction, Txid};
- use crate::blockchain::{Blockchain, Capability, Progress};
- use crate::database::{BatchDatabase, BatchOperations, MemoryDatabase};
- use crate::FeeRate;
-
- use super::*;
-
struct DummyBlockchain;
- impl Blockchain for DummyBlockchain {
- fn get_capabilities(&self) -> HashSet<Capability> {
- Default::default()
- }
- fn setup<D: BatchDatabase, P: 'static + Progress>(
- &self,
- _database: &mut D,
- _progress_update: P,
- ) -> Result<(), Error> {
- Ok(())
- }
+ impl GetTx for DummyBlockchain {
fn get_tx(&self, _txid: &Txid) -> Result<Option<Transaction>, Error> {
Ok(None)
}
- fn broadcast(&self, _tx: &Transaction) -> Result<(), Error> {
- Ok(())
- }
- fn get_height(&self) -> Result<u32, Error> {
- Ok(42)
- }
- fn estimate_fee(&self, _target: usize) -> Result<FeeRate, Error> {
- Ok(FeeRate::default_min_relay_fee())
- }
}
#[test]