+#[test]
+fn test_always_create_tlv_format_onion_payloads() {
+ // Verify that we always generate tlv onion format payloads, even if the features specifically
+ // specifies no support for variable length onions, as the legacy payload format has been
+ // deprecated in BOLT4.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let mut node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+
+ // Set `node[1]`'s init features to features which return `false` for
+ // `supports_variable_length_onion()`
+ let mut no_variable_length_onion_features = InitFeatures::empty();
+ no_variable_length_onion_features.set_static_remote_key_required();
+ *node_cfgs[1].override_init_features.borrow_mut() = Some(no_variable_length_onion_features);
+
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+ let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ create_announced_chan_between_nodes(&nodes, 0, 1);
+ create_announced_chan_between_nodes(&nodes, 1, 2);
+
+ let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
+ .with_bolt11_features(Bolt11InvoiceFeatures::empty()).unwrap();
+ let (route, _payment_hash, _payment_preimage, _payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 40000);
+
+ let hops = &route.paths[0].hops;
+ // Asserts that the first hop to `node[1]` signals no support for variable length onions.
+ assert!(!hops[0].node_features.supports_variable_length_onion());
+ // Asserts that the first hop to `node[1]` signals no support for variable length onions.
+ assert!(!hops[1].node_features.supports_variable_length_onion());
+
+ let cur_height = nodes[0].best_block_info().1 + 1;
+ let (onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(
+ &route.paths[0], 40000, RecipientOnionFields::spontaneous_empty(), cur_height, &None).unwrap();
+
+ match onion_payloads[0] {
+ msgs::OutboundOnionPayload::Forward {..} => {},
+ _ => { panic!(
+ "Should have generated a `msgs::OnionHopDataFormat::NonFinalNode` payload for `hops[0]`,
+ despite that the features signals no support for variable length onions"
+ )}
+ }
+ match onion_payloads[1] {
+ msgs::OutboundOnionPayload::Receive {..} => {},
+ _ => {panic!(
+ "Should have generated a `msgs::OnionHopDataFormat::FinalNode` payload for `hops[1]`,
+ despite that the features signals no support for variable length onions"
+ )}
+ }
+}
+
+#[test]
+fn test_trampoline_onion_payload_serialization() {
+ // As per https://github.com/lightning/bolts/blob/c01d2e6267d4a8d1095f0f1188970055a9a22d29/bolt04/trampoline-payment-onion-test.json#L3
+ let trampoline_payload = OutboundTrampolinePayload::Forward {
+ amt_to_forward: 100000000,
+ outgoing_cltv_value: 800000,
+ outgoing_node_id: PublicKey::from_slice(&<Vec<u8>>::from_hex("02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145").unwrap()).unwrap(),
+ };
+
+ let slice_to_hex = |slice: &[u8]| {
+ slice.iter()
+ .map(|b| format!("{:02x}", b).to_string())
+ .collect::<String>()
+ };
+
+ let carol_payload_hex = slice_to_hex(&trampoline_payload.encode());
+ assert_eq!(carol_payload_hex, "2e020405f5e10004030c35000e2102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145");
+}
+
+fn do_test_fail_htlc_backwards_with_reason(failure_code: FailureCode) {
+
+ 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]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ create_announced_chan_between_nodes(&nodes, 0, 1);
+
+ let payment_amount = 100_000;
+ let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_amount);
+ nodes[0].node.send_payment_with_route(&route, payment_hash,
+ RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)).unwrap();
+ check_added_monitors!(nodes[0], 1);
+
+ let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+ let mut payment_event = SendEvent::from_event(events.pop().unwrap());
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
+ commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
+
+ expect_pending_htlcs_forwardable!(nodes[1]);
+ expect_payment_claimable!(nodes[1], payment_hash, payment_secret, payment_amount);
+ nodes[1].node.fail_htlc_backwards_with_reason(&payment_hash, failure_code);
+
+ expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], vec![HTLCDestination::FailedPayment { payment_hash: payment_hash }]);
+ check_added_monitors!(nodes[1], 1);
+
+ let events = nodes[1].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ let (update_fail_htlc, commitment_signed) = match events[0] {
+ MessageSendEvent::UpdateHTLCs { node_id: _ , updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, ref update_fee, ref commitment_signed } } => {
+ assert!(update_add_htlcs.is_empty());
+ assert!(update_fulfill_htlcs.is_empty());
+ assert_eq!(update_fail_htlcs.len(), 1);
+ assert!(update_fail_malformed_htlcs.is_empty());
+ assert!(update_fee.is_none());
+ (update_fail_htlcs[0].clone(), commitment_signed)
+ },
+ _ => panic!("Unexpected event"),
+ };
+
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &update_fail_htlc);
+ commitment_signed_dance!(nodes[0], nodes[1], commitment_signed, false, true);
+
+ let failure_data = match failure_code {
+ FailureCode::TemporaryNodeFailure => vec![],
+ FailureCode::RequiredNodeFeatureMissing => vec![],
+ FailureCode::IncorrectOrUnknownPaymentDetails => {
+ let mut htlc_msat_height_data = (payment_amount as u64).to_be_bytes().to_vec();
+ htlc_msat_height_data.extend_from_slice(&CHAN_CONFIRM_DEPTH.to_be_bytes());
+ htlc_msat_height_data
+ },
+ FailureCode::InvalidOnionPayload(data) => {
+ match data {
+ Some((typ, offset)) => [BigSize(typ).encode(), offset.encode()].concat(),
+ None => Vec::new(),
+ }
+ }
+ };
+
+ let failure_code = failure_code.into();
+ let permanent_flag = 0x4000;
+ let permanent_fail = (failure_code & permanent_flag) != 0;
+ expect_payment_failed!(nodes[0], payment_hash, permanent_fail, failure_code, failure_data);
+
+}
+
+#[test]
+fn test_fail_htlc_backwards_with_reason() {
+ do_test_fail_htlc_backwards_with_reason(FailureCode::TemporaryNodeFailure);
+ do_test_fail_htlc_backwards_with_reason(FailureCode::RequiredNodeFeatureMissing);
+ do_test_fail_htlc_backwards_with_reason(FailureCode::IncorrectOrUnknownPaymentDetails);
+ do_test_fail_htlc_backwards_with_reason(FailureCode::InvalidOnionPayload(Some((1 << 16, 42))));
+ do_test_fail_htlc_backwards_with_reason(FailureCode::InvalidOnionPayload(None));
+}
+