diff --git a/src/builder.rs b/src/builder.rs index 224cc9fa7..d48b71c7c 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1029,6 +1029,7 @@ fn build_with_store_internal( Arc::clone(&tx_broadcaster), Arc::clone(&fee_estimator), Arc::clone(&payment_store), + Arc::clone(&config), Arc::clone(&logger), )); diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 53de4b8d5..cd6217688 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -5,8 +5,10 @@ // http://opensource.org/licenses/MIT>, at your option. You may not use this file except in // accordance with one or both of these licenses. +use bitcoin::address::NetworkUnchecked; use persist::KVStoreWalletPersister; +use crate::config::Config; use crate::logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger}; use crate::fee_estimator::{ConfirmationTarget, FeeEstimator}; @@ -43,11 +45,12 @@ use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, Signing}; use bitcoin::{ - Amount, FeeRate, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, WitnessProgram, - WitnessVersion, + Address, Amount, FeeRate, Network, ScriptBuf, Transaction, TxOut, Txid, WPubkeyHash, + WitnessProgram, WitnessVersion, }; use std::ops::Deref; +use std::str::FromStr; use std::sync::{Arc, Mutex}; pub(crate) enum OnchainSendAmount { @@ -71,6 +74,7 @@ where broadcaster: B, fee_estimator: E, payment_store: Arc>>, + config: Arc, logger: L, } @@ -83,11 +87,11 @@ where pub(crate) fn new( wallet: bdk_wallet::PersistedWallet, wallet_persister: KVStoreWalletPersister, broadcaster: B, fee_estimator: E, - payment_store: Arc>>, logger: L, + payment_store: Arc>>, config: Arc, logger: L, ) -> Self { let inner = Mutex::new(wallet); let persister = Mutex::new(wallet_persister); - Self { inner, persister, broadcaster, fee_estimator, payment_store, logger } + Self { inner, persister, broadcaster, fee_estimator, payment_store, config, logger } } pub(crate) fn get_full_scan_request(&self) -> FullScanRequest { @@ -327,10 +331,21 @@ where self.get_balances(total_anchor_channels_reserve_sats).map(|(_, s)| s) } + fn parse_and_validate_address( + &self, network: Network, address: &Address, + ) -> Result { + Address::::from_str(address.to_string().as_str()) + .map_err(|_| Error::InvalidAddress)? + .require_network(network) + .map_err(|_| Error::InvalidAddress) + } + pub(crate) fn send_to_address( &self, address: &bitcoin::Address, send_amount: OnchainSendAmount, fee_rate: Option, ) -> Result { + self.parse_and_validate_address(self.config.network, &address)?; + // Use the set fee_rate or default to fee estimation. let confirmation_target = ConfirmationTarget::OnchainPayment; let fee_rate = diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index f2dfa4b5e..ded88d35c 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -30,11 +30,13 @@ use lightning::util::persist::KVStore; use lightning_invoice::{Bolt11InvoiceDescription, Description}; +use bitcoin::address::NetworkUnchecked; use bitcoin::hashes::Hash; +use bitcoin::Address; use bitcoin::Amount; - use log::LevelFilter; +use std::str::FromStr; use std::sync::Arc; #[test] @@ -302,6 +304,10 @@ fn onchain_send_receive() { let addr_a = node_a.onchain_payment().new_address().unwrap(); let addr_b = node_b.onchain_payment().new_address().unwrap(); + // This is a Bitcoin Testnet address. Sending funds to this address from the Regtest network will fail + let static_address = "tb1q0d40e5rta4fty63z64gztf8c3v20cvet6v2jdh"; + let unchecked_address = Address::::from_str(static_address).unwrap(); + let addr_c = unchecked_address.assume_checked(); let premine_amount_sat = 1_100_000; premine_and_distribute_funds( @@ -366,6 +372,16 @@ fn onchain_send_receive() { node_a.onchain_payment().send_to_address(&addr_b, expected_node_a_balance + 1, None) ); + assert_eq!( + Err(NodeError::InvalidAddress), + node_a.onchain_payment().send_to_address(&addr_c, expected_node_a_balance + 1, None) + ); + + assert_eq!( + Err(NodeError::InvalidAddress), + node_a.onchain_payment().send_all_to_address(&addr_c, true, None) + ); + let amount_to_send_sats = 54321; let txid = node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).unwrap();