use crate::ln::msgs;
use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate};
use crate::ln::wire::Encode;
-use crate::util::ser::{Writeable, Writer};
+use crate::util::ser::{Writeable, Writer, BigSize};
use crate::util::test_utils;
use crate::util::config::{UserConfig, ChannelConfig, MaxDustHTLCExposure};
use crate::util::errors::APIError;
use bitcoin::hash_types::BlockHash;
-use bitcoin::hashes::Hash;
+use bitcoin::hashes::{Hash, HashEngine};
+use bitcoin::hashes::hmac::{Hmac, HmacEngine};
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::secp256k1;
// 3: final node fails backward (but tamper onion payloads from node0)
// 100: trigger error in the intermediate node and tamper returning fail_htlc
// 200: trigger error in the final node and tamper returning fail_htlc
-fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, mut callback_msg: F1, mut callback_fail: F2, mut callback_node: F3, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>, expected_short_channel_id: Option<u64>)
+fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(
+ _name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash,
+ payment_secret: &PaymentSecret, mut callback_msg: F1, mut callback_fail: F2,
+ mut callback_node: F3, expected_retryable: bool, expected_error_code: Option<u16>,
+ expected_channel_update: Option<NetworkUpdate>, expected_short_channel_id: Option<u64>
+)
where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
F2: for <'a> FnMut(&'a mut msgs::UpdateFailHTLC),
F3: FnMut(),
data: Vec<u8>
}
impl BogusOnionHopData {
- fn new(orig: msgs::OnionHopData) -> Self {
+ fn new(orig: msgs::OutboundOnionPayload) -> Self {
Self { data: orig.encode() }
}
}
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
}, true, Some(23), None, None);
+
+ run_onion_failure_test_with_fail_intercept("bogus err packet with valid hmac", 200, &nodes,
+ &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
+ let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
+ let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
+ let mut decoded_err_packet = msgs::DecodedOnionErrorPacket {
+ failuremsg: vec![0],
+ pad: vec![0; 255],
+ hmac: [0; 32],
+ };
+ let um = onion_utils::gen_um_from_shared_secret(&onion_keys[1].shared_secret.as_ref());
+ let mut hmac = HmacEngine::<Sha256>::new(&um);
+ hmac.input(&decoded_err_packet.encode()[32..]);
+ decoded_err_packet.hmac = Hmac::from_engine(hmac).into_inner();
+ msg.reason = onion_utils::encrypt_failure_packet(
+ &onion_keys[1].shared_secret.as_ref(), &decoded_err_packet.encode()[..])
+ }, || nodes[2].node.fail_htlc_backwards(&payment_hash), false, None,
+ Some(NetworkUpdate::NodeFailure { node_id: route.paths[0].hops[1].pubkey, is_permanent: true }),
+ Some(channels[1].0.contents.short_channel_id));
+ run_onion_failure_test_with_fail_intercept("0-length channel update in UPDATE onion failure", 200, &nodes,
+ &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
+ let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
+ let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
+ let mut decoded_err_packet = msgs::DecodedOnionErrorPacket {
+ failuremsg: vec![
+ 0x10, 0x7, // UPDATE|7
+ 0x0, 0x0 // 0-len channel update
+ ],
+ pad: vec![0; 255 - 4 /* 4-byte error message */],
+ hmac: [0; 32],
+ };
+ let um = onion_utils::gen_um_from_shared_secret(&onion_keys[1].shared_secret.as_ref());
+ let mut hmac = HmacEngine::<Sha256>::new(&um);
+ hmac.input(&decoded_err_packet.encode()[32..]);
+ decoded_err_packet.hmac = Hmac::from_engine(hmac).into_inner();
+ msg.reason = onion_utils::encrypt_failure_packet(
+ &onion_keys[1].shared_secret.as_ref(), &decoded_err_packet.encode()[..])
+ }, || nodes[2].node.fail_htlc_backwards(&payment_hash), true, Some(0x1000|7),
+ Some(NetworkUpdate::ChannelFailure {
+ short_channel_id: channels[1].0.contents.short_channel_id,
+ is_permanent: false,
+ }),
+ Some(channels[1].0.contents.short_channel_id));
}
#[test]
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].format {
- msgs::OnionHopDataFormat::NonFinalNode {..} => {},
+ 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].format {
- msgs::OnionHopDataFormat::FinalNode {..} => {},
+ 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"
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 as u16;
+ 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);
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));
}
macro_rules! get_phantom_route {