Skip to content

Commit e09d9e0

Browse files
committed
Add HRN-based payments to sendpayment
1 parent d6e65fa commit e09d9e0

File tree

1 file changed

+74
-5
lines changed

1 file changed

+74
-5
lines changed

src/cli.rs

+74-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use lightning::ln::channelmanager::{
1717
use lightning::ln::msgs::SocketAddress;
1818
use lightning::ln::types::ChannelId;
1919
use lightning::offers::offer::{self, Offer};
20+
use lightning::onion_message::dns_resolution::HumanReadableName;
21+
use lightning::onion_message::messenger::Destination;
2022
use lightning::routing::gossip::NodeId;
2123
use lightning::routing::router::{PaymentParameters, RouteParameters};
2224
use lightning::sign::{EntropySource, KeysManager};
@@ -142,9 +144,10 @@ pub(crate) fn poll_for_user_input(
142144
"sendpayment" => {
143145
let invoice_str = words.next();
144146
if invoice_str.is_none() {
145-
println!("ERROR: sendpayment requires an invoice: `sendpayment <invoice>`");
147+
println!("ERROR: sendpayment requires an invoice: `sendpayment <invoice> [amount_msat]`");
146148
continue;
147149
}
150+
let invoice_str = invoice_str.unwrap();
148151

149152
let mut user_provided_amt: Option<u64> = None;
150153
if let Some(amt_msat_str) = words.next() {
@@ -157,7 +160,7 @@ pub(crate) fn poll_for_user_input(
157160
};
158161
}
159162

160-
if let Ok(offer) = Offer::from_str(invoice_str.unwrap()) {
163+
if let Ok(offer) = Offer::from_str(invoice_str) {
161164
let random_bytes = keys_manager.get_secure_random_bytes();
162165
let payment_id = PaymentId(random_bytes);
163166

@@ -213,11 +216,77 @@ pub(crate) fn poll_for_user_input(
213216
let amt = Some(amt_msat);
214217
let pay = channel_manager
215218
.pay_for_offer(&offer, None, amt, None, payment_id, retry, None);
216-
if pay.is_err() {
219+
if pay.is_ok() {
220+
println!("Payment in flight");
221+
} else {
217222
println!("ERROR: Failed to pay: {:?}", pay);
218223
}
224+
} else if let Ok(hrn) = HumanReadableName::from_encoded(invoice_str) {
225+
let random_bytes = keys_manager.get_secure_random_bytes();
226+
let payment_id = PaymentId(random_bytes);
227+
228+
if user_provided_amt.is_none() {
229+
println!("Can't pay to a human-readable-name without an amount");
230+
continue;
231+
}
232+
233+
// We need some nodes that will resolve DNS for us in order to pay a Human
234+
// Readable Name. They don't need to be trusted, but until onion message
235+
// forwarding is widespread we'll directly connect to them, revealing who
236+
// we intend to pay.
237+
let mut dns_resolvers = Vec::new();
238+
for (node_id, node) in network_graph.read_only().nodes().unordered_iter() {
239+
if let Some(info) = &node.announcement_info {
240+
// Sadly, 31 nodes currently squat on the DNS Resolver feature bit
241+
// without speaking it.
242+
// Its unclear why they're doing so, but none of them currently
243+
// also have the onion messaging feature bit set, so here we check
244+
// for both.
245+
let supports_dns = info.features().supports_dns_resolution();
246+
let supports_om = info.features().supports_onion_messages();
247+
if supports_dns && supports_om {
248+
if let Ok(pubkey) = node_id.as_pubkey() {
249+
dns_resolvers.push(Destination::Node(pubkey));
250+
}
251+
}
252+
}
253+
if dns_resolvers.len() > 5 {
254+
break;
255+
}
256+
}
257+
if dns_resolvers.is_empty() {
258+
println!(
259+
"Failed to find any DNS resolving nodes, check your network graph is synced"
260+
);
261+
continue;
262+
}
263+
264+
let amt_msat = user_provided_amt.unwrap();
265+
outbound_payments.lock().unwrap().payments.insert(
266+
payment_id,
267+
PaymentInfo {
268+
preimage: None,
269+
secret: None,
270+
status: HTLCStatus::Pending,
271+
amt_msat: MillisatAmount(Some(amt_msat)),
272+
},
273+
);
274+
fs_store
275+
.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode())
276+
.unwrap();
277+
278+
let retry = Retry::Timeout(Duration::from_secs(10));
279+
let pay = |a, b, c, d, e, f| {
280+
channel_manager.pay_for_offer_from_human_readable_name(a, b, c, d, e, f)
281+
};
282+
let pay = pay(hrn, amt_msat, payment_id, retry, None, dns_resolvers);
283+
if pay.is_ok() {
284+
println!("Payment in flight");
285+
} else {
286+
println!("ERROR: Failed to pay");
287+
}
219288
} else {
220-
match Bolt11Invoice::from_str(invoice_str.unwrap()) {
289+
match Bolt11Invoice::from_str(invoice_str) {
221290
Ok(invoice) => send_payment(
222291
&channel_manager,
223292
&invoice,
@@ -505,7 +574,7 @@ fn help() {
505574
println!(" disconnectpeer <peer_pubkey>");
506575
println!(" listpeers");
507576
println!("\n Payments:");
508-
println!(" sendpayment <invoice|offer> [<amount_msat>]");
577+
println!(" sendpayment <invoice|offer|human readable name> [<amount_msat>]");
509578
println!(" keysend <dest_pubkey> <amt_msats>");
510579
println!(" listpayments");
511580
println!("\n Invoices:");

0 commit comments

Comments
 (0)