Skip to content

feat: add onchain address validation for network-specific addresses #519

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
));

Expand Down
23 changes: 19 additions & 4 deletions src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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 {
Expand All @@ -71,6 +74,7 @@ where
broadcaster: B,
fee_estimator: E,
payment_store: Arc<PaymentStore<Arc<Logger>>>,
config: Arc<Config>,
logger: L,
}

Expand All @@ -83,11 +87,11 @@ where
pub(crate) fn new(
wallet: bdk_wallet::PersistedWallet<KVStoreWalletPersister>,
wallet_persister: KVStoreWalletPersister, broadcaster: B, fee_estimator: E,
payment_store: Arc<PaymentStore<Arc<Logger>>>, logger: L,
payment_store: Arc<PaymentStore<Arc<Logger>>>, config: Arc<Config>, 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<KeychainKind> {
Expand Down Expand Up @@ -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, Error> {
Address::<NetworkUnchecked>::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<FeeRate>,
) -> Result<Txid, Error> {
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 =
Expand Down
18 changes: 17 additions & 1 deletion tests/integration_tests_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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::<NetworkUnchecked>::from_str(static_address).unwrap();
let addr_c = unchecked_address.assume_checked();

let premine_amount_sat = 1_100_000;
premine_and_distribute_funds(
Expand Down Expand Up @@ -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();
Expand Down
Loading