Skip to content

Commit 2381a78

Browse files
committed
feat: add onchain address validation for network-specific addresses on sending all to address
1 parent 2543155 commit 2381a78

File tree

4 files changed

+159
-35
lines changed

4 files changed

+159
-35
lines changed

src/error.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,6 @@ pub enum Error {
121121
LiquiditySourceUnavailable,
122122
/// The given operation failed due to the LSP's required opening fee being too high.
123123
LiquidityFeeTooHigh,
124-
/// The given address is invalid.
125-
InvalidAddressFormat,
126-
///Address belongs to the wrong network
127-
InvalidNetworkAddress {
128-
/// The expected network.
129-
expected: Network
130-
},
131124
}
132125

133126
impl fmt::Display for Error {
@@ -201,8 +194,6 @@ impl fmt::Display for Error {
201194
Self::LiquidityFeeTooHigh => {
202195
write!(f, "The given operation failed due to the LSP's required opening fee being too high.")
203196
},
204-
Self::InvalidAddressFormat => write!(f, "The given address is invalid."),
205-
Self::InvalidNetworkAddress { expected } => write!(f, "The given address is invalid. Expected network: {:?}", expected),
206197
}
207198
}
208199
}

src/main.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use bitcoin::address::{NetworkUnchecked, ParseError};
2+
use bitcoin::Address;
3+
use ldk_node::bitcoin::secp256k1::PublicKey;
4+
use ldk_node::bitcoin::Network;
5+
use ldk_node::lightning::ln::msgs::SocketAddress;
6+
use ldk_node::lightning_invoice::Bolt11Invoice;
7+
use ldk_node::Builder;
8+
use std::str::FromStr;
9+
mod error;
10+
11+
pub use error::Error as NodeError;
12+
use error::Error;
13+
14+
fn main() {
15+
let mut builder = Builder::new();
16+
builder.set_network(Network::Testnet);
17+
builder.set_chain_source_esplora("https://blockstream.info/testnet/api".to_string(), None);
18+
builder.set_gossip_source_rgs(
19+
"https://rapidsync.lightningdevkit.org/testnet/snapshot".to_string(),
20+
);
21+
22+
let node = builder.build().unwrap();
23+
24+
node.start().unwrap();
25+
26+
let funding_address = node.onchain_payment().new_address();
27+
// let funding_address = "tb1q0d40e5rta4fty63z64gztf8c3v20cvet6v2jdh"; // Testnet address
28+
println!("Funding address: {:?}", funding_address);
29+
// .. fund address ..
30+
// Fund the wallet using the printed address before proceeding
31+
println!("Please fund the wallet with at least 11000 sats (including fees).");
32+
println!("Press Enter to continue after funding...");
33+
// let mut input = String::new();
34+
// std::io::stdin().read_line(&mut input).unwrap();
35+
36+
// Wait for synchronization
37+
println!("Waiting for wallet to sync with the blockchain...");
38+
// std::thread::sleep(std::time::Duration::from_secs(600)); // Wait for 10 minutes (600 seconds) for the wallet to sync
39+
40+
// let funding_address = funding_address.unwrap();
41+
// node.onchain_payment().send_to_address(&funding_address, 10000, None).unwrap();
42+
// println!("Sent 10000 sat to funding address");
43+
44+
let balance = node.list_balances();
45+
println!("Wallet balance: {:?}", balance);
46+
47+
let config = node.config();
48+
println!("Node config: {:?}", config);
49+
50+
let node_id = node.node_id();
51+
println!("Node ID: {:?}", node_id);
52+
53+
let listnening_address = node.listening_addresses();
54+
println!("Listening address: {:?}", listnening_address);
55+
56+
//todo: delay for 10 min or more to allow real funding of a generated address
57+
58+
// let node_id = PublicKey::from_str("026fa208407265cddd6f8184803e71480c3ba1d8885d9e322dba1c57bb414d1ed1").unwrap();
59+
// let node_addr = SocketAddress::from_str("127.0.0.1:9735").unwrap();
60+
let channels = node.list_channels();
61+
for channel in channels {
62+
println!("Channel: {:?}", channel);
63+
}
64+
65+
let send_to_address = get_sendto_address().unwrap();
66+
67+
const ADDR: &str = "tb1q0d40e5rta4fty63z64gztf8c3v20cvet6v2jdh";
68+
// let val = parse_and_validate_address(node.config().network, &send_to_address.to_string()).unwrap();
69+
// println!("Parsed and validated address: {:?}", val);
70+
71+
// let validated_address =
72+
// unchecked_address.require_network(node.config().network).map_err(|_| Error::InvalidNetwork);
73+
74+
// let tx_id = node.onchain_payment().send_to_address(&send_to_address, 300, None)?;
75+
// println!("Transaction ID: {:?}", tx_id);
76+
match node.onchain_payment().send_to_address(&send_to_address, 300, None) {
77+
Ok(txid) => {
78+
println!("Successfully sent transaction: {}", txid);
79+
// Continue with success case
80+
},
81+
// Err(Error::InvalidNetworkAddress { expected, received }) => {
82+
// eprintln!("Error: Please use a {} address (this was a {} address)", expected, actual);
83+
// // Handle network mismatch case
84+
// },
85+
// Err(Error::InvalidAddressFormat(e)) => {
86+
// eprintln!("Error: Invalid address format: {}", e);
87+
// // Handle bad address case
88+
// },
89+
Err(e) => {
90+
eprintln!("Error: {}", e);
91+
node.stop().unwrap();
92+
// Handle other errors
93+
},
94+
}
95+
// node.open_channel(node_id, node_addr, 10000, None, None).unwrap();
96+
node.stop().unwrap();
97+
98+
let event = node.wait_next_event();
99+
println!("EVENT: {:?}", event);
100+
node.event_handled();
101+
102+
let invoice = Bolt11Invoice::from_str("lnbcrt1m1pn7cpr8pp57czqam62fenlx7dq9gd0zj53kymydl6kgmd8j76qeawszeagcvjqdqqcqzzsxqyz5vqsp57kzl3xlsvkx68jeegrt548tp02vrkcu7ku7x5eszwx36825kgshs9qxpqysgqlyfja29vh9m3jsu6uxrzz7htg84qygc3kgahxw2njzrmfh8htaqs0sscr86wfywq934w8sxzqwhq6hpemg7c4ufnmqz559kzr0sswfgq7cs35c").unwrap();
103+
node.bolt11_payment().send(&invoice, None).unwrap();
104+
105+
node.stop().unwrap();
106+
}
107+
108+
fn get_sendto_address() -> Result<Address, ParseError> {
109+
let static_address = "bc1qmhwps3pz9ff0ms8gvte66l59g0zx7cvx3fw0nn";
110+
// let static_address = "tb1q0d40e5rta4fty63z64gztf8c3v20cvet6v2jdh";
111+
let unchecked_address = Address::<NetworkUnchecked>::from_str(static_address)?;
112+
let send_to_address = unchecked_address.assume_checked();
113+
114+
Ok(send_to_address)
115+
}
116+
117+
fn parse_and_validate_address(network: Network, addr: &str) -> Result<Address, ParseError> {
118+
// Parse the address into an unchecked state
119+
let unchecked_address = Address::<NetworkUnchecked>::from_str(addr)?;
120+
121+
// Validate the network and convert to a checked address
122+
let validated_address = unchecked_address.require_network(network)?;
123+
124+
Ok(validated_address)
125+
126+
// let address = ADDR.parse::<Address<_>>()?.require_network(network)?;
127+
// Ok(address)
128+
}

src/payment/onchain.rs

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl OnchainPayment {
8282
return Err(Error::NotRunning);
8383
}
8484

85-
let validated_address = self.parse_and_validate_address(self.config.network, &address.to_string())?;
85+
let validated_address = self.parse_and_validate_address(self.config.network, &address)?;
8686

8787
let cur_anchor_reserve_sats =
8888
crate::total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
@@ -92,25 +92,6 @@ impl OnchainPayment {
9292
self.wallet.send_to_address(&validated_address, send_amount, fee_rate_opt)
9393
}
9494

95-
/// Validates a Bitcoin address is properly formatted and matches the expected network.
96-
///
97-
/// Returns `Ok(Address)` if valid, or:
98-
/// - `InvalidAddressFormat` if malformed
99-
/// - `InvalidNetworkAddress` if valid but wrong network
100-
///
101-
/// # Example
102-
/// ```
103-
/// let address = "tb1q..."; // Testnet address
104-
/// parse_and_validate_address(Network::Testnet, address)?;
105-
pub fn parse_and_validate_address(&self, network: Network, address: &str) -> Result<Address, Error> {
106-
Address::<NetworkUnchecked>::from_str(address)
107-
.map_err(|_| Error::InvalidAddressFormat)?
108-
.require_network(network)
109-
.map_err(|_| Error::InvalidNetworkAddress {
110-
expected: network,
111-
})
112-
}
113-
11495
/// Send an on-chain payment to the given address, draining the available funds.
11596
///
11697
/// This is useful if you have closed all channels and want to migrate funds to another
@@ -134,6 +115,8 @@ impl OnchainPayment {
134115
return Err(Error::NotRunning);
135116
}
136117

118+
let validated_address = self.parse_and_validate_address(self.config.network, &address)?;
119+
137120
let send_amount = if retain_reserves {
138121
let cur_anchor_reserve_sats =
139122
crate::total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
@@ -143,6 +126,25 @@ impl OnchainPayment {
143126
};
144127

145128
let fee_rate_opt = maybe_map_fee_rate_opt!(fee_rate);
146-
self.wallet.send_to_address(address, send_amount, fee_rate_opt)
129+
self.wallet.send_to_address(&validated_address, send_amount, fee_rate_opt)
130+
}
131+
132+
/// Validates a Bitcoin address is properly formatted and matches the expected network.
133+
///
134+
/// Returns `Ok(Address)` if valid, or:
135+
/// - `InvalidAddressFormat` if malformed
136+
/// - `InvalidNetworkAddress` if valid but wrong network
137+
///
138+
/// # Example
139+
/// ```
140+
/// let address = "tb1q..."; // Testnet address
141+
/// parse_and_validate_address(Network::Testnet, address)?;
142+
pub fn parse_and_validate_address(
143+
&self, network: Network, address: &Address,
144+
) -> Result<Address, Error> {
145+
Address::<NetworkUnchecked>::from_str(address.to_string().as_str())
146+
.map_err(|_| Error::InvalidAddress)?
147+
.require_network(network)
148+
.map_err(|_| Error::InvalidAddress)
147149
}
148150
}

tests/integration_tests_rust.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ use lightning::util::persist::KVStore;
3030

3131
use bitcoincore_rpc::RpcApi;
3232

33-
use bitcoin::hashes::Hash;
34-
use bitcoin::Amount;
3533
use bitcoin::address::NetworkUnchecked;
34+
use bitcoin::hashes::Hash;
3635
use bitcoin::Address;
36+
use bitcoin::Amount;
3737
use lightning_invoice::{Bolt11InvoiceDescription, Description};
3838
use log::LevelFilter;
3939

@@ -365,12 +365,15 @@ fn onchain_send_receive() {
365365
);
366366

367367
assert_eq!(
368-
Err(NodeError::InvalidNetworkAddress {
369-
expected: node_a.config().network,
370-
}),
368+
Err(NodeError::InvalidAddress),
371369
node_a.onchain_payment().send_to_address(&addr_c, expected_node_a_balance + 1, None)
372370
);
373371

372+
assert_eq!(
373+
Err(NodeError::InvalidAddress),
374+
node_a.onchain_payment().send_all_to_address(&addr_c, true, None)
375+
);
376+
374377
let amount_to_send_sats = 54321;
375378
let txid =
376379
node_b.onchain_payment().send_to_address(&addr_a, amount_to_send_sats, None).unwrap();

0 commit comments

Comments
 (0)