bdk/src/wallet/address_validator.rs

155 lines
5.2 KiB
Rust
Raw Normal View History

// Bitcoin Dev Kit
// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
2020-08-31 11:26:36 +02:00
//
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
2020-08-31 11:26:36 +02:00
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.
2020-08-31 11:26:36 +02:00
2020-09-04 11:44:49 +02:00
//! Address validation callbacks
//!
//! The typical usage of those callbacks is for displaying the newly-generated address on a
//! hardware wallet, so that the user can cross-check its correctness.
//!
//! More generally speaking though, these callbacks can also be used to "do something" every time
//! an address is generated, without necessarily checking or validating it.
//!
//! An address validator can be attached to a [`Wallet`](super::Wallet) by using the
//! [`Wallet::add_address_validator`](super::Wallet::add_address_validator) method, and
//! whenever a new address is generated (either explicitly by the user with
//! [`Wallet::get_address`](super::Wallet::get_address) or internally to create a change
2020-09-04 11:44:49 +02:00
//! address) all the attached validators will be polled, in sequence. All of them must complete
//! successfully to continue.
//!
//! ## Example
//!
//! ```
//! # use std::sync::Arc;
//! # use bitcoin::*;
2020-09-14 14:25:38 +02:00
//! # use bdk::address_validator::*;
//! # use bdk::database::*;
//! # use bdk::*;
//! # use bdk::wallet::AddressIndex::New;
//! #[derive(Debug)]
2020-09-04 11:44:49 +02:00
//! struct PrintAddressAndContinue;
//!
//! impl AddressValidator for PrintAddressAndContinue {
//! fn validate(
//! &self,
//! keychain: KeychainKind,
//! hd_keypaths: &HdKeyPaths,
2020-09-04 11:44:49 +02:00
//! script: &Script
//! ) -> Result<(), AddressValidatorError> {
//! let address = Address::from_script(script, Network::Testnet)
2020-09-04 16:29:25 +02:00
//! .as_ref()
//! .map(Address::to_string)
//! .unwrap_or(script.to_string());
//! println!("New address of type {:?}: {}", keychain, address);
2020-09-04 11:44:49 +02:00
//! println!("HD keypaths: {:#?}", hd_keypaths);
//!
//! Ok(())
//! }
//! }
//!
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
//! let mut wallet = Wallet::new(descriptor, None, Network::Testnet, MemoryDatabase::default())?;
//! wallet.add_address_validator(Arc::new(PrintAddressAndContinue));
2020-09-04 11:44:49 +02:00
//!
//! let address = wallet.get_address(New)?;
2020-09-04 11:44:49 +02:00
//! println!("Address: {}", address);
2020-09-14 14:25:38 +02:00
//! # Ok::<(), bdk::Error>(())
2020-09-04 11:44:49 +02:00
//! ```
2020-08-31 10:49:44 +02:00
use std::fmt;
2020-08-15 23:21:13 +02:00
use bitcoin::Script;
use crate::descriptor::HdKeyPaths;
use crate::types::KeychainKind;
2020-08-15 23:21:13 +02:00
2020-09-04 11:44:49 +02:00
/// Errors that can be returned to fail the validation of an address
2020-08-15 23:21:13 +02:00
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AddressValidatorError {
2020-12-11 14:10:11 -08:00
/// User rejected the address
2020-08-15 23:21:13 +02:00
UserRejected,
2020-12-11 14:10:11 -08:00
/// Network connection error
2020-08-15 23:21:13 +02:00
ConnectionError,
2020-12-11 14:10:11 -08:00
/// Network request timeout error
2020-08-15 23:21:13 +02:00
TimeoutError,
2020-12-11 14:10:11 -08:00
/// Invalid script
2020-08-15 23:21:13 +02:00
InvalidScript,
2020-12-11 14:10:11 -08:00
/// A custom error message
2020-09-04 11:44:49 +02:00
Message(String),
2020-08-15 23:21:13 +02:00
}
2020-08-31 10:49:44 +02:00
impl fmt::Display for AddressValidatorError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for AddressValidatorError {}
2020-09-04 11:44:49 +02:00
/// Trait to build address validators
///
/// All the address validators attached to a wallet with [`Wallet::add_address_validator`](super::Wallet::add_address_validator) will be polled
/// every time an address (external or internal) is generated by the wallet. Errors returned in the
/// validator will be propagated up to the original caller that triggered the address generation.
///
/// For a usage example see [this module](crate::address_validator)'s documentation.
pub trait AddressValidator: Send + Sync + fmt::Debug {
2020-09-04 11:44:49 +02:00
/// Validate or inspect an address
2020-08-15 23:21:13 +02:00
fn validate(
&self,
keychain: KeychainKind,
hd_keypaths: &HdKeyPaths,
2020-08-15 23:21:13 +02:00
script: &Script,
) -> Result<(), AddressValidatorError>;
}
#[cfg(test)]
mod test {
use std::sync::Arc;
use super::*;
use crate::wallet::AddressIndex::New;
use crate::wallet::{get_funded_wallet, test::get_test_wpkh};
2020-08-15 23:21:13 +02:00
#[derive(Debug)]
2020-08-15 23:21:13 +02:00
struct TestValidator;
impl AddressValidator for TestValidator {
fn validate(
&self,
_keychain: KeychainKind,
_hd_keypaths: &HdKeyPaths,
2020-08-15 23:21:13 +02:00
_script: &bitcoin::Script,
) -> Result<(), AddressValidatorError> {
Err(AddressValidatorError::InvalidScript)
}
}
#[test]
#[should_panic(expected = "InvalidScript")]
fn test_address_validator_external() {
let (mut wallet, _, _) = get_funded_wallet(get_test_wpkh());
wallet.add_address_validator(Arc::new(TestValidator));
2020-08-15 23:21:13 +02:00
wallet.get_address(New).unwrap();
2020-08-15 23:21:13 +02:00
}
#[test]
#[should_panic(expected = "InvalidScript")]
fn test_address_validator_internal() {
let (mut wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
wallet.add_address_validator(Arc::new(TestValidator));
2020-08-15 23:21:13 +02:00
let addr = crate::testutils!(@external descriptors, 10);
let mut builder = wallet.build_tx();
builder.add_recipient(addr.script_pubkey(), 25_000);
builder.finish().unwrap();
2020-08-15 23:21:13 +02:00
}
}