Skip to content
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

Payment Retries #1059

Merged
merged 9 commits into from
Oct 27, 2021
20 changes: 15 additions & 5 deletions fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use lightning::ln::peer_handler::{MessageHandler,PeerManager,SocketDescriptor,Ig
use lightning::ln::msgs::DecodeError;
use lightning::ln::script::ShutdownScript;
use lightning::routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
use lightning::routing::router::{get_route, Payee};
use lightning::routing::router::{find_route, Payee, RouteParameters};
use lightning::routing::scorer::Scorer;
use lightning::util::config::UserConfig;
use lightning::util::errors::APIError;
Expand Down Expand Up @@ -437,9 +437,14 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
}
},
4 => {
let value = slice_to_be24(get_slice!(3)) as u64;
let final_value_msat = slice_to_be24(get_slice!(3)) as u64;
let payee = Payee::new(get_pubkey!());
let route = match get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, value, 42, Arc::clone(&logger), &scorer) {
let params = RouteParameters {
payee,
final_value_msat,
final_cltv_expiry_delta: 42,
};
let route = match find_route(&our_id, &params, &net_graph_msg_handler.network_graph, None, Arc::clone(&logger), &scorer) {
Ok(route) => route,
Err(_) => return,
};
Expand All @@ -455,9 +460,14 @@ pub fn do_test(data: &[u8], logger: &Arc<dyn Logger>) {
}
},
15 => {
let value = slice_to_be24(get_slice!(3)) as u64;
let final_value_msat = slice_to_be24(get_slice!(3)) as u64;
let payee = Payee::new(get_pubkey!());
let mut route = match get_route(&our_id, &payee, &net_graph_msg_handler.network_graph, None, value, 42, Arc::clone(&logger), &scorer) {
let params = RouteParameters {
payee,
final_value_msat,
final_cltv_expiry_delta: 42,
};
let mut route = match find_route(&our_id, &params, &net_graph_msg_handler.network_graph, None, Arc::clone(&logger), &scorer) {
Ok(route) => route,
Err(_) => return,
};
Expand Down
12 changes: 8 additions & 4 deletions fuzz/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use lightning::chain::transaction::OutPoint;
use lightning::ln::channelmanager::{ChannelDetails, ChannelCounterparty};
use lightning::ln::features::InitFeatures;
use lightning::ln::msgs;
use lightning::routing::router::{get_route, Payee, RouteHint, RouteHintHop};
use lightning::routing::router::{find_route, Payee, RouteHint, RouteHintHop, RouteParameters};
use lightning::routing::scorer::Scorer;
use lightning::util::logger::Logger;
use lightning::util::ser::Readable;
Expand Down Expand Up @@ -250,10 +250,14 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
}
let scorer = Scorer::new(0);
for target in node_pks.iter() {
let payee = Payee::new(*target).with_route_hints(last_hops.clone());
let _ = get_route(&our_pubkey, &payee, &net_graph,
let params = RouteParameters {
payee: Payee::new(*target).with_route_hints(last_hops.clone()),
final_value_msat: slice_to_be64(get_slice!(8)),
final_cltv_expiry_delta: slice_to_be32(get_slice!(4)),
};
let _ = find_route(&our_pubkey, &params, &net_graph,
first_hops.map(|c| c.iter().collect::<Vec<_>>()).as_ref().map(|a| a.as_slice()),
slice_to_be64(get_slice!(8)), slice_to_be32(get_slice!(4)), Arc::clone(&logger), &scorer);
Arc::clone(&logger), &scorer);
}
},
}
Expand Down
52 changes: 50 additions & 2 deletions lightning-invoice/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
//! * For parsing use `str::parse::<Invoice>(&self)` (see the docs of `impl FromStr for Invoice`)
//! * For constructing invoices use the `InvoiceBuilder`
//! * For serializing invoices use the `Display`/`ToString` traits
pub mod payment;
pub mod utils;

extern crate bech32;
extern crate bitcoin_hashes;
extern crate lightning;
#[macro_use] extern crate lightning;
extern crate num_traits;
extern crate secp256k1;

Expand Down Expand Up @@ -1187,6 +1188,19 @@ impl Invoice {
.unwrap_or(Duration::from_secs(DEFAULT_EXPIRY_TIME))
}

/// Returns whether the invoice has expired.
pub fn is_expired(&self) -> bool {
Self::is_expired_from_epoch(self.timestamp(), self.expiry_time())
}

/// Returns whether the expiry time from the given epoch has passed.
pub(crate) fn is_expired_from_epoch(epoch: &SystemTime, expiry_time: Duration) -> bool {
match epoch.elapsed() {
Ok(elapsed) => elapsed > expiry_time,
Err(_) => false,
jkczyz marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// Returns the invoice's `min_final_cltv_expiry` time, if present, otherwise
/// [`DEFAULT_MIN_FINAL_CLTV_EXPIRY`].
pub fn min_final_cltv_expiry(&self) -> u64 {
Expand Down Expand Up @@ -1219,8 +1233,13 @@ impl Invoice {
self.signed_invoice.currency()
}

/// Returns the amount if specified in the invoice as millisatoshis.
pub fn amount_milli_satoshis(&self) -> Option<u64> {
self.signed_invoice.amount_pico_btc().map(|v| v / 10)
}

/// Returns the amount if specified in the invoice as pico <currency>.
pub fn amount_pico_btc(&self) -> Option<u64> {
fn amount_pico_btc(&self) -> Option<u64> {
self.signed_invoice.amount_pico_btc()
}
}
Expand Down Expand Up @@ -1867,6 +1886,7 @@ mod test {
assert!(invoice.check_signature().is_ok());
assert_eq!(invoice.tagged_fields().count(), 10);

assert_eq!(invoice.amount_milli_satoshis(), Some(123));
assert_eq!(invoice.amount_pico_btc(), Some(1230));
assert_eq!(invoice.currency(), Currency::BitcoinTestnet);
assert_eq!(
Expand Down Expand Up @@ -1913,5 +1933,33 @@ mod test {

assert_eq!(invoice.min_final_cltv_expiry(), DEFAULT_MIN_FINAL_CLTV_EXPIRY);
assert_eq!(invoice.expiry_time(), Duration::from_secs(DEFAULT_EXPIRY_TIME));
assert!(!invoice.is_expired());
}

#[test]
fn test_expiration() {
use ::*;
use secp256k1::Secp256k1;
use secp256k1::key::SecretKey;

let timestamp = SystemTime::now()
.checked_sub(Duration::from_secs(DEFAULT_EXPIRY_TIME * 2))
.unwrap();
let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
.description("Test".into())
.payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
.payment_secret(PaymentSecret([0; 32]))
.timestamp(timestamp)
.build_raw()
.unwrap()
.sign::<_, ()>(|hash| {
let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
let secp_ctx = Secp256k1::new();
Ok(secp_ctx.sign_recoverable(hash, &privkey))
})
.unwrap();
let invoice = Invoice::from_signed(signed_invoice).unwrap();

assert!(invoice.is_expired());
}
}