- 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 {};
- let scorer = RefCell::new(TestScorer::new());
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &scorer, &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,
- rejected_by_dest: 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 router = TestRouter {};
- let scorer = RefCell::new(TestScorer::new().expect(PaymentPath::Failure {
- path: path.clone(), short_channel_id: path[0].short_channel_id,
- }));
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &scorer, &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,
- rejected_by_dest: 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 router = TestRouter {};
- let scorer = RefCell::new(TestScorer::new()
- .expect(PaymentPath::Success { path: route.paths[0].clone() })
- .expect(PaymentPath::Success { path: route.paths[1].clone() })
- );
- let logger = TestLogger::new();
- let invoice_payer =
- InvoicePayer::new(&payer, router, &scorer, &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);
- }
-
- struct TestRouter;
-
- impl TestRouter {
- 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: final_value_msat / 2, cltv_expiry_delta: 144
- }],
- vec![RouteHop {
- pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
- channel_features: ChannelFeatures::empty(),
- node_features: NodeFeatures::empty(),
- short_channel_id: 1, 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<S: Score> Router<S> for TestRouter {
- fn find_route(
- &self, _payer: &PublicKey, route_params: &RouteParameters, _payment_hash: &PaymentHash,
- _first_hops: Option<&[&ChannelDetails]>, _scorer: &S
- ) -> Result<Route, LightningError> {
- Ok(Route {
- payment_params: Some(route_params.payment_params.clone()), ..Self::route_for_value(route_params.final_value_msat)
- })
- }
- }
-
- struct FailingRouter;
-
- impl<S: Score> Router<S> for FailingRouter {
- fn find_route(
- &self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash,
- _first_hops: Option<&[&ChannelDetails]>, _scorer: &S
- ) -> Result<Route, LightningError> {
- Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError })
- }
- }
-
- struct TestScorer {
- expectations: Option<VecDeque<PaymentPath>>,
- }
-
- #[derive(Debug)]
- enum PaymentPath {
- Failure { path: Vec<RouteHop>, short_channel_id: u64 },
- Success { path: Vec<RouteHop> },
- }
-
- impl TestScorer {
- fn new() -> Self {
- Self {
- expectations: None,
- }
- }
-
- fn expect(mut self, expectation: PaymentPath) -> Self {
- self.expectations.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<(), std::io::Error> { unreachable!(); }
- }
-
- impl Score for TestScorer {
- fn channel_penalty_msat(
- &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, _usage: ChannelUsage
- ) -> u64 { 0 }
-
- fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {
- if let Some(expectations) = &mut self.expectations {
- match expectations.pop_front() {
- Some(PaymentPath::Failure { path, short_channel_id }) => {
- assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
- assert_eq!(actual_short_channel_id, short_channel_id);
- },
- Some(PaymentPath::Success { path }) => {
- panic!("Unexpected successful payment path: {:?}", path)
- },
- None => panic!("Unexpected payment_path_failed call: {:?}", actual_path),
- }
- }
- }
-
- fn payment_path_successful(&mut self, actual_path: &[&RouteHop]) {
- if let Some(expectations) = &mut self.expectations {
- match expectations.pop_front() {
- Some(PaymentPath::Failure { path, .. }) => {
- panic!("Unexpected payment path failure: {:?}", path)
- },
- Some(PaymentPath::Success { path }) => {
- assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
- },
- None => panic!("Unexpected payment_path_successful call: {:?}", actual_path),
- }
- }
- }
- }
-
- impl Drop for TestScorer {
- fn drop(&mut self) {
- if std::thread::panicking() {
- return;
- }
-
- if let Some(expectations) = &self.expectations {
- if !expectations.is_empty() {
- panic!("Unsatisfied scorer expectations: {:?}", 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::MonitorUpdateFailed);
- self.fails_with(failure, OnAttempt(attempt))
- }
-
- fn fails_with_partial_failure(self, retry: RouteParameters, attempt: OnAttempt) -> Self {
- self.fails_with(PaymentSendFailure::PartialFailure {
- results: 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<PaymentId, 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(PaymentId([1; 32])),
- }
- }
-
- 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>
- ) -> Result<PaymentId, PaymentSendFailure> {
- self.check_value_msats(Amount::ForInvoice(route.get_total_amount()));
- self.check_attempts()
- }
-
- fn send_spontaneous_payment(
- &self, route: &Route, _payment_preimage: PaymentPreimage,
- ) -> Result<PaymentId, 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().map(|_| ())
- }
-
- fn abandon_payment(&self, _payment_id: PaymentId) { }
- }
-
- // *** Full Featured Functional Tests with a Real ChannelManager ***
- struct ManualRouter(RefCell<VecDeque<Result<Route, LightningError>>>);
-
- impl<S: Score> Router<S> for ManualRouter {
- fn find_route(
- &self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash,
- _first_hops: Option<&[&ChannelDetails]>, _scorer: &S
- ) -> Result<Route, LightningError> {
- self.0.borrow_mut().pop_front().unwrap()
- }
- }
- 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());
- }
- }
-
- #[test]
- fn retry_multi_path_single_failed_payment() {
- // Tests that we can/will retry after a single path of an MPP payment failed immediately
- let chanmon_cfgs = create_chanmon_cfgs(2);
- let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
- let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None, None]);
- let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
-
- create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0, InitFeatures::known(), InitFeatures::known());
- create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0, InitFeatures::known(), InitFeatures::known());
- let chans = nodes[0].node.list_usable_channels();
- let mut route = Route {
- paths: vec![
- vec![RouteHop {
- pubkey: nodes[1].node.get_our_node_id(),
- node_features: NodeFeatures::known(),
- short_channel_id: chans[0].short_channel_id.unwrap(),
- channel_features: ChannelFeatures::known(),
- fee_msat: 10_000,
- cltv_expiry_delta: 100,
- }],
- vec![RouteHop {
- pubkey: nodes[1].node.get_our_node_id(),
- node_features: NodeFeatures::known(),
- short_channel_id: chans[1].short_channel_id.unwrap(),
- channel_features: ChannelFeatures::known(),
- fee_msat: 100_000_001, // Our default max-HTLC-value is 10% of the channel value, which this is one more than
- cltv_expiry_delta: 100,
- }],
- ],
- payment_params: Some(PaymentParameters::from_node_id(nodes[1].node.get_our_node_id())),
- };
- let router = ManualRouter(RefCell::new(VecDeque::new()));
- router.expect_find_route(Ok(route.clone()));
- // On retry, split the payment across both channels.
- route.paths[0][0].fee_msat = 50_000_001;
- route.paths[1][0].fee_msat = 50_000_000;
- router.expect_find_route(Ok(route.clone()));
-
- let event_handler = |_: &_| { panic!(); };
- let scorer = RefCell::new(TestScorer::new());
- let invoice_payer = InvoicePayer::new(nodes[0].node, router, &scorer, nodes[0].logger, event_handler, Retry::Attempts(1));
-
- assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
- &nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),
- duration_since_epoch(), 3600).unwrap())
- .is_ok());
- let htlc_msgs = nodes[0].node.get_and_clear_pending_msg_events();
- assert_eq!(htlc_msgs.len(), 2);
- check_added_monitors!(nodes[0], 2);
- }
-
- #[test]
- fn immediate_retry_on_failure() {
- // Tests that we can/will retry immediately after a failure