mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 07:17:40 +01:00
Merge pull request #2756 from arik-so/arik/trampoline/2023-11-outbound
Serialize Trampoline payloads in outbound onions.
This commit is contained in:
commit
6d1111111c
2 changed files with 136 additions and 3 deletions
|
@ -66,6 +66,8 @@
|
||||||
//! for more info).
|
//! for more info).
|
||||||
//! - `Keysend` - send funds to a node without an invoice
|
//! - `Keysend` - send funds to a node without an invoice
|
||||||
//! (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) for more information).
|
//! (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) for more information).
|
||||||
|
//! - `Trampoline` - supports receiving and forwarding Trampoline payments
|
||||||
|
//! (see the [`Trampoline` feature proposal](https://github.com/lightning/bolts/pull/836) for more information).
|
||||||
//!
|
//!
|
||||||
//! LDK knows about the following features, but does not support them:
|
//! LDK knows about the following features, but does not support them:
|
||||||
//! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be
|
//! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be
|
||||||
|
@ -152,6 +154,8 @@ mod sealed {
|
||||||
ChannelType | SCIDPrivacy,
|
ChannelType | SCIDPrivacy,
|
||||||
// Byte 6
|
// Byte 6
|
||||||
ZeroConf,
|
ZeroConf,
|
||||||
|
// Byte 7
|
||||||
|
Trampoline,
|
||||||
]);
|
]);
|
||||||
define_context!(NodeContext, [
|
define_context!(NodeContext, [
|
||||||
// Byte 0
|
// Byte 0
|
||||||
|
@ -168,6 +172,8 @@ mod sealed {
|
||||||
ChannelType | SCIDPrivacy,
|
ChannelType | SCIDPrivacy,
|
||||||
// Byte 6
|
// Byte 6
|
||||||
ZeroConf | Keysend,
|
ZeroConf | Keysend,
|
||||||
|
// Byte 7
|
||||||
|
Trampoline,
|
||||||
]);
|
]);
|
||||||
define_context!(ChannelContext, []);
|
define_context!(ChannelContext, []);
|
||||||
define_context!(Bolt11InvoiceContext, [
|
define_context!(Bolt11InvoiceContext, [
|
||||||
|
@ -185,6 +191,8 @@ mod sealed {
|
||||||
,
|
,
|
||||||
// Byte 6
|
// Byte 6
|
||||||
PaymentMetadata,
|
PaymentMetadata,
|
||||||
|
// Byte 7
|
||||||
|
Trampoline,
|
||||||
]);
|
]);
|
||||||
define_context!(OfferContext, []);
|
define_context!(OfferContext, []);
|
||||||
define_context!(InvoiceRequestContext, []);
|
define_context!(InvoiceRequestContext, []);
|
||||||
|
@ -420,6 +428,9 @@ mod sealed {
|
||||||
define_feature!(55, Keysend, [NodeContext],
|
define_feature!(55, Keysend, [NodeContext],
|
||||||
"Feature flags for keysend payments.", set_keysend_optional, set_keysend_required,
|
"Feature flags for keysend payments.", set_keysend_optional, set_keysend_required,
|
||||||
supports_keysend, requires_keysend);
|
supports_keysend, requires_keysend);
|
||||||
|
define_feature!(57, Trampoline, [InitContext, NodeContext, Bolt11InvoiceContext],
|
||||||
|
"Feature flags for Trampoline routing.", set_trampoline_routing_optional, set_trampoline_routing_required,
|
||||||
|
supports_trampoline_routing, requires_trampoline_routing);
|
||||||
// Note: update the module-level docs when a new feature bit is added!
|
// Note: update the module-level docs when a new feature bit is added!
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1666,7 +1666,7 @@ mod fuzzy_internal_msgs {
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::ln::{PaymentPreimage, PaymentSecret};
|
use crate::ln::{PaymentPreimage, PaymentSecret};
|
||||||
use crate::ln::features::BlindedHopFeatures;
|
use crate::ln::features::BlindedHopFeatures;
|
||||||
use super::FinalOnionHopData;
|
use super::{FinalOnionHopData, TrampolineOnionPacket};
|
||||||
|
|
||||||
// These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
|
// These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
|
||||||
// them from untrusted input):
|
// them from untrusted input):
|
||||||
|
@ -1711,6 +1711,13 @@ mod fuzzy_internal_msgs {
|
||||||
amt_to_forward: u64,
|
amt_to_forward: u64,
|
||||||
outgoing_cltv_value: u32,
|
outgoing_cltv_value: u32,
|
||||||
},
|
},
|
||||||
|
#[allow(unused)]
|
||||||
|
TrampolineEntrypoint {
|
||||||
|
amt_to_forward: u64,
|
||||||
|
outgoing_cltv_value: u32,
|
||||||
|
multipath_trampoline_data: Option<FinalOnionHopData>,
|
||||||
|
trampoline_packet: TrampolineOnionPacket,
|
||||||
|
},
|
||||||
Receive {
|
Receive {
|
||||||
payment_data: Option<FinalOnionHopData>,
|
payment_data: Option<FinalOnionHopData>,
|
||||||
payment_metadata: Option<Vec<u8>>,
|
payment_metadata: Option<Vec<u8>>,
|
||||||
|
@ -1779,6 +1786,52 @@ impl fmt::Debug for OnionPacket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// BOLT 4 onion packet including hop data for the next peer.
|
||||||
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct TrampolineOnionPacket {
|
||||||
|
/// Bolt 04 version number
|
||||||
|
pub version: u8,
|
||||||
|
/// A random sepc256k1 point, used to build the ECDH shared secret to decrypt hop_data
|
||||||
|
pub public_key: PublicKey,
|
||||||
|
/// Encrypted payload for the next hop
|
||||||
|
//
|
||||||
|
// 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<u8>) }
|
||||||
|
pub hop_data: Vec<u8>,
|
||||||
|
/// HMAC to verify the integrity of hop_data
|
||||||
|
pub hmac: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl onion_utils::Packet for TrampolineOnionPacket {
|
||||||
|
type Data = Vec<u8>;
|
||||||
|
fn new(public_key: PublicKey, hop_data: Vec<u8>, hmac: [u8; 32]) -> Self {
|
||||||
|
Self {
|
||||||
|
version: 0,
|
||||||
|
public_key,
|
||||||
|
hop_data,
|
||||||
|
hmac,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Writeable for TrampolineOnionPacket {
|
||||||
|
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
|
||||||
|
self.version.write(w)?;
|
||||||
|
self.public_key.write(w)?;
|
||||||
|
w.write_all(&self.hop_data)?;
|
||||||
|
self.hmac.write(w)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for TrampolineOnionPacket {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_fmt(format_args!("TrampolineOnionPacket version {} with hmac {:?}", self.version, &self.hmac[..]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub(crate) struct OnionErrorPacket {
|
pub(crate) struct OnionErrorPacket {
|
||||||
// This really should be a constant size slice, but the spec lets these things be up to 128KB?
|
// This really should be a constant size slice, but the spec lets these things be up to 128KB?
|
||||||
|
@ -2492,6 +2545,17 @@ impl Writeable for OutboundOnionPayload {
|
||||||
(6, short_channel_id, required)
|
(6, short_channel_id, required)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
Self::TrampolineEntrypoint {
|
||||||
|
amt_to_forward, outgoing_cltv_value, ref multipath_trampoline_data,
|
||||||
|
ref trampoline_packet
|
||||||
|
} => {
|
||||||
|
_encode_varint_length_prefixed_tlv!(w, {
|
||||||
|
(2, HighZeroBytesDroppedBigSize(*amt_to_forward), required),
|
||||||
|
(4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
|
||||||
|
(8, multipath_trampoline_data, option),
|
||||||
|
(20, trampoline_packet, required)
|
||||||
|
});
|
||||||
|
},
|
||||||
Self::Receive {
|
Self::Receive {
|
||||||
ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat,
|
ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat,
|
||||||
cltv_expiry_height, ref custom_tlvs,
|
cltv_expiry_height, ref custom_tlvs,
|
||||||
|
@ -3059,10 +3123,10 @@ mod tests {
|
||||||
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
|
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
|
||||||
use crate::ln::ChannelId;
|
use crate::ln::ChannelId;
|
||||||
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
|
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
|
||||||
use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields};
|
use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields, TrampolineOnionPacket};
|
||||||
use crate::ln::msgs::SocketAddress;
|
use crate::ln::msgs::SocketAddress;
|
||||||
use crate::routing::gossip::{NodeAlias, NodeId};
|
use crate::routing::gossip::{NodeAlias, NodeId};
|
||||||
use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited};
|
use crate::util::ser::{BigSize, Hostname, Readable, ReadableArgs, TransactionU16LenLimited, Writeable};
|
||||||
use crate::util::test_utils;
|
use crate::util::test_utils;
|
||||||
|
|
||||||
use bitcoin::hashes::hex::FromHex;
|
use bitcoin::hashes::hex::FromHex;
|
||||||
|
@ -4354,6 +4418,64 @@ mod tests {
|
||||||
} else { panic!(); }
|
} else { panic!(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encoding_final_onion_hop_data_with_trampoline_packet() {
|
||||||
|
let secp_ctx = Secp256k1::new();
|
||||||
|
let (_private_key, public_key) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
|
||||||
|
|
||||||
|
let compressed_public_key = public_key.serialize();
|
||||||
|
assert_eq!(compressed_public_key.len(), 33);
|
||||||
|
|
||||||
|
let trampoline_packet = TrampolineOnionPacket {
|
||||||
|
version: 0,
|
||||||
|
public_key,
|
||||||
|
hop_data: vec![1; 650], // this should be the standard encoded length
|
||||||
|
hmac: [2; 32],
|
||||||
|
};
|
||||||
|
let encoded_trampoline_packet = trampoline_packet.encode();
|
||||||
|
assert_eq!(encoded_trampoline_packet.len(), 716);
|
||||||
|
|
||||||
|
let msg = msgs::OutboundOnionPayload::TrampolineEntrypoint {
|
||||||
|
multipath_trampoline_data: None,
|
||||||
|
amt_to_forward: 0x0badf00d01020304,
|
||||||
|
outgoing_cltv_value: 0xffffffff,
|
||||||
|
trampoline_packet,
|
||||||
|
};
|
||||||
|
let encoded_payload = msg.encode();
|
||||||
|
|
||||||
|
let trampoline_type_bytes = &encoded_payload[19..=19];
|
||||||
|
let mut trampoline_type_cursor = Cursor::new(trampoline_type_bytes);
|
||||||
|
let trampoline_type_big_size: BigSize = Readable::read(&mut trampoline_type_cursor).unwrap();
|
||||||
|
assert_eq!(trampoline_type_big_size.0, 20);
|
||||||
|
|
||||||
|
let trampoline_length_bytes = &encoded_payload[20..=22];
|
||||||
|
let mut trampoline_length_cursor = Cursor::new(trampoline_length_bytes);
|
||||||
|
let trampoline_length_big_size: BigSize = Readable::read(&mut trampoline_length_cursor).unwrap();
|
||||||
|
assert_eq!(trampoline_length_big_size.0, encoded_trampoline_packet.len() as u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encoding_final_onion_hop_data_with_eclair_trampoline_packet() {
|
||||||
|
let public_key = PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()).unwrap();
|
||||||
|
let hop_data = <Vec<u8>>::from_hex("cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4c").unwrap();
|
||||||
|
let hmac_vector = <Vec<u8>>::from_hex("bb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap();
|
||||||
|
let mut hmac = [0; 32];
|
||||||
|
hmac.copy_from_slice(&hmac_vector);
|
||||||
|
|
||||||
|
let compressed_public_key = public_key.serialize();
|
||||||
|
assert_eq!(compressed_public_key.len(), 33);
|
||||||
|
|
||||||
|
let trampoline_packet = TrampolineOnionPacket {
|
||||||
|
version: 0,
|
||||||
|
public_key,
|
||||||
|
hop_data,
|
||||||
|
hmac,
|
||||||
|
};
|
||||||
|
let encoded_trampoline_packet = trampoline_packet.encode();
|
||||||
|
let expected_eclair_trampoline_packet = <Vec<u8>>::from_hex("0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4cbb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap();
|
||||||
|
assert_eq!(encoded_trampoline_packet, expected_eclair_trampoline_packet);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query_channel_range_end_blocknum() {
|
fn query_channel_range_end_blocknum() {
|
||||||
let tests: Vec<(u32, u32, u32)> = vec![
|
let tests: Vec<(u32, u32, u32)> = vec![
|
||||||
|
|
Loading…
Add table
Reference in a new issue