Split get_tx into its own trait
to make supporting verify_tx easier
This commit is contained in:
parent
dcd90f8b61
commit
c0e75fc1a8
@ -89,9 +89,6 @@ impl Blockchain for AnyBlockchain {
|
|||||||
maybe_await!(impl_inner_method!(self, get_capabilities))
|
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> {
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
maybe_await!(impl_inner_method!(self, broadcast, tx))
|
maybe_await!(impl_inner_method!(self, broadcast, tx))
|
||||||
}
|
}
|
||||||
@ -108,6 +105,13 @@ impl GetHeight for AnyBlockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[maybe_async]
|
||||||
impl WalletSync for AnyBlockchain {
|
impl WalletSync for AnyBlockchain {
|
||||||
fn wallet_sync<D: BatchDatabase>(
|
fn wallet_sync<D: BatchDatabase>(
|
||||||
|
@ -67,7 +67,7 @@ mod peer;
|
|||||||
mod store;
|
mod store;
|
||||||
mod sync;
|
mod sync;
|
||||||
|
|
||||||
use super::{Blockchain, Capability, ConfigurableBlockchain, GetHeight, Progress, WalletSync};
|
use crate::blockchain::*;
|
||||||
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
|
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::types::{KeychainKind, LocalUtxo, TransactionDetails};
|
use crate::types::{KeychainKind, LocalUtxo, TransactionDetails};
|
||||||
@ -225,12 +225,6 @@ impl Blockchain for CompactFiltersBlockchain {
|
|||||||
vec![Capability::FullHistory].into_iter().collect()
|
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> {
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
self.peers[0].broadcast_tx(tx.clone())?;
|
self.peers[0].broadcast_tx(tx.clone())?;
|
||||||
|
|
||||||
@ -249,6 +243,14 @@ impl GetHeight for CompactFiltersBlockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
impl WalletSync for CompactFiltersBlockchain {
|
||||||
#[allow(clippy::mutex_atomic)] // Mutex is easier to understand than a CAS loop.
|
#[allow(clippy::mutex_atomic)] // Mutex is easier to understand than a CAS loop.
|
||||||
fn wallet_setup<D: BatchDatabase>(
|
fn wallet_setup<D: BatchDatabase>(
|
||||||
|
@ -68,10 +68,6 @@ impl Blockchain for ElectrumBlockchain {
|
|||||||
.collect()
|
.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> {
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
Ok(self.client.transaction_broadcast(tx).map(|_| ())?)
|
Ok(self.client.transaction_broadcast(tx).map(|_| ())?)
|
||||||
}
|
}
|
||||||
@ -94,6 +90,12 @@ impl GetHeight for ElectrumBlockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
impl WalletSync for ElectrumBlockchain {
|
||||||
fn wallet_setup<D: BatchDatabase>(
|
fn wallet_setup<D: BatchDatabase>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -91,10 +91,6 @@ impl Blockchain for EsploraBlockchain {
|
|||||||
.collect()
|
.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> {
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
Ok(await_or_block!(self.url_client._broadcast(tx))?)
|
Ok(await_or_block!(self.url_client._broadcast(tx))?)
|
||||||
}
|
}
|
||||||
@ -112,6 +108,13 @@ impl GetHeight for EsploraBlockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[maybe_async]
|
||||||
impl WalletSync for EsploraBlockchain {
|
impl WalletSync for EsploraBlockchain {
|
||||||
fn wallet_setup<D: BatchDatabase>(
|
fn wallet_setup<D: BatchDatabase>(
|
||||||
|
@ -87,10 +87,6 @@ impl Blockchain for EsploraBlockchain {
|
|||||||
.collect()
|
.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> {
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
let _txid = self.url_client._broadcast(tx)?;
|
let _txid = self.url_client._broadcast(tx)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -108,6 +104,12 @@ impl GetHeight for EsploraBlockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
impl WalletSync for EsploraBlockchain {
|
||||||
fn wallet_setup<D: BatchDatabase>(
|
fn wallet_setup<D: BatchDatabase>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -86,11 +86,9 @@ pub enum Capability {
|
|||||||
|
|
||||||
/// Trait that defines the actions that must be supported by a blockchain backend
|
/// Trait that defines the actions that must be supported by a blockchain backend
|
||||||
#[maybe_async]
|
#[maybe_async]
|
||||||
pub trait Blockchain: WalletSync + GetHeight {
|
pub trait Blockchain: WalletSync + GetHeight + GetTx {
|
||||||
/// Return the set of [`Capability`] supported by this backend
|
/// Return the set of [`Capability`] supported by this backend
|
||||||
fn get_capabilities(&self) -> HashSet<Capability>;
|
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
|
/// Broadcast a transaction
|
||||||
fn broadcast(&self, tx: &Transaction) -> Result<(), Error>;
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error>;
|
||||||
/// Estimate the fee rate required to confirm a transaction in a given `target` of blocks
|
/// Estimate the fee rate required to confirm a transaction in a given `target` of blocks
|
||||||
@ -104,6 +102,13 @@ pub trait GetHeight {
|
|||||||
fn get_height(&self) -> Result<u32, Error>;
|
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.
|
/// Trait for blockchains that can sync by updating the database directly.
|
||||||
#[maybe_async]
|
#[maybe_async]
|
||||||
pub trait WalletSync {
|
pub trait WalletSync {
|
||||||
@ -230,9 +235,6 @@ impl<T: Blockchain> Blockchain for Arc<T> {
|
|||||||
maybe_await!(self.deref().get_capabilities())
|
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> {
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
maybe_await!(self.deref().broadcast(tx))
|
maybe_await!(self.deref().broadcast(tx))
|
||||||
}
|
}
|
||||||
@ -242,6 +244,13 @@ impl<T: Blockchain> Blockchain for Arc<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[maybe_async]
|
||||||
impl<T: GetHeight> GetHeight for Arc<T> {
|
impl<T: GetHeight> GetHeight for Arc<T> {
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
fn get_height(&self) -> Result<u32, Error> {
|
||||||
|
@ -33,9 +33,7 @@
|
|||||||
|
|
||||||
use crate::bitcoin::consensus::deserialize;
|
use crate::bitcoin::consensus::deserialize;
|
||||||
use crate::bitcoin::{Address, Network, OutPoint, Transaction, TxOut, Txid};
|
use crate::bitcoin::{Address, Network, OutPoint, Transaction, TxOut, Txid};
|
||||||
use crate::blockchain::{
|
use crate::blockchain::*;
|
||||||
Blockchain, Capability, ConfigurableBlockchain, GetHeight, Progress, WalletSync,
|
|
||||||
};
|
|
||||||
use crate::database::{BatchDatabase, DatabaseUtils};
|
use crate::database::{BatchDatabase, DatabaseUtils};
|
||||||
use crate::{BlockTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails};
|
use crate::{BlockTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails};
|
||||||
use bitcoincore_rpc::json::{
|
use bitcoincore_rpc::json::{
|
||||||
@ -141,10 +139,6 @@ impl Blockchain for RpcBlockchain {
|
|||||||
self.capabilities.clone()
|
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> {
|
fn broadcast(&self, tx: &Transaction) -> Result<(), Error> {
|
||||||
Ok(self.client.send_raw_transaction(tx).map(|_| ())?)
|
Ok(self.client.send_raw_transaction(tx).map(|_| ())?)
|
||||||
}
|
}
|
||||||
@ -161,6 +155,12 @@ impl Blockchain for RpcBlockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
impl GetHeight for RpcBlockchain {
|
||||||
fn get_height(&self) -> Result<u32, Error> {
|
fn get_height(&self) -> Result<u32, Error> {
|
||||||
Ok(self.client.get_blockchain_info().map(|i| i.blocks as u32)?)
|
Ok(self.client.get_blockchain_info().map(|i| i.blocks as u32)?)
|
||||||
|
@ -1097,7 +1097,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
// 2.
|
// 2.
|
||||||
// Core (#2) -> Us (#4)
|
// 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 bdk_address = wallet.get_address(AddressIndex::New).unwrap().address;
|
||||||
let core_address = test_client.get_new_address(None, None).unwrap();
|
let core_address = test_client.get_new_address(None, None).unwrap();
|
||||||
let tx = testutils! {
|
let tx = testutils! {
|
||||||
@ -1108,7 +1108,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let txid_1 = test_client.receive(tx);
|
let txid_1 = test_client.receive(tx);
|
||||||
let tx_1: Transaction = deserialize(&test_client.get_transaction(&txid_1, None).unwrap().hex).unwrap();
|
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;
|
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();
|
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.received, 50_000);
|
||||||
assert_eq!(tx_1.sent, 0);
|
assert_eq!(tx_1.sent, 0);
|
||||||
@ -1119,7 +1119,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
};
|
};
|
||||||
let txid_2 = test_client.receive(tx);
|
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();
|
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.received, 10_000);
|
||||||
assert_eq!(tx_2.sent, 0);
|
assert_eq!(tx_2.sent, 0);
|
||||||
|
@ -171,7 +171,6 @@ impl<D> Wallet<D>
|
|||||||
where
|
where
|
||||||
D: BatchDatabase,
|
D: BatchDatabase,
|
||||||
{
|
{
|
||||||
|
|
||||||
#[deprecated = "Just use Wallet::new -- all wallets are offline now!"]
|
#[deprecated = "Just use Wallet::new -- all wallets are offline now!"]
|
||||||
/// Create a new "offline" wallet
|
/// Create a new "offline" wallet
|
||||||
pub fn new_offline<E: IntoWalletDescriptor>(
|
pub fn new_offline<E: IntoWalletDescriptor>(
|
||||||
|
@ -17,7 +17,7 @@ use std::fmt;
|
|||||||
use bitcoin::consensus::serialize;
|
use bitcoin::consensus::serialize;
|
||||||
use bitcoin::{OutPoint, Transaction, Txid};
|
use bitcoin::{OutPoint, Transaction, Txid};
|
||||||
|
|
||||||
use crate::blockchain::Blockchain;
|
use crate::blockchain::GetTx;
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ use crate::error::Error;
|
|||||||
/// Depending on the [capabilities](crate::blockchain::Blockchain::get_capabilities) of the
|
/// Depending on the [capabilities](crate::blockchain::Blockchain::get_capabilities) of the
|
||||||
/// [`Blockchain`] backend, the method could fail when called with old "historical" transactions or
|
/// [`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.
|
/// 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,
|
tx: &Transaction,
|
||||||
database: &D,
|
database: &D,
|
||||||
blockchain: &B,
|
blockchain: &B,
|
||||||
@ -104,43 +104,18 @@ impl_error!(bitcoinconsensus::Error, Consensus, VerifyError);
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::collections::HashSet;
|
use super::*;
|
||||||
|
use crate::database::{BatchOperations, MemoryDatabase};
|
||||||
use bitcoin::consensus::encode::deserialize;
|
use bitcoin::consensus::encode::deserialize;
|
||||||
use bitcoin::hashes::hex::FromHex;
|
use bitcoin::hashes::hex::FromHex;
|
||||||
use bitcoin::{Transaction, Txid};
|
use bitcoin::{Transaction, Txid};
|
||||||
|
|
||||||
use crate::blockchain::{Blockchain, Capability, Progress};
|
|
||||||
use crate::database::{BatchDatabase, BatchOperations, MemoryDatabase};
|
|
||||||
use crate::FeeRate;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
struct DummyBlockchain;
|
struct DummyBlockchain;
|
||||||
|
|
||||||
impl Blockchain for DummyBlockchain {
|
impl GetTx 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(())
|
|
||||||
}
|
|
||||||
fn get_tx(&self, _txid: &Txid) -> Result<Option<Transaction>, Error> {
|
fn get_tx(&self, _txid: &Txid) -> Result<Option<Transaction>, Error> {
|
||||||
Ok(None)
|
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]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user