-
- 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<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 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());
- }
- }
-
- #[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, channelmanager::provided_init_features(), channelmanager::provided_init_features());
- create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0, channelmanager::provided_init_features(), channelmanager::provided_init_features());
- 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: channelmanager::provided_node_features(),
- short_channel_id: chans[0].short_channel_id.unwrap(),
- channel_features: channelmanager::provided_channel_features(),
- fee_msat: 10_000,
- cltv_expiry_delta: 100,
- }],
- vec![RouteHop {
- pubkey: nodes[1].node.get_our_node_id(),
- node_features: channelmanager::provided_node_features(),
- short_channel_id: chans[1].short_channel_id.unwrap(),
- channel_features: channelmanager::provided_channel_features(),
- 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 invoice_payer = InvoicePayer::new(nodes[0].node, router, 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
- 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, channelmanager::provided_init_features(), channelmanager::provided_init_features());
- create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0, channelmanager::provided_init_features(), channelmanager::provided_init_features());
- 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: channelmanager::provided_node_features(),
- short_channel_id: chans[0].short_channel_id.unwrap(),
- channel_features: channelmanager::provided_channel_features(),
- 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.push(route.paths[0].clone());
- route.paths[0][0].short_channel_id = chans[1].short_channel_id.unwrap();
- route.paths[0][0].fee_msat = 50_000_000;
- route.paths[1][0].fee_msat = 50_000_001;
- router.expect_find_route(Ok(route.clone()));
-
- let event_handler = |_: &_| { panic!(); };
- let invoice_payer = InvoicePayer::new(nodes[0].node, router, 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 no_extra_retries_on_back_to_back_fail() {
- // In a previous release, we had a race where we may exceed the payment retry count if we
- // get two failures in a row with the second having `all_paths_failed` set.
- // Generally, when we give up trying to retry a payment, we don't know for sure what the
- // current state of the ChannelManager event queue is. Specifically, we cannot be sure that
- // there are not multiple additional `PaymentPathFailed` or even `PaymentSent` events
- // pending which we will see later. Thus, when we previously removed the retry tracking map
- // entry after a `all_paths_failed` `PaymentPathFailed` event, we may have dropped the
- // retry entry even though more events for the same payment were still pending. This led to
- // us retrying a payment again even though we'd already given up on it.
- //
- // We now have a separate event - `PaymentFailed` which indicates no HTLCs remain and which
- // is used to remove the payment retry counter entries instead. This tests for the specific
- // excess-retry case while also testing `PaymentFailed` generation.
-
- let chanmon_cfgs = create_chanmon_cfgs(3);
- let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
- let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
- let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
-
- let chan_1_scid = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 0, channelmanager::provided_init_features(), channelmanager::provided_init_features()).0.contents.short_channel_id;
- let chan_2_scid = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 10_000_000, 0, channelmanager::provided_init_features(), channelmanager::provided_init_features()).0.contents.short_channel_id;
-
- let mut route = Route {
- paths: vec![
- vec![RouteHop {
- pubkey: nodes[1].node.get_our_node_id(),
- node_features: channelmanager::provided_node_features(),
- short_channel_id: chan_1_scid,
- channel_features: channelmanager::provided_channel_features(),
- fee_msat: 0,
- cltv_expiry_delta: 100,
- }, RouteHop {
- pubkey: nodes[2].node.get_our_node_id(),
- node_features: channelmanager::provided_node_features(),
- short_channel_id: chan_2_scid,
- channel_features: channelmanager::provided_channel_features(),
- fee_msat: 100_000_000,
- cltv_expiry_delta: 100,
- }],
- vec![RouteHop {
- pubkey: nodes[1].node.get_our_node_id(),
- node_features: channelmanager::provided_node_features(),
- short_channel_id: chan_1_scid,
- channel_features: channelmanager::provided_channel_features(),
- fee_msat: 0,
- cltv_expiry_delta: 100,
- }, RouteHop {
- pubkey: nodes[2].node.get_our_node_id(),
- node_features: channelmanager::provided_node_features(),
- short_channel_id: chan_2_scid,
- channel_features: channelmanager::provided_channel_features(),
- fee_msat: 100_000_000,
- cltv_expiry_delta: 100,
- }]
- ],
- payment_params: Some(PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())),
- };
- let router = ManualRouter(RefCell::new(VecDeque::new()));
- router.expect_find_route(Ok(route.clone()));
- // On retry, we'll only be asked for one path
- route.paths.remove(1);
- router.expect_find_route(Ok(route.clone()));
-
- let expected_events: RefCell<VecDeque<&dyn Fn(&Event)>> = RefCell::new(VecDeque::new());
- let event_handler = |event: &Event| {
- let event_checker = expected_events.borrow_mut().pop_front().unwrap();
- event_checker(event);
- };
- let invoice_payer = InvoicePayer::new(nodes[0].node, router, 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_updates = SendEvent::from_node(&nodes[0]);
- check_added_monitors!(nodes[0], 1);
- assert_eq!(htlc_updates.msgs.len(), 1);
-
- nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &htlc_updates.msgs[0]);
- nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &htlc_updates.commitment_msg);
- check_added_monitors!(nodes[1], 1);
- let (bs_first_raa, bs_first_cs) = get_revoke_commit_msgs!(nodes[1], nodes[0].node.get_our_node_id());
-
- nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_first_raa);
- check_added_monitors!(nodes[0], 1);
- let second_htlc_updates = SendEvent::from_node(&nodes[0]);
-
- nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &bs_first_cs);
- check_added_monitors!(nodes[0], 1);
- let as_first_raa = get_event_msg!(nodes[0], MessageSendEvent::SendRevokeAndACK, nodes[1].node.get_our_node_id());
-
- nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &second_htlc_updates.msgs[0]);
- nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &second_htlc_updates.commitment_msg);
- check_added_monitors!(nodes[1], 1);
- let bs_second_raa = get_event_msg!(nodes[1], MessageSendEvent::SendRevokeAndACK, nodes[0].node.get_our_node_id());
-
- nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_first_raa);
- check_added_monitors!(nodes[1], 1);
- let bs_fail_update = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
-
- nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_second_raa);
- check_added_monitors!(nodes[0], 1);
-
- nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &bs_fail_update.update_fail_htlcs[0]);
- nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &bs_fail_update.commitment_signed);
- check_added_monitors!(nodes[0], 1);
- let (as_second_raa, as_third_cs) = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id());
-
- nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_second_raa);
- check_added_monitors!(nodes[1], 1);
- let bs_second_fail_update = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
-
- nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &as_third_cs);
- check_added_monitors!(nodes[1], 1);
- let bs_third_raa = get_event_msg!(nodes[1], MessageSendEvent::SendRevokeAndACK, nodes[0].node.get_our_node_id());
-
- nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &bs_second_fail_update.update_fail_htlcs[0]);
- nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &bs_second_fail_update.commitment_signed);
- check_added_monitors!(nodes[0], 1);
-
- nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_third_raa);
- check_added_monitors!(nodes[0], 1);
- let (as_third_raa, as_fourth_cs) = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id());
-
- nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_third_raa);
- check_added_monitors!(nodes[1], 1);
- nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &as_fourth_cs);
- check_added_monitors!(nodes[1], 1);
- let bs_fourth_raa = get_event_msg!(nodes[1], MessageSendEvent::SendRevokeAndACK, nodes[0].node.get_our_node_id());
-
- nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_fourth_raa);
- check_added_monitors!(nodes[0], 1);
-
- // At this point A has sent two HTLCs which both failed due to lack of fee. It now has two
- // pending `PaymentPathFailed` events, one with `all_paths_failed` unset, and the second
- // with it set. The first event will use up the only retry we are allowed, with the second
- // `PaymentPathFailed` being passed up to the user (us, in this case). Previously, we'd
- // treated this as "HTLC complete" and dropped the retry counter, causing us to retry again
- // if the final HTLC failed.
- expected_events.borrow_mut().push_back(&|ev: &Event| {
- if let Event::PaymentPathFailed { payment_failed_permanently, all_paths_failed, .. } = ev {
- assert!(!payment_failed_permanently);
- assert!(all_paths_failed);
- } else { panic!("Unexpected event"); }
- });
- nodes[0].node.process_pending_events(&invoice_payer);
- assert!(expected_events.borrow().is_empty());
-
- let retry_htlc_updates = SendEvent::from_node(&nodes[0]);
- check_added_monitors!(nodes[0], 1);
-
- nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &retry_htlc_updates.msgs[0]);
- commitment_signed_dance!(nodes[1], nodes[0], &retry_htlc_updates.commitment_msg, false, true);
- let bs_fail_update = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
- nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &bs_fail_update.update_fail_htlcs[0]);
- commitment_signed_dance!(nodes[0], nodes[1], &bs_fail_update.commitment_signed, false, true);
-
- expected_events.borrow_mut().push_back(&|ev: &Event| {
- if let Event::PaymentPathFailed { payment_failed_permanently, all_paths_failed, .. } = ev {
- assert!(!payment_failed_permanently);
- assert!(all_paths_failed);
- } else { panic!("Unexpected event"); }
- });
- expected_events.borrow_mut().push_back(&|ev: &Event| {
- if let Event::PaymentFailed { .. } = ev {
- } else { panic!("Unexpected event"); }
- });
- nodes[0].node.process_pending_events(&invoice_payer);
- assert!(expected_events.borrow().is_empty());