Skip to content

Commit 58b51a5

Browse files
committed
f: add end-to-end functional test
1 parent 657b3a9 commit 58b51a5

File tree

2 files changed

+77
-105
lines changed

2 files changed

+77
-105
lines changed

lightning/src/ln/async_payments_tests.rs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ use crate::ln::msgs::{
2323
};
2424
use crate::ln::offers_tests;
2525
use crate::ln::onion_utils::LocalHTLCFailureReason;
26-
use crate::ln::outbound_payment::PendingOutboundPayment;
27-
use crate::ln::outbound_payment::Retry;
26+
use crate::ln::outbound_payment::{PendingOutboundPayment, Retry, TEST_ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY};
2827
use crate::offers::async_receive_offer_cache::{
2928
TEST_MAX_CACHED_OFFERS_TARGET, TEST_MAX_UPDATE_ATTEMPTS,
3029
TEST_MIN_OFFER_PATHS_RELATIVE_EXPIRY_SECS, TEST_OFFER_REFRESH_THRESHOLD,
@@ -681,6 +680,79 @@ fn expired_static_invoice_fail() {
681680
// doesn't currently provide them with a reply path to do so.
682681
}
683682

683+
#[cfg_attr(feature = "std", ignore)]
684+
#[test]
685+
fn timeout_unreleased_payment() {
686+
// If a server holds a pending HTLC for too long, payment is considered expired.
687+
let chanmon_cfgs = create_chanmon_cfgs(3);
688+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
689+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
690+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
691+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
692+
create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
693+
694+
let sender = &nodes[0];
695+
let server = &nodes[1];
696+
let recipient = &nodes[2];
697+
698+
let recipient_id = vec![42; 32];
699+
let inv_server_paths = server.node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap();
700+
recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap();
701+
702+
let static_invoice =
703+
pass_static_invoice_server_messages(server, recipient, recipient_id.clone()).invoice;
704+
let offer = recipient.node.get_async_receive_offer().unwrap();
705+
706+
let amt_msat = 5000;
707+
let payment_id = PaymentId([1; 32]);
708+
let params = RouteParametersConfig::default();
709+
sender
710+
.node
711+
.pay_for_offer(&offer, None, Some(amt_msat), None, payment_id, Retry::Attempts(0), params)
712+
.unwrap();
713+
714+
let invreq_om = sender
715+
.onion_messenger
716+
.next_onion_message_for_peer(server.node.get_our_node_id())
717+
.unwrap();
718+
server.onion_messenger.handle_onion_message(sender.node.get_our_node_id(), &invreq_om);
719+
720+
let mut events = server.node.get_and_clear_pending_events();
721+
assert_eq!(events.len(), 1);
722+
let reply_path = match events.pop().unwrap() {
723+
Event::StaticInvoiceRequested { reply_path, .. } => reply_path,
724+
_ => panic!(),
725+
};
726+
727+
server.node.send_static_invoice(static_invoice.clone(), reply_path).unwrap();
728+
let static_invoice_om = server
729+
.onion_messenger
730+
.next_onion_message_for_peer(sender.node.get_our_node_id())
731+
.unwrap();
732+
733+
// We handle the static invoice to held the pending HTLC
734+
sender
735+
.onion_messenger
736+
.handle_onion_message(sender.node.get_our_node_id(), &static_invoice_om);
737+
738+
// We advance enough time to expire the payment.
739+
// We add 2 hours as is the margin added to remove stale payments in non-std implementation.
740+
let timeout_time_expiry = TEST_ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY
741+
+ Duration::from_secs(7200)
742+
+ Duration::from_secs(1);
743+
advance_time_by(timeout_time_expiry, sender);
744+
sender.node.timer_tick_occurred();
745+
let events = sender.node.get_and_clear_pending_events();
746+
assert_eq!(events.len(), 1);
747+
match events[0] {
748+
Event::PaymentFailed { payment_id: ev_payment_id, reason, .. } => {
749+
assert_eq!(reason.unwrap(), PaymentFailureReason::PaymentExpired);
750+
assert_eq!(ev_payment_id, payment_id);
751+
},
752+
_ => panic!(),
753+
}
754+
}
755+
684756
#[test]
685757
fn async_receive_mpp() {
686758
let chanmon_cfgs = create_chanmon_cfgs(4);

lightning/src/ln/outbound_payment.rs

Lines changed: 3 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ pub(crate) const IDEMPOTENCY_TIMEOUT_TICKS: u8 = 7;
5959
/// payee to fulfill.
6060
const ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY: Duration = Duration::from_secs(60 * 60 * 24 * 7);
6161

62+
#[cfg(all(async_payments, test))]
63+
pub(crate) const TEST_ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY: Duration = ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY;
64+
6265
/// Stores the session_priv for each part of a payment that is still pending. For versions 0.0.102
6366
/// and later, also stores information for retrying the payment.
6467
pub(crate) enum PendingOutboundPayment {
@@ -3361,109 +3364,6 @@ mod tests {
33613364
}, None));
33623365
}
33633366

3364-
#[test]
3365-
#[rustfmt::skip]
3366-
fn time_out_unreleased_async_payments_using_stale_absolute_time() {
3367-
let pending_events = Mutex::new(VecDeque::new());
3368-
let outbound_payments = OutboundPayments::new(new_hash_map());
3369-
let payment_id = PaymentId([0; 32]);
3370-
let absolute_expiry = 60;
3371-
3372-
let mut outbounds = outbound_payments.pending_outbound_payments.lock().unwrap();
3373-
let payment_params = PaymentParameters::from_node_id(test_utils::pubkey(42), 0)
3374-
.with_expiry_time(absolute_expiry);
3375-
let route_params = RouteParameters {
3376-
payment_params,
3377-
final_value_msat: 0,
3378-
max_total_routing_fee_msat: None,
3379-
};
3380-
let payment_hash = PaymentHash([0; 32]);
3381-
let outbound = PendingOutboundPayment::StaticInvoiceReceived {
3382-
payment_hash,
3383-
keysend_preimage: PaymentPreimage([0; 32]),
3384-
retry_strategy: Retry::Attempts(0),
3385-
route_params,
3386-
invoice_request: dummy_invoice_request(),
3387-
static_invoice: dummy_static_invoice(),
3388-
expiry_time: StaleExpiration::AbsoluteTimeout(Duration::from_secs(absolute_expiry)),
3389-
};
3390-
outbounds.insert(payment_id, outbound);
3391-
core::mem::drop(outbounds);
3392-
3393-
// The payment will not be removed if it isn't expired yet.
3394-
outbound_payments.remove_stale_payments(Duration::from_secs(absolute_expiry), &pending_events);
3395-
let outbounds = outbound_payments.pending_outbound_payments.lock().unwrap();
3396-
assert_eq!(outbounds.len(), 1);
3397-
let events = pending_events.lock().unwrap();
3398-
assert_eq!(events.len(), 0);
3399-
core::mem::drop(outbounds);
3400-
core::mem::drop(events);
3401-
3402-
outbound_payments.remove_stale_payments(Duration::from_secs(absolute_expiry + 1), &pending_events);
3403-
let outbounds = outbound_payments.pending_outbound_payments.lock().unwrap();
3404-
assert_eq!(outbounds.len(), 0);
3405-
let events = pending_events.lock().unwrap();
3406-
assert_eq!(events.len(), 1);
3407-
assert_eq!(events[0], (Event::PaymentFailed {
3408-
payment_hash: Some(payment_hash),
3409-
payment_id,
3410-
reason: Some(PaymentFailureReason::PaymentExpired),
3411-
}, None));
3412-
}
3413-
3414-
#[test]
3415-
#[rustfmt::skip]
3416-
fn time_out_unreleased_async_payments_using_stale_timer_ticks() {
3417-
let pending_events = Mutex::new(VecDeque::new());
3418-
let outbound_payments = OutboundPayments::new(new_hash_map());
3419-
let payment_id = PaymentId([0; 32]);
3420-
let absolute_expiry = 60;
3421-
3422-
let mut outbounds = outbound_payments.pending_outbound_payments.lock().unwrap();
3423-
let payment_params = PaymentParameters::from_node_id(test_utils::pubkey(42), 0)
3424-
.with_expiry_time(absolute_expiry);
3425-
let route_params = RouteParameters {
3426-
payment_params,
3427-
final_value_msat: 0,
3428-
max_total_routing_fee_msat: None,
3429-
};
3430-
let payment_hash = PaymentHash([0; 32]);
3431-
let timer_ticks = 1;
3432-
let expiration = StaleExpiration::TimerTicks(timer_ticks);
3433-
let outbound = PendingOutboundPayment::StaticInvoiceReceived {
3434-
payment_hash,
3435-
keysend_preimage: PaymentPreimage([0; 32]),
3436-
retry_strategy: Retry::Attempts(0),
3437-
route_params,
3438-
invoice_request: dummy_invoice_request(),
3439-
static_invoice: dummy_static_invoice(),
3440-
expiry_time: expiration,
3441-
};
3442-
outbounds.insert(payment_id, outbound);
3443-
core::mem::drop(outbounds);
3444-
3445-
// First time should go through
3446-
outbound_payments.remove_stale_payments(Duration::from_secs(absolute_expiry), &pending_events);
3447-
let outbounds = outbound_payments.pending_outbound_payments.lock().unwrap();
3448-
assert_eq!(outbounds.len(), 1);
3449-
let events = pending_events.lock().unwrap();
3450-
assert_eq!(events.len(), 0);
3451-
core::mem::drop(outbounds);
3452-
core::mem::drop(events);
3453-
3454-
// As timer ticks is 1, payment should be timed out
3455-
outbound_payments.remove_stale_payments(Duration::from_secs(absolute_expiry), &pending_events);
3456-
let outbounds = outbound_payments.pending_outbound_payments.lock().unwrap();
3457-
assert_eq!(outbounds.len(), 0);
3458-
let events = pending_events.lock().unwrap();
3459-
assert_eq!(events.len(), 1);
3460-
assert_eq!(events[0], (Event::PaymentFailed {
3461-
payment_hash: Some(payment_hash),
3462-
payment_id,
3463-
reason: Some(PaymentFailureReason::PaymentExpired),
3464-
}, None));
3465-
}
3466-
34673367
#[test]
34683368
#[rustfmt::skip]
34693369
fn abandon_unreleased_async_payment() {

0 commit comments

Comments
 (0)