diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 2efc3c1c0..19c92e574 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1740,6 +1740,17 @@ mod fuzzy_internal_msgs { } } + pub(crate) enum OutboundTrampolinePayload { + #[allow(unused)] + Forward { + /// The value, in msat, of the payment after this hop's fee is deducted. + amt_to_forward: u64, + outgoing_cltv_value: u32, + /// The node id to which the trampoline node must find a route + outgoing_node_id: PublicKey, + } + } + pub struct DecodedOnionErrorPacket { pub(crate) hmac: [u8; 32], pub(crate) failuremsg: Vec, @@ -1798,7 +1809,7 @@ pub struct TrampolineOnionPacket { // Unlike the onion packets used for payments, Trampoline onion packets have to be shorter than // 1300 bytes. The expected default is 650 bytes. // TODO: if 650 ends up being the most common size, optimize this to be: - // enum { ThirteenHundred([u8; 650]), VarLen(Vec) } + // enum { SixFifty([u8; 650]), VarLen(Vec) } pub hop_data: Vec, /// HMAC to verify the integrity of hop_data pub hmac: [u8; 32], @@ -2597,6 +2608,22 @@ impl Writeable for OutboundOnionPayload { } } +impl Writeable for OutboundTrampolinePayload { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + match self { + Self::Forward { amt_to_forward, outgoing_cltv_value, outgoing_node_id } => { + _encode_varint_length_prefixed_tlv!(w, { + (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required), + (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required), + (14, outgoing_node_id, required) + }); + } + } + Ok(()) + } +} + + impl ReadableArgs<(Option, &NS)> for InboundOnionPayload where NS::Target: NodeSigner { fn read(r: &mut R, args: (Option, &NS)) -> Result { let (update_add_blinding_point, node_signer) = args; diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index 4bdd234f5..80c588e25 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -22,7 +22,7 @@ use crate::routing::gossip::{NetworkUpdate, RoutingFees}; use crate::routing::router::{get_route, PaymentParameters, Route, RouteParameters, RouteHint, RouteHintHop}; use crate::ln::features::{InitFeatures, Bolt11InvoiceFeatures}; use crate::ln::msgs; -use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate}; +use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate, OutboundTrampolinePayload}; use crate::ln::wire::Encode; use crate::util::ser::{Writeable, Writer, BigSize}; use crate::util::test_utils; @@ -35,11 +35,12 @@ use bitcoin::hashes::hmac::{Hmac, HmacEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1; -use bitcoin::secp256k1::{Secp256k1, SecretKey}; +use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use crate::io; use crate::prelude::*; use core::default::Default; +use bitcoin::hashes::hex::FromHex; use crate::ln::functional_test_utils::*; @@ -966,6 +967,25 @@ fn test_always_create_tlv_format_onion_payloads() { } } +#[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(&>::from_hex("02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145").unwrap()).unwrap(), + }; + + let slice_to_hex = |slice: &[u8]| { + slice.iter() + .map(|b| format!("{:02x}", b).to_string()) + .collect::() + }; + + 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); diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 53ef0729a..74de4a923 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -291,6 +291,24 @@ pub(super) fn construct_onion_packet( ) } +#[allow(unused)] +pub(super) fn construct_trampoline_onion_packet( + payloads: Vec, onion_keys: Vec, + prng_seed: [u8; 32], associated_data: &PaymentHash, length: u16, +) -> Result { + let mut packet_data = vec![0u8; length as usize]; + + let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]); + chacha.process(&vec![0u8; length as usize], &mut packet_data); + + construct_onion_packet_with_init_noise::<_, _>( + payloads, + onion_keys, + packet_data, + Some(associated_data), + ) +} + #[cfg(test)] /// Used in testing to write bogus `BogusOnionHopData` as well as `RawOnionHopData`, which is /// otherwise not representable in `msgs::OnionHopData`.