- let event = Event::PaymentPathFailed {
- payment_id,
- payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: false,
- path: TestRouter::path_for_value(final_value_msat / 2),
- short_channel_id: None,
- retry: Some(RouteParameters {
- final_value_msat: final_value_msat / 2, ..TestRouter::retry_for_invoice(&invoice)
- }),
- };
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), false);
- assert_eq!(*payer.attempts.borrow(), 3);
-
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), true);
- assert_eq!(*payer.attempts.borrow(), 3);
- }
-
- #[cfg(feature = "std")]
- #[test]
- fn fails_paying_invoice_after_max_retry_timeout() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new()
- .expect_send(Amount::ForInvoice(final_value_msat))
- .expect_send(Amount::OnRetry(final_value_msat / 2));
-
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- type InvoicePayerUsingSinceEpoch <P, R, L, E> = InvoicePayerUsingTime::<P, R, L, E, SinceEpoch>;
-
- let invoice_payer =
- InvoicePayerUsingSinceEpoch::new(&payer, router, &logger, event_handler, Retry::Timeout(Duration::from_secs(120)));
-
- let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
- assert_eq!(*payer.attempts.borrow(), 1);
-
- let event = Event::PaymentPathFailed {
- payment_id,
- payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: true,
- path: TestRouter::path_for_value(final_value_msat),
- short_channel_id: None,
- retry: Some(TestRouter::retry_for_invoice(&invoice)),
- };
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), false);
- assert_eq!(*payer.attempts.borrow(), 2);
-
- SinceEpoch::advance(Duration::from_secs(121));
-
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), true);
- assert_eq!(*payer.attempts.borrow(), 2);
- }
-
- #[test]
- fn fails_paying_invoice_with_missing_retry_params() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
-
- let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
- assert_eq!(*payer.attempts.borrow(), 1);
-
- let event = Event::PaymentPathFailed {
- payment_id,
- payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: false,
- path: vec![],
- short_channel_id: None,
- retry: None,
- };
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), true);
- assert_eq!(*payer.attempts.borrow(), 1);
- }
-
- // Expiration is checked only in an std environment
- #[cfg(feature = "std")]
- #[test]
- fn fails_paying_invoice_after_expiration() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payer = TestPayer::new();
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = expired_invoice(payment_preimage);
- if let PaymentError::Invoice(msg) = invoice_payer.pay_invoice(&invoice).unwrap_err() {
- assert_eq!(msg, "Invoice expired prior to send");
- } else { panic!("Expected Invoice Error"); }
- }
-
- // Expiration is checked only in an std environment
- #[cfg(feature = "std")]
- #[test]
- fn fails_retrying_invoice_after_expiration() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
-
- let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
- assert_eq!(*payer.attempts.borrow(), 1);
-
- let mut retry_data = TestRouter::retry_for_invoice(&invoice);
- retry_data.payment_params.expiry_time = Some(SystemTime::now()
- .checked_sub(Duration::from_secs(2)).unwrap()
- .duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs());
- let event = Event::PaymentPathFailed {
- payment_id,
- payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: false,
- path: vec![],
- short_channel_id: None,
- retry: Some(retry_data),
- };
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), true);
- assert_eq!(*payer.attempts.borrow(), 1);
- }
-
- #[test]
- fn fails_paying_invoice_after_retry_error() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new()
- .fails_on_attempt(2)
- .expect_send(Amount::ForInvoice(final_value_msat))
- .expect_send(Amount::OnRetry(final_value_msat / 2));
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
-
- let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
- assert_eq!(*payer.attempts.borrow(), 1);
-
- let event = Event::PaymentPathFailed {
- payment_id,
- payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: false,
- path: TestRouter::path_for_value(final_value_msat / 2),
- short_channel_id: None,
- retry: Some(TestRouter::retry_for_invoice(&invoice)),
- };
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), true);
- assert_eq!(*payer.attempts.borrow(), 2);
- }
-
- #[test]
- fn fails_paying_invoice_after_rejected_by_payee() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
-
- let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
- assert_eq!(*payer.attempts.borrow(), 1);
-
- let event = Event::PaymentPathFailed {
- payment_id,
- payment_hash: PaymentHash(invoice.payment_hash().clone().into_inner()),
- network_update: None,
- payment_failed_permanently: true,
- all_paths_failed: false,
- path: vec![],
- short_channel_id: None,
- retry: Some(TestRouter::retry_for_invoice(&invoice)),
- };
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), true);
- assert_eq!(*payer.attempts.borrow(), 1);
- }
-
- #[test]
- fn fails_repaying_invoice_with_pending_payment() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new()
- .expect_send(Amount::ForInvoice(final_value_msat))
- .expect_send(Amount::ForInvoice(final_value_msat));
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
-
- let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
-
- // Cannot repay an invoice pending payment.
- match invoice_payer.pay_invoice(&invoice) {
- Err(PaymentError::Invoice("payment pending")) => {},
- Err(_) => panic!("unexpected error"),
- Ok(_) => panic!("expected invoice error"),
- }
-
- // Can repay an invoice once cleared from cache.
- let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
- invoice_payer.remove_cached_payment(&payment_hash);
- assert!(invoice_payer.pay_invoice(&invoice).is_ok());
-
- // Cannot retry paying an invoice if cleared from cache.
- invoice_payer.remove_cached_payment(&payment_hash);
- let event = Event::PaymentPathFailed {
- payment_id,
- payment_hash,
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: false,
- path: vec![],
- short_channel_id: None,
- retry: Some(TestRouter::retry_for_invoice(&invoice)),
- };
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), true);
- }
-
- #[test]
- fn fails_paying_invoice_with_routing_errors() {
- let payer = TestPayer::new();
- let router = FailingRouter {};
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, |_: &_| {}, Retry::Attempts(0));
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- match invoice_payer.pay_invoice(&invoice) {
- Err(PaymentError::Routing(_)) => {},
- Err(_) => panic!("unexpected error"),
- Ok(_) => panic!("expected routing error"),
- }
- }
-
- #[test]
- fn fails_paying_invoice_with_sending_errors() {
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new()
- .fails_on_attempt(1)
- .expect_send(Amount::ForInvoice(final_value_msat));
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, |_: &_| {}, Retry::Attempts(0));
-
- match invoice_payer.pay_invoice(&invoice) {
- Err(PaymentError::Sending(_)) => {},
- Err(_) => panic!("unexpected error"),
- Ok(_) => panic!("expected sending error"),
- }
- }
-
- #[test]
- fn pays_zero_value_invoice_using_amount() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = zero_value_invoice(payment_preimage);
- let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
- let final_value_msat = 100;
-
- let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
-
- let payment_id =
- Some(invoice_payer.pay_zero_value_invoice(&invoice, final_value_msat).unwrap());
- assert_eq!(*payer.attempts.borrow(), 1);
-
- invoice_payer.handle_event(&Event::PaymentSent {
- payment_id, payment_preimage, payment_hash, fee_paid_msat: None
- });
- assert_eq!(*event_handled.borrow(), true);
- assert_eq!(*payer.attempts.borrow(), 1);
- }
-
- #[test]
- fn fails_paying_zero_value_invoice_with_amount() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payer = TestPayer::new();
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
-
- // Cannot repay an invoice pending payment.
- match invoice_payer.pay_zero_value_invoice(&invoice, 100) {
- Err(PaymentError::Invoice("amount unexpected")) => {},
- Err(_) => panic!("unexpected error"),
- Ok(_) => panic!("expected invoice error"),
- }
- }
-
- #[test]
- fn pays_pubkey_with_amount() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let pubkey = pubkey();
- let payment_preimage = PaymentPreimage([1; 32]);
- let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
- let final_value_msat = 100;
- let final_cltv_expiry_delta = 42;
-
- let payer = TestPayer::new()
- .expect_send(Amount::Spontaneous(final_value_msat))
- .expect_send(Amount::OnRetry(final_value_msat));
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
-
- let payment_id = Some(invoice_payer.pay_pubkey(
- pubkey, payment_preimage, final_value_msat, final_cltv_expiry_delta
- ).unwrap());
- assert_eq!(*payer.attempts.borrow(), 1);
-
- let retry = RouteParameters {
- payment_params: PaymentParameters::for_keysend(pubkey),
- final_value_msat,
- final_cltv_expiry_delta,
- };
- let event = Event::PaymentPathFailed {
- payment_id,
- payment_hash,
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: false,
- path: vec![],
- short_channel_id: None,
- retry: Some(retry),
- };
- invoice_payer.handle_event(&event);
- assert_eq!(*event_handled.borrow(), false);
- assert_eq!(*payer.attempts.borrow(), 2);
-
- invoice_payer.handle_event(&Event::PaymentSent {
- payment_id, payment_preimage, payment_hash, fee_paid_msat: None
- });
- assert_eq!(*event_handled.borrow(), true);
- assert_eq!(*payer.attempts.borrow(), 2);
- }
-
- #[test]
- fn scores_failed_channel() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
- let path = TestRouter::path_for_value(final_value_msat);
- let short_channel_id = Some(path[0].short_channel_id);
-
- // Expect that scorer is given short_channel_id upon handling the event.
- let payer = TestPayer::new()
- .expect_send(Amount::ForInvoice(final_value_msat))
- .expect_send(Amount::OnRetry(final_value_msat / 2));
- let scorer = TestScorer::new().expect(TestResult::PaymentFailure {
- path: path.clone(), short_channel_id: path[0].short_channel_id,
- });
- let router = TestRouter::new(scorer);
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
-
- let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
- let event = Event::PaymentPathFailed {
- payment_id,
- payment_hash,
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: false,
- path,
- short_channel_id,
- retry: Some(TestRouter::retry_for_invoice(&invoice)),
- };
- invoice_payer.handle_event(&event);
- }
-
- #[test]
- fn scores_successful_channels() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let payment_hash = Some(PaymentHash(invoice.payment_hash().clone().into_inner()));
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
- let route = TestRouter::route_for_value(final_value_msat);
-
- // Expect that scorer is given short_channel_id upon handling the event.
- let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
- let scorer = TestScorer::new()
- .expect(TestResult::PaymentSuccess { path: route.paths[0].clone() })
- .expect(TestResult::PaymentSuccess { path: route.paths[1].clone() });
- let router = TestRouter::new(scorer);
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
-
- let payment_id = invoice_payer.pay_invoice(&invoice).unwrap();
- let event = Event::PaymentPathSuccessful {
- payment_id, payment_hash, path: route.paths[0].clone()
- };
- invoice_payer.handle_event(&event);
- let event = Event::PaymentPathSuccessful {
- payment_id, payment_hash, path: route.paths[1].clone()
- };
- invoice_payer.handle_event(&event);
- }
-
- #[test]
- fn generates_correct_inflight_map_data() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice = invoice(payment_preimage);
- let payment_hash = Some(PaymentHash(invoice.payment_hash().clone().into_inner()));
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
- let final_value_msat = invoice.amount_milli_satoshis().unwrap();
- let route = TestRouter::route_for_value(final_value_msat);
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
-
- let payment_id = invoice_payer.pay_invoice(&invoice).unwrap();
-
- let inflight_map = invoice_payer.create_inflight_map();
- // First path check
- assert_eq!(inflight_map.0.get(&(0, false)).unwrap().clone(), 94);
- assert_eq!(inflight_map.0.get(&(1, true)).unwrap().clone(), 84);
- assert_eq!(inflight_map.0.get(&(2, false)).unwrap().clone(), 64);
-
- // Second path check
- assert_eq!(inflight_map.0.get(&(3, false)).unwrap().clone(), 74);
- assert_eq!(inflight_map.0.get(&(4, false)).unwrap().clone(), 64);
-
- invoice_payer.handle_event(&Event::PaymentPathSuccessful {
- payment_id, payment_hash, path: route.paths[0].clone()
- });
-
- let inflight_map = invoice_payer.create_inflight_map();
-
- assert_eq!(inflight_map.0.get(&(0, false)), None);
- assert_eq!(inflight_map.0.get(&(1, true)), None);
- assert_eq!(inflight_map.0.get(&(2, false)), None);
-
- // Second path should still be inflight
- assert_eq!(inflight_map.0.get(&(3, false)).unwrap().clone(), 74);
- assert_eq!(inflight_map.0.get(&(4, false)).unwrap().clone(), 64)
- }
-
- #[test]
- fn considers_inflight_htlcs_between_invoice_payments_when_path_succeeds() {
- // First, let's just send a payment through, but only make sure one of the path completes
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let payment_invoice = invoice(payment_preimage);
- let payment_hash = Some(PaymentHash(payment_invoice.payment_hash().clone().into_inner()));
- let final_value_msat = payment_invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new()
- .expect_send(Amount::ForInvoice(final_value_msat))
- .expect_send(Amount::ForInvoice(final_value_msat));
- let final_value_msat = payment_invoice.amount_milli_satoshis().unwrap();
- let route = TestRouter::route_for_value(final_value_msat);
- let scorer = TestScorer::new()
- // 1st invoice, 1st path
- .expect_usage(ChannelUsage { amount_msat: 64, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 84, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 94, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- // 1st invoice, 2nd path
- .expect_usage(ChannelUsage { amount_msat: 64, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 74, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- // 2nd invoice, 1st path
- .expect_usage(ChannelUsage { amount_msat: 64, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 84, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 94, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- // 2nd invoice, 2nd path
- .expect_usage(ChannelUsage { amount_msat: 64, inflight_htlc_msat: 64, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 74, inflight_htlc_msat: 74, effective_capacity: EffectiveCapacity::Unknown } );
- let router = TestRouter::new(scorer);
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
-
- // Succeed 1st path, leave 2nd path inflight
- let payment_id = invoice_payer.pay_invoice(&payment_invoice).unwrap();
- invoice_payer.handle_event(&Event::PaymentPathSuccessful {
- payment_id, payment_hash, path: route.paths[0].clone()
- });
-
- // Let's pay a second invoice that will be using the same path. This should trigger the
- // assertions that expect the last 4 ChannelUsage values above where TestScorer is initialized.
- // Particularly, the 2nd path of the 1st payment, since it is not yet complete, should still
- // have 64 msats inflight for paths considering the channel with scid of 1.
- let payment_preimage_2 = PaymentPreimage([2; 32]);
- let payment_invoice_2 = invoice(payment_preimage_2);
- invoice_payer.pay_invoice(&payment_invoice_2).unwrap();
- }
-
- #[test]
- fn considers_inflight_htlcs_between_retries() {
- // First, let's just send a payment through, but only make sure one of the path completes
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let payment_invoice = invoice(payment_preimage);
- let payment_hash = PaymentHash(payment_invoice.payment_hash().clone().into_inner());
- let final_value_msat = payment_invoice.amount_milli_satoshis().unwrap();
-
- let payer = TestPayer::new()
- .expect_send(Amount::ForInvoice(final_value_msat))
- .expect_send(Amount::OnRetry(final_value_msat / 2))
- .expect_send(Amount::OnRetry(final_value_msat / 4));
- let final_value_msat = payment_invoice.amount_milli_satoshis().unwrap();
- let scorer = TestScorer::new()
- // 1st invoice, 1st path
- .expect_usage(ChannelUsage { amount_msat: 64, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 84, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 94, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- // 1st invoice, 2nd path
- .expect_usage(ChannelUsage { amount_msat: 64, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 74, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- // Retry 1, 1st path
- .expect_usage(ChannelUsage { amount_msat: 32, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 52, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 62, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- // Retry 1, 2nd path
- .expect_usage(ChannelUsage { amount_msat: 32, inflight_htlc_msat: 64, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 42, inflight_htlc_msat: 64 + 10, effective_capacity: EffectiveCapacity::Unknown } )
- // Retry 2, 1st path
- .expect_usage(ChannelUsage { amount_msat: 16, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 36, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 46, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
- // Retry 2, 2nd path
- .expect_usage(ChannelUsage { amount_msat: 16, inflight_htlc_msat: 64 + 32, effective_capacity: EffectiveCapacity::Unknown } )
- .expect_usage(ChannelUsage { amount_msat: 26, inflight_htlc_msat: 74 + 32 + 10, effective_capacity: EffectiveCapacity::Unknown } );
- let router = TestRouter::new(scorer);
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
-
- // Fail 1st path, leave 2nd path inflight
- let payment_id = Some(invoice_payer.pay_invoice(&payment_invoice).unwrap());
- invoice_payer.handle_event(&Event::PaymentPathFailed {
- payment_id,
- payment_hash,
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: false,
- path: TestRouter::path_for_value(final_value_msat),
- short_channel_id: None,
- retry: Some(TestRouter::retry_for_invoice(&payment_invoice)),
- });
-
- // Fails again the 1st path of our retry
- invoice_payer.handle_event(&Event::PaymentPathFailed {
- payment_id,
- payment_hash,
- network_update: None,
- payment_failed_permanently: false,
- all_paths_failed: false,
- path: TestRouter::path_for_value(final_value_msat / 2),
- short_channel_id: None,
- retry: Some(RouteParameters {
- final_value_msat: final_value_msat / 4,
- ..TestRouter::retry_for_invoice(&payment_invoice)
- }),
- });
- }
-
- #[test]
- fn accounts_for_some_inflight_htlcs_sent_during_partial_failure() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice_to_pay = invoice(payment_preimage);
- let final_value_msat = invoice_to_pay.amount_milli_satoshis().unwrap();
-
- let retry = TestRouter::retry_for_invoice(&invoice_to_pay);
- let payer = TestPayer::new()
- .fails_with_partial_failure(
- retry.clone(), OnAttempt(1),
- Some(vec![
- Err(ChannelUnavailable { err: "abc".to_string() }), Err(MonitorUpdateInProgress)
- ]))
- .expect_send(Amount::ForInvoice(final_value_msat));
-
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
-
- invoice_payer.pay_invoice(&invoice_to_pay).unwrap();
- let inflight_map = invoice_payer.create_inflight_map();
-
- // Only the second path, which failed with `MonitorUpdateInProgress` should be added to our
- // inflight map because retries are disabled.
- assert_eq!(inflight_map.0.len(), 2);
- }
-
- #[test]
- fn accounts_for_all_inflight_htlcs_sent_during_partial_failure() {
- let event_handled = core::cell::RefCell::new(false);
- let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
-
- let payment_preimage = PaymentPreimage([1; 32]);
- let invoice_to_pay = invoice(payment_preimage);
- let final_value_msat = invoice_to_pay.amount_milli_satoshis().unwrap();
-
- let retry = TestRouter::retry_for_invoice(&invoice_to_pay);
- let payer = TestPayer::new()
- .fails_with_partial_failure(
- retry.clone(), OnAttempt(1),
- Some(vec![
- Ok(()), Err(MonitorUpdateInProgress)
- ]))
- .expect_send(Amount::ForInvoice(final_value_msat));
-
- let router = TestRouter::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
-
- invoice_payer.pay_invoice(&invoice_to_pay).unwrap();
- let inflight_map = invoice_payer.create_inflight_map();
-
- // All paths successful, hence we check of the existence of all 5 hops.
- assert_eq!(inflight_map.0.len(), 5);
- }
-
- struct TestRouter {
- scorer: RefCell<TestScorer>,
- }
-
- impl TestRouter {
- fn new(scorer: TestScorer) -> Self {
- TestRouter { scorer: RefCell::new(scorer) }
- }
-
- fn route_for_value(final_value_msat: u64) -> Route {
- Route {
- paths: vec![
- vec![
- RouteHop {
- pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
- channel_features: ChannelFeatures::empty(),
- node_features: NodeFeatures::empty(),
- short_channel_id: 0,
- fee_msat: 10,
- cltv_expiry_delta: 0
- },
- RouteHop {
- pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
- channel_features: ChannelFeatures::empty(),
- node_features: NodeFeatures::empty(),
- short_channel_id: 1,
- fee_msat: 20,
- cltv_expiry_delta: 0
- },
- RouteHop {
- pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
- channel_features: ChannelFeatures::empty(),
- node_features: NodeFeatures::empty(),
- short_channel_id: 2,
- fee_msat: final_value_msat / 2,
- cltv_expiry_delta: 0
- },
- ],
- vec![
- RouteHop {
- pubkey: PublicKey::from_slice(&hex::decode("029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255").unwrap()[..]).unwrap(),
- channel_features: ChannelFeatures::empty(),
- node_features: NodeFeatures::empty(),
- short_channel_id: 3,
- fee_msat: 10,
- cltv_expiry_delta: 144
- },
- RouteHop {
- pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
- channel_features: ChannelFeatures::empty(),
- node_features: NodeFeatures::empty(),
- short_channel_id: 4,
- fee_msat: final_value_msat / 2,
- cltv_expiry_delta: 144
- }
- ],
- ],
- payment_params: None,
- }
- }
-
- fn path_for_value(final_value_msat: u64) -> Vec<RouteHop> {
- TestRouter::route_for_value(final_value_msat).paths[0].clone()
- }
-
- fn retry_for_invoice(invoice: &Invoice) -> RouteParameters {
- let mut payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key())
- .with_expiry_time(expiry_time_from_unix_epoch(invoice).as_secs())
- .with_route_hints(invoice.route_hints());
- if let Some(features) = invoice.features() {
- payment_params = payment_params.with_features(features.clone());
- }
- let final_value_msat = invoice.amount_milli_satoshis().unwrap() / 2;
- RouteParameters {
- payment_params,
- final_value_msat,
- final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
- }
- }
- }
-
- impl Router for TestRouter {
- fn find_route(
- &self, payer: &PublicKey, route_params: &RouteParameters, _payment_hash: &PaymentHash,
- _first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs
- ) -> Result<Route, LightningError> {
- // Simulate calling the Scorer just as you would in find_route
- let route = Self::route_for_value(route_params.final_value_msat);
- let mut locked_scorer = self.scorer.lock();
- let scorer = ScorerAccountingForInFlightHtlcs::new(locked_scorer.deref_mut(), inflight_htlcs);
- for path in route.paths {
- let mut aggregate_msat = 0u64;
- for (idx, hop) in path.iter().rev().enumerate() {
- aggregate_msat += hop.fee_msat;
- let usage = ChannelUsage {
- amount_msat: aggregate_msat,
- inflight_htlc_msat: 0,
- effective_capacity: EffectiveCapacity::Unknown,
- };
-
- // Since the path is reversed, the last element in our iteration is the first
- // hop.
- if idx == path.len() - 1 {
- scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(payer), &NodeId::from_pubkey(&hop.pubkey), usage);
- } else {
- scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(&path[idx + 1].pubkey), &NodeId::from_pubkey(&hop.pubkey), usage);
- }
- }
- }
-
- Ok(Route {
- payment_params: Some(route_params.payment_params.clone()), ..Self::route_for_value(route_params.final_value_msat)
- })
- }
-
- fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) {
- self.scorer.lock().payment_path_failed(path, short_channel_id);
- }
-
- fn notify_payment_path_successful(&self, path: &[&RouteHop]) {
- self.scorer.lock().payment_path_successful(path);
- }
-
- fn notify_payment_probe_successful(&self, path: &[&RouteHop]) {
- self.scorer.lock().probe_successful(path);
- }
-
- fn notify_payment_probe_failed(&self, path: &[&RouteHop], short_channel_id: u64) {
- self.scorer.lock().probe_failed(path, short_channel_id);
- }
- }
-
- struct FailingRouter;
-
- impl Router for FailingRouter {
- fn find_route(
- &self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash,
- _first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs
- ) -> Result<Route, LightningError> {
- Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError })
- }
-
- fn notify_payment_path_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {}
-
- fn notify_payment_path_successful(&self, _path: &[&RouteHop]) {}
-
- fn notify_payment_probe_successful(&self, _path: &[&RouteHop]) {}
-
- fn notify_payment_probe_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {}
- }
-
- struct TestScorer {
- event_expectations: Option<VecDeque<TestResult>>,
- scorer_expectations: RefCell<Option<VecDeque<ChannelUsage>>>,
- }
-
- #[derive(Debug)]
- enum TestResult {
- PaymentFailure { path: Vec<RouteHop>, short_channel_id: u64 },
- PaymentSuccess { path: Vec<RouteHop> },
- }
-
- impl TestScorer {
- fn new() -> Self {
- Self {
- event_expectations: None,
- scorer_expectations: RefCell::new(None),
- }
- }
-
- fn expect(mut self, expectation: TestResult) -> Self {
- self.event_expectations.get_or_insert_with(|| VecDeque::new()).push_back(expectation);
- self
- }
-
- fn expect_usage(self, expectation: ChannelUsage) -> Self {
- self.scorer_expectations.borrow_mut().get_or_insert_with(|| VecDeque::new()).push_back(expectation);
- self
- }
- }
-
- #[cfg(c_bindings)]
- impl lightning::util::ser::Writeable for TestScorer {
- fn write<W: lightning::util::ser::Writer>(&self, _: &mut W) -> Result<(), lightning::io::Error> { unreachable!(); }
- }
-
- impl Score for TestScorer {
- fn channel_penalty_msat(
- &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, usage: ChannelUsage
- ) -> u64 {
- if let Some(scorer_expectations) = self.scorer_expectations.borrow_mut().as_mut() {
- match scorer_expectations.pop_front() {
- Some(expectation) => {
- assert_eq!(expectation.amount_msat, usage.amount_msat);
- assert_eq!(expectation.inflight_htlc_msat, usage.inflight_htlc_msat);
- },
- None => {},
- }
- }
- 0
- }
-
- fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {
- if let Some(expectations) = &mut self.event_expectations {
- match expectations.pop_front() {
- Some(TestResult::PaymentFailure { path, short_channel_id }) => {
- assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
- assert_eq!(actual_short_channel_id, short_channel_id);
- },
- Some(TestResult::PaymentSuccess { path }) => {
- panic!("Unexpected successful payment path: {:?}", path)
- },
- None => panic!("Unexpected notify_payment_path_failed call: {:?}", actual_path),
- }
- }
- }
-
- fn payment_path_successful(&mut self, actual_path: &[&RouteHop]) {
- if let Some(expectations) = &mut self.event_expectations {
- match expectations.pop_front() {
- Some(TestResult::PaymentFailure { path, .. }) => {
- panic!("Unexpected payment path failure: {:?}", path)
- },
- Some(TestResult::PaymentSuccess { path }) => {
- assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
- },
- None => panic!("Unexpected notify_payment_path_successful call: {:?}", actual_path),
- }
- }
- }
-
- fn probe_failed(&mut self, actual_path: &[&RouteHop], _: u64) {
- if let Some(expectations) = &mut self.event_expectations {
- match expectations.pop_front() {
- Some(TestResult::PaymentFailure { path, .. }) => {
- panic!("Unexpected failed payment path: {:?}", path)
- },
- Some(TestResult::PaymentSuccess { path }) => {
- panic!("Unexpected successful payment path: {:?}", path)
- },
- None => panic!("Unexpected notify_payment_path_failed call: {:?}", actual_path),
- }
- }
- }
- fn probe_successful(&mut self, actual_path: &[&RouteHop]) {
- if let Some(expectations) = &mut self.event_expectations {
- match expectations.pop_front() {
- Some(TestResult::PaymentFailure { path, .. }) => {
- panic!("Unexpected payment path failure: {:?}", path)
- },
- Some(TestResult::PaymentSuccess { path }) => {
- panic!("Unexpected successful payment path: {:?}", path)
- },
- None => panic!("Unexpected notify_payment_path_successful call: {:?}", actual_path),
- }
- }
- }
- }
-
- impl Drop for TestScorer {
- fn drop(&mut self) {
- if std::thread::panicking() {
- return;
- }
-
- if let Some(event_expectations) = &self.event_expectations {
- if !event_expectations.is_empty() {
- panic!("Unsatisfied event expectations: {:?}", event_expectations);
- }
- }
-
- if let Some(scorer_expectations) = self.scorer_expectations.borrow().as_ref() {
- if !scorer_expectations.is_empty() {
- panic!("Unsatisfied scorer expectations: {:?}", scorer_expectations)
- }
- }
- }
- }
-
- struct TestPayer {
- expectations: core::cell::RefCell<VecDeque<Amount>>,
- attempts: core::cell::RefCell<usize>,
- failing_on_attempt: core::cell::RefCell<HashMap<usize, PaymentSendFailure>>,
- }
-
- #[derive(Clone, Debug, PartialEq, Eq)]
- enum Amount {
- ForInvoice(u64),
- Spontaneous(u64),
- OnRetry(u64),
- }
-
- struct OnAttempt(usize);
-
- impl TestPayer {
- fn new() -> Self {
- Self {
- expectations: core::cell::RefCell::new(VecDeque::new()),
- attempts: core::cell::RefCell::new(0),
- failing_on_attempt: core::cell::RefCell::new(HashMap::new()),
- }
- }
-
- fn expect_send(self, value_msat: Amount) -> Self {
- self.expectations.borrow_mut().push_back(value_msat);
- self
- }
-
- fn fails_on_attempt(self, attempt: usize) -> Self {
- let failure = PaymentSendFailure::ParameterError(APIError::MonitorUpdateInProgress);
- self.fails_with(failure, OnAttempt(attempt))
- }
-
- fn fails_with_partial_failure(self, retry: RouteParameters, attempt: OnAttempt, results: Option<Vec<Result<(), APIError>>>) -> Self {
- self.fails_with(PaymentSendFailure::PartialFailure {
- results: results.unwrap_or(vec![]),
- failed_paths_retry: Some(retry),
- payment_id: PaymentId([1; 32]),
- }, attempt)
- }
-
- fn fails_with(self, failure: PaymentSendFailure, attempt: OnAttempt) -> Self {
- self.failing_on_attempt.borrow_mut().insert(attempt.0, failure);
- self
- }
-
- fn check_attempts(&self) -> Result<(), PaymentSendFailure> {
- let mut attempts = self.attempts.borrow_mut();
- *attempts += 1;
-
- match self.failing_on_attempt.borrow_mut().remove(&*attempts) {
- Some(failure) => Err(failure),
- None => Ok(())
- }
- }
-
- fn check_value_msats(&self, actual_value_msats: Amount) {
- let expected_value_msats = self.expectations.borrow_mut().pop_front();
- if let Some(expected_value_msats) = expected_value_msats {
- assert_eq!(actual_value_msats, expected_value_msats);
- } else {
- panic!("Unexpected amount: {:?}", actual_value_msats);
- }
- }
- }
-
- impl Drop for TestPayer {
- fn drop(&mut self) {
- if std::thread::panicking() {
- return;
- }
-
- if !self.expectations.borrow().is_empty() {
- panic!("Unsatisfied payment expectations: {:?}", self.expectations.borrow());
- }
- }
- }
-
- impl Payer for TestPayer {
- fn node_id(&self) -> PublicKey {
- let secp_ctx = Secp256k1::new();
- PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap())
- }
-
- fn first_hops(&self) -> Vec<ChannelDetails> {
- Vec::new()
- }
-
- fn send_payment(
- &self, route: &Route, _payment_hash: PaymentHash,
- _payment_secret: &Option<PaymentSecret>, _payment_id: PaymentId,
- ) -> Result<(), PaymentSendFailure> {
- self.check_value_msats(Amount::ForInvoice(route.get_total_amount()));
- self.check_attempts()
- }
-
- fn send_spontaneous_payment(
- &self, route: &Route, _payment_preimage: PaymentPreimage, _payment_id: PaymentId,
- ) -> Result<(), PaymentSendFailure> {
- self.check_value_msats(Amount::Spontaneous(route.get_total_amount()));
- self.check_attempts()
- }
-
- fn retry_payment(
- &self, route: &Route, _payment_id: PaymentId
- ) -> Result<(), PaymentSendFailure> {
- self.check_value_msats(Amount::OnRetry(route.get_total_amount()));
- self.check_attempts()
- }
-
- fn abandon_payment(&self, _payment_id: PaymentId) { }
- }
-
- // *** Full Featured Functional Tests with a Real ChannelManager ***
- struct ManualRouter(RefCell<VecDeque<Result<Route, LightningError>>>);
-
- impl Router for ManualRouter {
- fn find_route(
- &self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash,
- _first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs
- ) -> Result<Route, LightningError> {
- self.0.borrow_mut().pop_front().unwrap()
- }
-
- fn notify_payment_path_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {}
-
- fn notify_payment_path_successful(&self, _path: &[&RouteHop]) {}
-
- fn notify_payment_probe_successful(&self, _path: &[&RouteHop]) {}
-
- fn notify_payment_probe_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {}
- }
- impl ManualRouter {
- fn expect_find_route(&self, result: Result<Route, LightningError>) {
- self.0.borrow_mut().push_back(result);
- }
- }
- impl Drop for ManualRouter {
- fn drop(&mut self) {
- if std::thread::panicking() {
- return;
- }
- assert!(self.0.borrow_mut().is_empty());