Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 120 additions & 1 deletion lightning-tests/src/upgrade_downgrade_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,10 @@ use lightning_0_2::ln::channelmanager::PaymentId as PaymentId_0_2;
use lightning_0_2::ln::channelmanager::RecipientOnionFields as RecipientOnionFields_0_2;
use lightning_0_2::ln::functional_test_utils as lightning_0_2_utils;
use lightning_0_2::ln::msgs::ChannelMessageHandler as _;
use lightning_0_2::ln::msgs::OnionMessage as OnionMessage_0_2;
use lightning_0_2::onion_message::packet::Packet as Packet_0_2;
use lightning_0_2::routing::router as router_0_2;
use lightning_0_2::util::ser::MaybeReadable as MaybeReadable_0_2;
use lightning_0_2::util::ser::Writeable as _;

use lightning_0_1::commitment_signed_dance as commitment_signed_dance_0_1;
Expand DownExpand Up@@ -45,23 +48,29 @@ use lightning_0_0_125::ln::msgs::ChannelMessageHandler as _;
use lightning_0_0_125::routing::router as router_0_0_125;
use lightning_0_0_125::util::ser::Writeable as _;

use lightning::blinded_path::message::NextMessageHop;
use lightning::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER};
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs;
use lightning::ln::msgs::BaseMessageHandler as _;
use lightning::ln::msgs::ChannelMessageHandler as _;
use lightning::ln::msgs::MessageSendEvent;
use lightning::ln::splicing_tests::*;
use lightning::ln::types::ChannelId;
use lightning::onion_message::packet::Packet;
use lightning::sign::OutputSpender;
use lightning::util::ser::{MaybeReadable, Writeable};
use lightning::util::wallet_utils::WalletSourceSync;

use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

use bitcoin::script::Builder;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::{opcodes, Amount, TxOut};

use lightning::io::Cursor;

use std::sync::Arc;

#[test]
Expand DownExpand Up@@ -700,3 +709,113 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) {
expect_payment_claimable!(nodes[2], pay_hash, pay_secret, 1_000_000);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], pay_preimage);
}

/// Constructs a dummy `OnionMessage` (current version) for use in serialization tests.
fn dummy_onion_message() -> msgs::OnionMessage {
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
msgs::OnionMessage {
blinding_point: pubkey,
onion_routing_packet: Packet {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

/// Constructs a dummy `OnionMessage` (0.2 version) for use in serialization tests.
fn dummy_onion_message_0_2() -> OnionMessage_0_2 {
let pubkey = bitcoin::secp256k1::PublicKey::from_secret_key(
&Secp256k1::new(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
OnionMessage_0_2 {
blinding_point: pubkey,
onion_routing_packet: Packet_0_2 {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

#[test]
fn test_onion_message_intercepted_upgrade_from_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` serialized by LDK 0.2 (which uses
// `peer_node_id: PublicKey` in TLV field 0) can be deserialized by the current version,
// producing `NextMessageHop::NodeId`.
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());

let event_0_2 = Event_0_2::OnionMessageIntercepted {
peer_node_id: pubkey,
message: dummy_onion_message_0_2(),
};

let serialized = lightning_0_2::util::ser::Writeable::encode(&event_0_2);

let mut reader = Cursor::new(&serialized);
let deserialized = <Event as MaybeReadable>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event::OnionMessageIntercepted { prev_hop, next_hop, message } => {
// LDK 0.2 did not write a `prev_hop`, so it must default to `None`.
assert_eq!(prev_hop, None);
assert_eq!(next_hop, NextMessageHop::NodeId(pubkey));
assert_eq!(message, dummy_onion_message());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_node_id_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `NodeId` next hop serialized by
// the current version can be deserialized by LDK 0.2 (which expects `peer_node_id` in TLV
// field 0 and ignores the newer `prev_hop` in TLV field 3).
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
let prev_hop =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[43; 32]).unwrap());

let event = Event::OnionMessageIntercepted {
prev_hop: Some(prev_hop),
next_hop: NextMessageHop::NodeId(pubkey),
message: dummy_onion_message(),
};

let serialized = event.encode();

let mut reader = Cursor::new(&serialized);
let deserialized = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event_0_2::OnionMessageIntercepted { peer_node_id, message } => {
assert_eq!(peer_node_id, pubkey);
assert_eq!(message, dummy_onion_message_0_2());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_scid_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `ShortChannelId` next hop
// serialized by the current version cannot be deserialized by LDK 0.2, since the
// `peer_node_id` field (0) is not written for SCID variants and LDK 0.2 requires it.
let event = Event::OnionMessageIntercepted {
prev_hop: None,
next_hop: NextMessageHop::ShortChannelId(42),
message: dummy_onion_message(),
};

let serialized = event.encode();

// LDK 0.2 will try to read field 0 as required. Since it's absent, the read will fail.
let mut reader = Cursor::new(&serialized);
let result = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader);
assert!(result.is_err(), "LDK 0.2 should fail to decode a ShortChannelId variant");
}
5 changes: 5 additions & 0 deletions lightning/src/blinded_path/message.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -275,6 +275,11 @@ pub enum NextMessageHop {
ShortChannelId(u64),
}

impl_ser_tlv_based_enum!(NextMessageHop,
{0, NodeId} => (),
{2, ShortChannelId} => (),
);

/// An intermediate node, and possibly a short channel id leading to the next node.
///
/// Note:
Expand Down
53 changes: 42 additions & 11 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ pub mod bump_transaction;

pub use bump_transaction::BumpTransactionEvent;

use crate::blinded_path::message::{BlindedMessagePath, OffersContext};
use crate::blinded_path::message::{BlindedMessagePath, NextMessageHop, OffersContext};
use crate::blinded_path::payment::{
Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef,
};
Expand DownExpand Up@@ -1836,9 +1836,13 @@ pub enum Event {
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments
BumpTransaction(BumpTransactionEvent),
/// We received an onion message that is intended to be forwarded to a peer
/// that is currently offline. This event will only be generated if the
/// `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
/// that is currently offline *or* that is intended to be forwarded along a channel with an
/// SCID unknown to us.
///
/// This event will only be generated if the `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs. The
/// [`NextMessageHop::ShortChannelId`] variant is only generated if `intercept_for_unknown_scids`
/// was set when constructing the `OnionMessenger`.
///
/// The offline peer should be awoken if possible on receipt of this event, such as via the LSPS5
/// protocol.
Expand All@@ -1852,9 +1856,21 @@ pub enum Event {
///
/// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
OnionMessageIntercepted {
/// The node id of the offline peer.
peer_node_id: PublicKey,
/// The onion message intended to be forwarded to `peer_node_id`.
/// The node id of the peer that sent the message, if known.
///
/// This is `None` when the message is sent with
/// [`MessageSendInstructions::ForwardedMessage`] (e.g., when calling
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]) rather than forwarded
/// internally by the `OnionMessenger`, as well as for events serialized prior to LDK 0.3.
/// Otherwise it is the node we received the message from.
///
/// [`MessageSendInstructions::ForwardedMessage`]: crate::onion_message::messenger::MessageSendInstructions::ForwardedMessage
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]: crate::offers::flow::OffersMessageFlow::enqueue_invoice_request_to_forward
prev_hop: Option<PublicKey>,
/// The next hop (offline peer or unknown SCID).
next_hop: NextMessageHop,
/// The onion message intended to be forwarded to the offline peer or via the unknown
/// channel once established.
message: msgs::OnionMessage,
},
/// Indicates that an onion message supporting peer has come online and any messages previously
Expand DownExpand Up@@ -2436,11 +2452,19 @@ impl Writeable for Event {
35u8.write(writer)?;
// Never write ConnectionNeeded events as buffered onion messages aren't serialized.
},
&Event::OnionMessageIntercepted { ref peer_node_id, ref message } => {
&Event::OnionMessageIntercepted { ref prev_hop, ref next_hop, ref message } => {
37u8.write(writer)?;
// 0 used to be peer_node_id in LDK v0.2 and prior; we keep writing it when the next
// hop is a node id for backwards compatibility.
let legacy_peer_node_id = match next_hop {
NextMessageHop::NodeId(node_id) => Some(node_id),
NextMessageHop::ShortChannelId(_) => None,
};
write_tlv_fields!(writer, {
(0, peer_node_id, required),
(0, legacy_peer_node_id, option),
(1, next_hop, required),
(2, message, required),
(3, prev_hop, option),
});
},
&Event::OnionMessagePeerConnected { ref peer_node_id } => {
Expand DownExpand Up@@ -3069,11 +3093,18 @@ impl MaybeReadable for Event {
37u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, required),
(0, peer_node_id, option),
(1, next_hop, option),
(2, message, required),
(3, prev_hop, option),
});

let next_hop = next_hop
.or(peer_node_id.map(NextMessageHop::NodeId))
.ok_or(msgs::DecodeError::InvalidValue)?;
Ok(Some(Event::OnionMessageIntercepted {
peer_node_id: peer_node_id.0.unwrap(),
prev_hop,
next_hop,
message: message.0.unwrap(),
}))
};
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -4799,6 +4799,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(
&chan_mgrs[i],
IgnoringMessageHandler {},
IgnoringMessageHandler {},
true,
);
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
let wallet_source = Arc::new(test_utils::TestWalletSource::new(
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Report the sending peer in `Event::OnionMessageIntercepted` by jkczyz · Pull Request #4682 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 120 additions & 1 deletion lightning-tests/src/upgrade_downgrade_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,10 @@ use lightning_0_2::ln::channelmanager::PaymentId as PaymentId_0_2;
use lightning_0_2::ln::channelmanager::RecipientOnionFields as RecipientOnionFields_0_2;
use lightning_0_2::ln::functional_test_utils as lightning_0_2_utils;
use lightning_0_2::ln::msgs::ChannelMessageHandler as _;
use lightning_0_2::ln::msgs::OnionMessage as OnionMessage_0_2;
use lightning_0_2::onion_message::packet::Packet as Packet_0_2;
use lightning_0_2::routing::router as router_0_2;
use lightning_0_2::util::ser::MaybeReadable as MaybeReadable_0_2;
use lightning_0_2::util::ser::Writeable as _;

use lightning_0_1::commitment_signed_dance as commitment_signed_dance_0_1;
Expand DownExpand Up@@ -45,23 +48,29 @@ use lightning_0_0_125::ln::msgs::ChannelMessageHandler as _;
use lightning_0_0_125::routing::router as router_0_0_125;
use lightning_0_0_125::util::ser::Writeable as _;

use lightning::blinded_path::message::NextMessageHop;
use lightning::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER};
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs;
use lightning::ln::msgs::BaseMessageHandler as _;
use lightning::ln::msgs::ChannelMessageHandler as _;
use lightning::ln::msgs::MessageSendEvent;
use lightning::ln::splicing_tests::*;
use lightning::ln::types::ChannelId;
use lightning::onion_message::packet::Packet;
use lightning::sign::OutputSpender;
use lightning::util::ser::{MaybeReadable, Writeable};
use lightning::util::wallet_utils::WalletSourceSync;

use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

use bitcoin::script::Builder;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::{opcodes, Amount, TxOut};

use lightning::io::Cursor;

use std::sync::Arc;

#[test]
Expand DownExpand Up@@ -700,3 +709,113 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) {
expect_payment_claimable!(nodes[2], pay_hash, pay_secret, 1_000_000);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], pay_preimage);
}

/// Constructs a dummy `OnionMessage` (current version) for use in serialization tests.
fn dummy_onion_message() -> msgs::OnionMessage {
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
msgs::OnionMessage {
blinding_point: pubkey,
onion_routing_packet: Packet {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

/// Constructs a dummy `OnionMessage` (0.2 version) for use in serialization tests.
fn dummy_onion_message_0_2() -> OnionMessage_0_2 {
let pubkey = bitcoin::secp256k1::PublicKey::from_secret_key(
&Secp256k1::new(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
OnionMessage_0_2 {
blinding_point: pubkey,
onion_routing_packet: Packet_0_2 {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

#[test]
fn test_onion_message_intercepted_upgrade_from_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` serialized by LDK 0.2 (which uses
// `peer_node_id: PublicKey` in TLV field 0) can be deserialized by the current version,
// producing `NextMessageHop::NodeId`.
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());

let event_0_2 = Event_0_2::OnionMessageIntercepted {
peer_node_id: pubkey,
message: dummy_onion_message_0_2(),
};

let serialized = lightning_0_2::util::ser::Writeable::encode(&event_0_2);

let mut reader = Cursor::new(&serialized);
let deserialized = <Event as MaybeReadable>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event::OnionMessageIntercepted { prev_hop, next_hop, message } => {
// LDK 0.2 did not write a `prev_hop`, so it must default to `None`.
assert_eq!(prev_hop, None);
assert_eq!(next_hop, NextMessageHop::NodeId(pubkey));
assert_eq!(message, dummy_onion_message());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_node_id_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `NodeId` next hop serialized by
// the current version can be deserialized by LDK 0.2 (which expects `peer_node_id` in TLV
// field 0 and ignores the newer `prev_hop` in TLV field 3).
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
let prev_hop =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[43; 32]).unwrap());

let event = Event::OnionMessageIntercepted {
prev_hop: Some(prev_hop),
next_hop: NextMessageHop::NodeId(pubkey),
message: dummy_onion_message(),
};

let serialized = event.encode();

let mut reader = Cursor::new(&serialized);
let deserialized = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event_0_2::OnionMessageIntercepted { peer_node_id, message } => {
assert_eq!(peer_node_id, pubkey);
assert_eq!(message, dummy_onion_message_0_2());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_scid_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `ShortChannelId` next hop
// serialized by the current version cannot be deserialized by LDK 0.2, since the
// `peer_node_id` field (0) is not written for SCID variants and LDK 0.2 requires it.
let event = Event::OnionMessageIntercepted {
prev_hop: None,
next_hop: NextMessageHop::ShortChannelId(42),
message: dummy_onion_message(),
};

let serialized = event.encode();

// LDK 0.2 will try to read field 0 as required. Since it's absent, the read will fail.
let mut reader = Cursor::new(&serialized);
let result = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader);
assert!(result.is_err(), "LDK 0.2 should fail to decode a ShortChannelId variant");
}
5 changes: 5 additions & 0 deletions lightning/src/blinded_path/message.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -275,6 +275,11 @@ pub enum NextMessageHop {
ShortChannelId(u64),
}

impl_ser_tlv_based_enum!(NextMessageHop,
{0, NodeId} => (),
{2, ShortChannelId} => (),
);

/// An intermediate node, and possibly a short channel id leading to the next node.
///
/// Note:
Expand Down
53 changes: 42 additions & 11 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ pub mod bump_transaction;

pub use bump_transaction::BumpTransactionEvent;

use crate::blinded_path::message::{BlindedMessagePath, OffersContext};
use crate::blinded_path::message::{BlindedMessagePath, NextMessageHop, OffersContext};
use crate::blinded_path::payment::{
Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef,
};
Expand DownExpand Up@@ -1836,9 +1836,13 @@ pub enum Event {
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments
BumpTransaction(BumpTransactionEvent),
/// We received an onion message that is intended to be forwarded to a peer
/// that is currently offline. This event will only be generated if the
/// `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
/// that is currently offline *or* that is intended to be forwarded along a channel with an
/// SCID unknown to us.
///
/// This event will only be generated if the `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs. The
/// [`NextMessageHop::ShortChannelId`] variant is only generated if `intercept_for_unknown_scids`
/// was set when constructing the `OnionMessenger`.
///
/// The offline peer should be awoken if possible on receipt of this event, such as via the LSPS5
/// protocol.
Expand All@@ -1852,9 +1856,21 @@ pub enum Event {
///
/// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
OnionMessageIntercepted {
/// The node id of the offline peer.
peer_node_id: PublicKey,
/// The onion message intended to be forwarded to `peer_node_id`.
/// The node id of the peer that sent the message, if known.
///
/// This is `None` when the message is sent with
/// [`MessageSendInstructions::ForwardedMessage`] (e.g., when calling
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]) rather than forwarded
/// internally by the `OnionMessenger`, as well as for events serialized prior to LDK 0.3.
/// Otherwise it is the node we received the message from.
///
/// [`MessageSendInstructions::ForwardedMessage`]: crate::onion_message::messenger::MessageSendInstructions::ForwardedMessage
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]: crate::offers::flow::OffersMessageFlow::enqueue_invoice_request_to_forward
prev_hop: Option<PublicKey>,
/// The next hop (offline peer or unknown SCID).
next_hop: NextMessageHop,
/// The onion message intended to be forwarded to the offline peer or via the unknown
/// channel once established.
message: msgs::OnionMessage,
},
/// Indicates that an onion message supporting peer has come online and any messages previously
Expand DownExpand Up@@ -2436,11 +2452,19 @@ impl Writeable for Event {
35u8.write(writer)?;
// Never write ConnectionNeeded events as buffered onion messages aren't serialized.
},
&Event::OnionMessageIntercepted { ref peer_node_id, ref message } => {
&Event::OnionMessageIntercepted { ref prev_hop, ref next_hop, ref message } => {
37u8.write(writer)?;
// 0 used to be peer_node_id in LDK v0.2 and prior; we keep writing it when the next
// hop is a node id for backwards compatibility.
let legacy_peer_node_id = match next_hop {
NextMessageHop::NodeId(node_id) => Some(node_id),
NextMessageHop::ShortChannelId(_) => None,
};
write_tlv_fields!(writer, {
(0, peer_node_id, required),
(0, legacy_peer_node_id, option),
(1, next_hop, required),
(2, message, required),
(3, prev_hop, option),
});
},
&Event::OnionMessagePeerConnected { ref peer_node_id } => {
Expand DownExpand Up@@ -3069,11 +3093,18 @@ impl MaybeReadable for Event {
37u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, required),
(0, peer_node_id, option),
(1, next_hop, option),
(2, message, required),
(3, prev_hop, option),
});

let next_hop = next_hop
.or(peer_node_id.map(NextMessageHop::NodeId))
.ok_or(msgs::DecodeError::InvalidValue)?;
Ok(Some(Event::OnionMessageIntercepted {
peer_node_id: peer_node_id.0.unwrap(),
prev_hop,
next_hop,
message: message.0.unwrap(),
}))
};
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -4799,6 +4799,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(
&chan_mgrs[i],
IgnoringMessageHandler {},
IgnoringMessageHandler {},
true,
);
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
let wallet_source = Arc::new(test_utils::TestWalletSource::new(
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Report the sending peer in `Event::OnionMessageIntercepted` by jkczyz · Pull Request #4682 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 120 additions & 1 deletion lightning-tests/src/upgrade_downgrade_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,10 @@ use lightning_0_2::ln::channelmanager::PaymentId as PaymentId_0_2;
use lightning_0_2::ln::channelmanager::RecipientOnionFields as RecipientOnionFields_0_2;
use lightning_0_2::ln::functional_test_utils as lightning_0_2_utils;
use lightning_0_2::ln::msgs::ChannelMessageHandler as _;
use lightning_0_2::ln::msgs::OnionMessage as OnionMessage_0_2;
use lightning_0_2::onion_message::packet::Packet as Packet_0_2;
use lightning_0_2::routing::router as router_0_2;
use lightning_0_2::util::ser::MaybeReadable as MaybeReadable_0_2;
use lightning_0_2::util::ser::Writeable as _;

use lightning_0_1::commitment_signed_dance as commitment_signed_dance_0_1;
Expand DownExpand Up@@ -45,23 +48,29 @@ use lightning_0_0_125::ln::msgs::ChannelMessageHandler as _;
use lightning_0_0_125::routing::router as router_0_0_125;
use lightning_0_0_125::util::ser::Writeable as _;

use lightning::blinded_path::message::NextMessageHop;
use lightning::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER};
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs;
use lightning::ln::msgs::BaseMessageHandler as _;
use lightning::ln::msgs::ChannelMessageHandler as _;
use lightning::ln::msgs::MessageSendEvent;
use lightning::ln::splicing_tests::*;
use lightning::ln::types::ChannelId;
use lightning::onion_message::packet::Packet;
use lightning::sign::OutputSpender;
use lightning::util::ser::{MaybeReadable, Writeable};
use lightning::util::wallet_utils::WalletSourceSync;

use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

use bitcoin::script::Builder;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::{opcodes, Amount, TxOut};

use lightning::io::Cursor;

use std::sync::Arc;

#[test]
Expand DownExpand Up@@ -700,3 +709,113 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) {
expect_payment_claimable!(nodes[2], pay_hash, pay_secret, 1_000_000);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], pay_preimage);
}

/// Constructs a dummy `OnionMessage` (current version) for use in serialization tests.
fn dummy_onion_message() -> msgs::OnionMessage {
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
msgs::OnionMessage {
blinding_point: pubkey,
onion_routing_packet: Packet {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

/// Constructs a dummy `OnionMessage` (0.2 version) for use in serialization tests.
fn dummy_onion_message_0_2() -> OnionMessage_0_2 {
let pubkey = bitcoin::secp256k1::PublicKey::from_secret_key(
&Secp256k1::new(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
OnionMessage_0_2 {
blinding_point: pubkey,
onion_routing_packet: Packet_0_2 {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

#[test]
fn test_onion_message_intercepted_upgrade_from_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` serialized by LDK 0.2 (which uses
// `peer_node_id: PublicKey` in TLV field 0) can be deserialized by the current version,
// producing `NextMessageHop::NodeId`.
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());

let event_0_2 = Event_0_2::OnionMessageIntercepted {
peer_node_id: pubkey,
message: dummy_onion_message_0_2(),
};

let serialized = lightning_0_2::util::ser::Writeable::encode(&event_0_2);

let mut reader = Cursor::new(&serialized);
let deserialized = <Event as MaybeReadable>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event::OnionMessageIntercepted { prev_hop, next_hop, message } => {
// LDK 0.2 did not write a `prev_hop`, so it must default to `None`.
assert_eq!(prev_hop, None);
assert_eq!(next_hop, NextMessageHop::NodeId(pubkey));
assert_eq!(message, dummy_onion_message());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_node_id_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `NodeId` next hop serialized by
// the current version can be deserialized by LDK 0.2 (which expects `peer_node_id` in TLV
// field 0 and ignores the newer `prev_hop` in TLV field 3).
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
let prev_hop =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[43; 32]).unwrap());

let event = Event::OnionMessageIntercepted {
prev_hop: Some(prev_hop),
next_hop: NextMessageHop::NodeId(pubkey),
message: dummy_onion_message(),
};

let serialized = event.encode();

let mut reader = Cursor::new(&serialized);
let deserialized = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event_0_2::OnionMessageIntercepted { peer_node_id, message } => {
assert_eq!(peer_node_id, pubkey);
assert_eq!(message, dummy_onion_message_0_2());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_scid_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `ShortChannelId` next hop
// serialized by the current version cannot be deserialized by LDK 0.2, since the
// `peer_node_id` field (0) is not written for SCID variants and LDK 0.2 requires it.
let event = Event::OnionMessageIntercepted {
prev_hop: None,
next_hop: NextMessageHop::ShortChannelId(42),
message: dummy_onion_message(),
};

let serialized = event.encode();

// LDK 0.2 will try to read field 0 as required. Since it's absent, the read will fail.
let mut reader = Cursor::new(&serialized);
let result = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader);
assert!(result.is_err(), "LDK 0.2 should fail to decode a ShortChannelId variant");
}
5 changes: 5 additions & 0 deletions lightning/src/blinded_path/message.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -275,6 +275,11 @@ pub enum NextMessageHop {
ShortChannelId(u64),
}

impl_ser_tlv_based_enum!(NextMessageHop,
{0, NodeId} => (),
{2, ShortChannelId} => (),
);

/// An intermediate node, and possibly a short channel id leading to the next node.
///
/// Note:
Expand Down
53 changes: 42 additions & 11 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ pub mod bump_transaction;

pub use bump_transaction::BumpTransactionEvent;

use crate::blinded_path::message::{BlindedMessagePath, OffersContext};
use crate::blinded_path::message::{BlindedMessagePath, NextMessageHop, OffersContext};
use crate::blinded_path::payment::{
Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef,
};
Expand DownExpand Up@@ -1836,9 +1836,13 @@ pub enum Event {
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments
BumpTransaction(BumpTransactionEvent),
/// We received an onion message that is intended to be forwarded to a peer
/// that is currently offline. This event will only be generated if the
/// `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
/// that is currently offline *or* that is intended to be forwarded along a channel with an
/// SCID unknown to us.
///
/// This event will only be generated if the `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs. The
/// [`NextMessageHop::ShortChannelId`] variant is only generated if `intercept_for_unknown_scids`
/// was set when constructing the `OnionMessenger`.
///
/// The offline peer should be awoken if possible on receipt of this event, such as via the LSPS5
/// protocol.
Expand All@@ -1852,9 +1856,21 @@ pub enum Event {
///
/// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
OnionMessageIntercepted {
/// The node id of the offline peer.
peer_node_id: PublicKey,
/// The onion message intended to be forwarded to `peer_node_id`.
/// The node id of the peer that sent the message, if known.
///
/// This is `None` when the message is sent with
/// [`MessageSendInstructions::ForwardedMessage`] (e.g., when calling
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]) rather than forwarded
/// internally by the `OnionMessenger`, as well as for events serialized prior to LDK 0.3.
/// Otherwise it is the node we received the message from.
///
/// [`MessageSendInstructions::ForwardedMessage`]: crate::onion_message::messenger::MessageSendInstructions::ForwardedMessage
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]: crate::offers::flow::OffersMessageFlow::enqueue_invoice_request_to_forward
prev_hop: Option<PublicKey>,
/// The next hop (offline peer or unknown SCID).
next_hop: NextMessageHop,
/// The onion message intended to be forwarded to the offline peer or via the unknown
/// channel once established.
message: msgs::OnionMessage,
},
/// Indicates that an onion message supporting peer has come online and any messages previously
Expand DownExpand Up@@ -2436,11 +2452,19 @@ impl Writeable for Event {
35u8.write(writer)?;
// Never write ConnectionNeeded events as buffered onion messages aren't serialized.
},
&Event::OnionMessageIntercepted { ref peer_node_id, ref message } => {
&Event::OnionMessageIntercepted { ref prev_hop, ref next_hop, ref message } => {
37u8.write(writer)?;
// 0 used to be peer_node_id in LDK v0.2 and prior; we keep writing it when the next
// hop is a node id for backwards compatibility.
let legacy_peer_node_id = match next_hop {
NextMessageHop::NodeId(node_id) => Some(node_id),
NextMessageHop::ShortChannelId(_) => None,
};
write_tlv_fields!(writer, {
(0, peer_node_id, required),
(0, legacy_peer_node_id, option),
(1, next_hop, required),
(2, message, required),
(3, prev_hop, option),
});
},
&Event::OnionMessagePeerConnected { ref peer_node_id } => {
Expand DownExpand Up@@ -3069,11 +3093,18 @@ impl MaybeReadable for Event {
37u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, required),
(0, peer_node_id, option),
(1, next_hop, option),
(2, message, required),
(3, prev_hop, option),
});

let next_hop = next_hop
.or(peer_node_id.map(NextMessageHop::NodeId))
.ok_or(msgs::DecodeError::InvalidValue)?;
Ok(Some(Event::OnionMessageIntercepted {
peer_node_id: peer_node_id.0.unwrap(),
prev_hop,
next_hop,
message: message.0.unwrap(),
}))
};
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -4799,6 +4799,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(
&chan_mgrs[i],
IgnoringMessageHandler {},
IgnoringMessageHandler {},
true,
);
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
let wallet_source = Arc::new(test_utils::TestWalletSource::new(
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Report the sending peer in `Event::OnionMessageIntercepted` by jkczyz · Pull Request #4682 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 120 additions & 1 deletion lightning-tests/src/upgrade_downgrade_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,10 @@ use lightning_0_2::ln::channelmanager::PaymentId as PaymentId_0_2;
use lightning_0_2::ln::channelmanager::RecipientOnionFields as RecipientOnionFields_0_2;
use lightning_0_2::ln::functional_test_utils as lightning_0_2_utils;
use lightning_0_2::ln::msgs::ChannelMessageHandler as _;
use lightning_0_2::ln::msgs::OnionMessage as OnionMessage_0_2;
use lightning_0_2::onion_message::packet::Packet as Packet_0_2;
use lightning_0_2::routing::router as router_0_2;
use lightning_0_2::util::ser::MaybeReadable as MaybeReadable_0_2;
use lightning_0_2::util::ser::Writeable as _;

use lightning_0_1::commitment_signed_dance as commitment_signed_dance_0_1;
Expand DownExpand Up@@ -45,23 +48,29 @@ use lightning_0_0_125::ln::msgs::ChannelMessageHandler as _;
use lightning_0_0_125::routing::router as router_0_0_125;
use lightning_0_0_125::util::ser::Writeable as _;

use lightning::blinded_path::message::NextMessageHop;
use lightning::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER};
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs;
use lightning::ln::msgs::BaseMessageHandler as _;
use lightning::ln::msgs::ChannelMessageHandler as _;
use lightning::ln::msgs::MessageSendEvent;
use lightning::ln::splicing_tests::*;
use lightning::ln::types::ChannelId;
use lightning::onion_message::packet::Packet;
use lightning::sign::OutputSpender;
use lightning::util::ser::{MaybeReadable, Writeable};
use lightning::util::wallet_utils::WalletSourceSync;

use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

use bitcoin::script::Builder;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::{opcodes, Amount, TxOut};

use lightning::io::Cursor;

use std::sync::Arc;

#[test]
Expand DownExpand Up@@ -700,3 +709,113 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) {
expect_payment_claimable!(nodes[2], pay_hash, pay_secret, 1_000_000);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], pay_preimage);
}

/// Constructs a dummy `OnionMessage` (current version) for use in serialization tests.
fn dummy_onion_message() -> msgs::OnionMessage {
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
msgs::OnionMessage {
blinding_point: pubkey,
onion_routing_packet: Packet {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

/// Constructs a dummy `OnionMessage` (0.2 version) for use in serialization tests.
fn dummy_onion_message_0_2() -> OnionMessage_0_2 {
let pubkey = bitcoin::secp256k1::PublicKey::from_secret_key(
&Secp256k1::new(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
OnionMessage_0_2 {
blinding_point: pubkey,
onion_routing_packet: Packet_0_2 {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

#[test]
fn test_onion_message_intercepted_upgrade_from_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` serialized by LDK 0.2 (which uses
// `peer_node_id: PublicKey` in TLV field 0) can be deserialized by the current version,
// producing `NextMessageHop::NodeId`.
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());

let event_0_2 = Event_0_2::OnionMessageIntercepted {
peer_node_id: pubkey,
message: dummy_onion_message_0_2(),
};

let serialized = lightning_0_2::util::ser::Writeable::encode(&event_0_2);

let mut reader = Cursor::new(&serialized);
let deserialized = <Event as MaybeReadable>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event::OnionMessageIntercepted { prev_hop, next_hop, message } => {
// LDK 0.2 did not write a `prev_hop`, so it must default to `None`.
assert_eq!(prev_hop, None);
assert_eq!(next_hop, NextMessageHop::NodeId(pubkey));
assert_eq!(message, dummy_onion_message());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_node_id_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `NodeId` next hop serialized by
// the current version can be deserialized by LDK 0.2 (which expects `peer_node_id` in TLV
// field 0 and ignores the newer `prev_hop` in TLV field 3).
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
let prev_hop =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[43; 32]).unwrap());

let event = Event::OnionMessageIntercepted {
prev_hop: Some(prev_hop),
next_hop: NextMessageHop::NodeId(pubkey),
message: dummy_onion_message(),
};

let serialized = event.encode();

let mut reader = Cursor::new(&serialized);
let deserialized = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event_0_2::OnionMessageIntercepted { peer_node_id, message } => {
assert_eq!(peer_node_id, pubkey);
assert_eq!(message, dummy_onion_message_0_2());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_scid_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `ShortChannelId` next hop
// serialized by the current version cannot be deserialized by LDK 0.2, since the
// `peer_node_id` field (0) is not written for SCID variants and LDK 0.2 requires it.
let event = Event::OnionMessageIntercepted {
prev_hop: None,
next_hop: NextMessageHop::ShortChannelId(42),
message: dummy_onion_message(),
};

let serialized = event.encode();

// LDK 0.2 will try to read field 0 as required. Since it's absent, the read will fail.
let mut reader = Cursor::new(&serialized);
let result = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader);
assert!(result.is_err(), "LDK 0.2 should fail to decode a ShortChannelId variant");
}
5 changes: 5 additions & 0 deletions lightning/src/blinded_path/message.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -275,6 +275,11 @@ pub enum NextMessageHop {
ShortChannelId(u64),
}

impl_ser_tlv_based_enum!(NextMessageHop,
{0, NodeId} => (),
{2, ShortChannelId} => (),
);

/// An intermediate node, and possibly a short channel id leading to the next node.
///
/// Note:
Expand Down
53 changes: 42 additions & 11 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ pub mod bump_transaction;

pub use bump_transaction::BumpTransactionEvent;

use crate::blinded_path::message::{BlindedMessagePath, OffersContext};
use crate::blinded_path::message::{BlindedMessagePath, NextMessageHop, OffersContext};
use crate::blinded_path::payment::{
Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef,
};
Expand DownExpand Up@@ -1836,9 +1836,13 @@ pub enum Event {
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments
BumpTransaction(BumpTransactionEvent),
/// We received an onion message that is intended to be forwarded to a peer
/// that is currently offline. This event will only be generated if the
/// `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
/// that is currently offline *or* that is intended to be forwarded along a channel with an
/// SCID unknown to us.
///
/// This event will only be generated if the `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs. The
/// [`NextMessageHop::ShortChannelId`] variant is only generated if `intercept_for_unknown_scids`
/// was set when constructing the `OnionMessenger`.
///
/// The offline peer should be awoken if possible on receipt of this event, such as via the LSPS5
/// protocol.
Expand All@@ -1852,9 +1856,21 @@ pub enum Event {
///
/// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
OnionMessageIntercepted {
/// The node id of the offline peer.
peer_node_id: PublicKey,
/// The onion message intended to be forwarded to `peer_node_id`.
/// The node id of the peer that sent the message, if known.
///
/// This is `None` when the message is sent with
/// [`MessageSendInstructions::ForwardedMessage`] (e.g., when calling
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]) rather than forwarded
/// internally by the `OnionMessenger`, as well as for events serialized prior to LDK 0.3.
/// Otherwise it is the node we received the message from.
///
/// [`MessageSendInstructions::ForwardedMessage`]: crate::onion_message::messenger::MessageSendInstructions::ForwardedMessage
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]: crate::offers::flow::OffersMessageFlow::enqueue_invoice_request_to_forward
prev_hop: Option<PublicKey>,
/// The next hop (offline peer or unknown SCID).
next_hop: NextMessageHop,
/// The onion message intended to be forwarded to the offline peer or via the unknown
/// channel once established.
message: msgs::OnionMessage,
},
/// Indicates that an onion message supporting peer has come online and any messages previously
Expand DownExpand Up@@ -2436,11 +2452,19 @@ impl Writeable for Event {
35u8.write(writer)?;
// Never write ConnectionNeeded events as buffered onion messages aren't serialized.
},
&Event::OnionMessageIntercepted { ref peer_node_id, ref message } => {
&Event::OnionMessageIntercepted { ref prev_hop, ref next_hop, ref message } => {
37u8.write(writer)?;
// 0 used to be peer_node_id in LDK v0.2 and prior; we keep writing it when the next
// hop is a node id for backwards compatibility.
let legacy_peer_node_id = match next_hop {
NextMessageHop::NodeId(node_id) => Some(node_id),
NextMessageHop::ShortChannelId(_) => None,
};
write_tlv_fields!(writer, {
(0, peer_node_id, required),
(0, legacy_peer_node_id, option),
(1, next_hop, required),
(2, message, required),
(3, prev_hop, option),
});
},
&Event::OnionMessagePeerConnected { ref peer_node_id } => {
Expand DownExpand Up@@ -3069,11 +3093,18 @@ impl MaybeReadable for Event {
37u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, required),
(0, peer_node_id, option),
(1, next_hop, option),
(2, message, required),
(3, prev_hop, option),
});

let next_hop = next_hop
.or(peer_node_id.map(NextMessageHop::NodeId))
.ok_or(msgs::DecodeError::InvalidValue)?;
Ok(Some(Event::OnionMessageIntercepted {
peer_node_id: peer_node_id.0.unwrap(),
prev_hop,
next_hop,
message: message.0.unwrap(),
}))
};
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -4799,6 +4799,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(
&chan_mgrs[i],
IgnoringMessageHandler {},
IgnoringMessageHandler {},
true,
);
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
let wallet_source = Arc::new(test_utils::TestWalletSource::new(
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Report the sending peer in `Event::OnionMessageIntercepted` by jkczyz · Pull Request #4682 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 120 additions & 1 deletion lightning-tests/src/upgrade_downgrade_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,10 @@ use lightning_0_2::ln::channelmanager::PaymentId as PaymentId_0_2;
use lightning_0_2::ln::channelmanager::RecipientOnionFields as RecipientOnionFields_0_2;
use lightning_0_2::ln::functional_test_utils as lightning_0_2_utils;
use lightning_0_2::ln::msgs::ChannelMessageHandler as _;
use lightning_0_2::ln::msgs::OnionMessage as OnionMessage_0_2;
use lightning_0_2::onion_message::packet::Packet as Packet_0_2;
use lightning_0_2::routing::router as router_0_2;
use lightning_0_2::util::ser::MaybeReadable as MaybeReadable_0_2;
use lightning_0_2::util::ser::Writeable as _;

use lightning_0_1::commitment_signed_dance as commitment_signed_dance_0_1;
Expand DownExpand Up@@ -45,23 +48,29 @@ use lightning_0_0_125::ln::msgs::ChannelMessageHandler as _;
use lightning_0_0_125::routing::router as router_0_0_125;
use lightning_0_0_125::util::ser::Writeable as _;

use lightning::blinded_path::message::NextMessageHop;
use lightning::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER};
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs;
use lightning::ln::msgs::BaseMessageHandler as _;
use lightning::ln::msgs::ChannelMessageHandler as _;
use lightning::ln::msgs::MessageSendEvent;
use lightning::ln::splicing_tests::*;
use lightning::ln::types::ChannelId;
use lightning::onion_message::packet::Packet;
use lightning::sign::OutputSpender;
use lightning::util::ser::{MaybeReadable, Writeable};
use lightning::util::wallet_utils::WalletSourceSync;

use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

use bitcoin::script::Builder;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::{opcodes, Amount, TxOut};

use lightning::io::Cursor;

use std::sync::Arc;

#[test]
Expand DownExpand Up@@ -700,3 +709,113 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) {
expect_payment_claimable!(nodes[2], pay_hash, pay_secret, 1_000_000);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], pay_preimage);
}

/// Constructs a dummy `OnionMessage` (current version) for use in serialization tests.
fn dummy_onion_message() -> msgs::OnionMessage {
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
msgs::OnionMessage {
blinding_point: pubkey,
onion_routing_packet: Packet {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

/// Constructs a dummy `OnionMessage` (0.2 version) for use in serialization tests.
fn dummy_onion_message_0_2() -> OnionMessage_0_2 {
let pubkey = bitcoin::secp256k1::PublicKey::from_secret_key(
&Secp256k1::new(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
OnionMessage_0_2 {
blinding_point: pubkey,
onion_routing_packet: Packet_0_2 {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

#[test]
fn test_onion_message_intercepted_upgrade_from_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` serialized by LDK 0.2 (which uses
// `peer_node_id: PublicKey` in TLV field 0) can be deserialized by the current version,
// producing `NextMessageHop::NodeId`.
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());

let event_0_2 = Event_0_2::OnionMessageIntercepted {
peer_node_id: pubkey,
message: dummy_onion_message_0_2(),
};

let serialized = lightning_0_2::util::ser::Writeable::encode(&event_0_2);

let mut reader = Cursor::new(&serialized);
let deserialized = <Event as MaybeReadable>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event::OnionMessageIntercepted { prev_hop, next_hop, message } => {
// LDK 0.2 did not write a `prev_hop`, so it must default to `None`.
assert_eq!(prev_hop, None);
assert_eq!(next_hop, NextMessageHop::NodeId(pubkey));
assert_eq!(message, dummy_onion_message());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_node_id_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `NodeId` next hop serialized by
// the current version can be deserialized by LDK 0.2 (which expects `peer_node_id` in TLV
// field 0 and ignores the newer `prev_hop` in TLV field 3).
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
let prev_hop =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[43; 32]).unwrap());

let event = Event::OnionMessageIntercepted {
prev_hop: Some(prev_hop),
next_hop: NextMessageHop::NodeId(pubkey),
message: dummy_onion_message(),
};

let serialized = event.encode();

let mut reader = Cursor::new(&serialized);
let deserialized = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event_0_2::OnionMessageIntercepted { peer_node_id, message } => {
assert_eq!(peer_node_id, pubkey);
assert_eq!(message, dummy_onion_message_0_2());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_scid_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `ShortChannelId` next hop
// serialized by the current version cannot be deserialized by LDK 0.2, since the
// `peer_node_id` field (0) is not written for SCID variants and LDK 0.2 requires it.
let event = Event::OnionMessageIntercepted {
prev_hop: None,
next_hop: NextMessageHop::ShortChannelId(42),
message: dummy_onion_message(),
};

let serialized = event.encode();

// LDK 0.2 will try to read field 0 as required. Since it's absent, the read will fail.
let mut reader = Cursor::new(&serialized);
let result = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader);
assert!(result.is_err(), "LDK 0.2 should fail to decode a ShortChannelId variant");
}
5 changes: 5 additions & 0 deletions lightning/src/blinded_path/message.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -275,6 +275,11 @@ pub enum NextMessageHop {
ShortChannelId(u64),
}

impl_ser_tlv_based_enum!(NextMessageHop,
{0, NodeId} => (),
{2, ShortChannelId} => (),
);

/// An intermediate node, and possibly a short channel id leading to the next node.
///
/// Note:
Expand Down
53 changes: 42 additions & 11 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ pub mod bump_transaction;

pub use bump_transaction::BumpTransactionEvent;

use crate::blinded_path::message::{BlindedMessagePath, OffersContext};
use crate::blinded_path::message::{BlindedMessagePath, NextMessageHop, OffersContext};
use crate::blinded_path::payment::{
Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef,
};
Expand DownExpand Up@@ -1836,9 +1836,13 @@ pub enum Event {
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments
BumpTransaction(BumpTransactionEvent),
/// We received an onion message that is intended to be forwarded to a peer
/// that is currently offline. This event will only be generated if the
/// `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
/// that is currently offline *or* that is intended to be forwarded along a channel with an
/// SCID unknown to us.
///
/// This event will only be generated if the `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs. The
/// [`NextMessageHop::ShortChannelId`] variant is only generated if `intercept_for_unknown_scids`
/// was set when constructing the `OnionMessenger`.
///
/// The offline peer should be awoken if possible on receipt of this event, such as via the LSPS5
/// protocol.
Expand All@@ -1852,9 +1856,21 @@ pub enum Event {
///
/// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
OnionMessageIntercepted {
/// The node id of the offline peer.
peer_node_id: PublicKey,
/// The onion message intended to be forwarded to `peer_node_id`.
/// The node id of the peer that sent the message, if known.
///
/// This is `None` when the message is sent with
/// [`MessageSendInstructions::ForwardedMessage`] (e.g., when calling
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]) rather than forwarded
/// internally by the `OnionMessenger`, as well as for events serialized prior to LDK 0.3.
/// Otherwise it is the node we received the message from.
///
/// [`MessageSendInstructions::ForwardedMessage`]: crate::onion_message::messenger::MessageSendInstructions::ForwardedMessage
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]: crate::offers::flow::OffersMessageFlow::enqueue_invoice_request_to_forward
prev_hop: Option<PublicKey>,
/// The next hop (offline peer or unknown SCID).
next_hop: NextMessageHop,
/// The onion message intended to be forwarded to the offline peer or via the unknown
/// channel once established.
message: msgs::OnionMessage,
},
/// Indicates that an onion message supporting peer has come online and any messages previously
Expand DownExpand Up@@ -2436,11 +2452,19 @@ impl Writeable for Event {
35u8.write(writer)?;
// Never write ConnectionNeeded events as buffered onion messages aren't serialized.
},
&Event::OnionMessageIntercepted { ref peer_node_id, ref message } => {
&Event::OnionMessageIntercepted { ref prev_hop, ref next_hop, ref message } => {
37u8.write(writer)?;
// 0 used to be peer_node_id in LDK v0.2 and prior; we keep writing it when the next
// hop is a node id for backwards compatibility.
let legacy_peer_node_id = match next_hop {
NextMessageHop::NodeId(node_id) => Some(node_id),
NextMessageHop::ShortChannelId(_) => None,
};
write_tlv_fields!(writer, {
(0, peer_node_id, required),
(0, legacy_peer_node_id, option),
(1, next_hop, required),
(2, message, required),
(3, prev_hop, option),
});
},
&Event::OnionMessagePeerConnected { ref peer_node_id } => {
Expand DownExpand Up@@ -3069,11 +3093,18 @@ impl MaybeReadable for Event {
37u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, required),
(0, peer_node_id, option),
(1, next_hop, option),
(2, message, required),
(3, prev_hop, option),
});

let next_hop = next_hop
.or(peer_node_id.map(NextMessageHop::NodeId))
.ok_or(msgs::DecodeError::InvalidValue)?;
Ok(Some(Event::OnionMessageIntercepted {
peer_node_id: peer_node_id.0.unwrap(),
prev_hop,
next_hop,
message: message.0.unwrap(),
}))
};
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -4799,6 +4799,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(
&chan_mgrs[i],
IgnoringMessageHandler {},
IgnoringMessageHandler {},
true,
);
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
let wallet_source = Arc::new(test_utils::TestWalletSource::new(
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Report the sending peer in `Event::OnionMessageIntercepted` by jkczyz · Pull Request #4682 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 120 additions & 1 deletion lightning-tests/src/upgrade_downgrade_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,10 @@ use lightning_0_2::ln::channelmanager::PaymentId as PaymentId_0_2;
use lightning_0_2::ln::channelmanager::RecipientOnionFields as RecipientOnionFields_0_2;
use lightning_0_2::ln::functional_test_utils as lightning_0_2_utils;
use lightning_0_2::ln::msgs::ChannelMessageHandler as _;
use lightning_0_2::ln::msgs::OnionMessage as OnionMessage_0_2;
use lightning_0_2::onion_message::packet::Packet as Packet_0_2;
use lightning_0_2::routing::router as router_0_2;
use lightning_0_2::util::ser::MaybeReadable as MaybeReadable_0_2;
use lightning_0_2::util::ser::Writeable as _;

use lightning_0_1::commitment_signed_dance as commitment_signed_dance_0_1;
Expand DownExpand Up@@ -45,23 +48,29 @@ use lightning_0_0_125::ln::msgs::ChannelMessageHandler as _;
use lightning_0_0_125::routing::router as router_0_0_125;
use lightning_0_0_125::util::ser::Writeable as _;

use lightning::blinded_path::message::NextMessageHop;
use lightning::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER};
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs;
use lightning::ln::msgs::BaseMessageHandler as _;
use lightning::ln::msgs::ChannelMessageHandler as _;
use lightning::ln::msgs::MessageSendEvent;
use lightning::ln::splicing_tests::*;
use lightning::ln::types::ChannelId;
use lightning::onion_message::packet::Packet;
use lightning::sign::OutputSpender;
use lightning::util::ser::{MaybeReadable, Writeable};
use lightning::util::wallet_utils::WalletSourceSync;

use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

use bitcoin::script::Builder;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::{opcodes, Amount, TxOut};

use lightning::io::Cursor;

use std::sync::Arc;

#[test]
Expand DownExpand Up@@ -700,3 +709,113 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) {
expect_payment_claimable!(nodes[2], pay_hash, pay_secret, 1_000_000);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], pay_preimage);
}

/// Constructs a dummy `OnionMessage` (current version) for use in serialization tests.
fn dummy_onion_message() -> msgs::OnionMessage {
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
msgs::OnionMessage {
blinding_point: pubkey,
onion_routing_packet: Packet {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

/// Constructs a dummy `OnionMessage` (0.2 version) for use in serialization tests.
fn dummy_onion_message_0_2() -> OnionMessage_0_2 {
let pubkey = bitcoin::secp256k1::PublicKey::from_secret_key(
&Secp256k1::new(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
OnionMessage_0_2 {
blinding_point: pubkey,
onion_routing_packet: Packet_0_2 {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

#[test]
fn test_onion_message_intercepted_upgrade_from_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` serialized by LDK 0.2 (which uses
// `peer_node_id: PublicKey` in TLV field 0) can be deserialized by the current version,
// producing `NextMessageHop::NodeId`.
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());

let event_0_2 = Event_0_2::OnionMessageIntercepted {
peer_node_id: pubkey,
message: dummy_onion_message_0_2(),
};

let serialized = lightning_0_2::util::ser::Writeable::encode(&event_0_2);

let mut reader = Cursor::new(&serialized);
let deserialized = <Event as MaybeReadable>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event::OnionMessageIntercepted { prev_hop, next_hop, message } => {
// LDK 0.2 did not write a `prev_hop`, so it must default to `None`.
assert_eq!(prev_hop, None);
assert_eq!(next_hop, NextMessageHop::NodeId(pubkey));
assert_eq!(message, dummy_onion_message());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_node_id_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `NodeId` next hop serialized by
// the current version can be deserialized by LDK 0.2 (which expects `peer_node_id` in TLV
// field 0 and ignores the newer `prev_hop` in TLV field 3).
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
let prev_hop =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[43; 32]).unwrap());

let event = Event::OnionMessageIntercepted {
prev_hop: Some(prev_hop),
next_hop: NextMessageHop::NodeId(pubkey),
message: dummy_onion_message(),
};

let serialized = event.encode();

let mut reader = Cursor::new(&serialized);
let deserialized = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event_0_2::OnionMessageIntercepted { peer_node_id, message } => {
assert_eq!(peer_node_id, pubkey);
assert_eq!(message, dummy_onion_message_0_2());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_scid_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `ShortChannelId` next hop
// serialized by the current version cannot be deserialized by LDK 0.2, since the
// `peer_node_id` field (0) is not written for SCID variants and LDK 0.2 requires it.
let event = Event::OnionMessageIntercepted {
prev_hop: None,
next_hop: NextMessageHop::ShortChannelId(42),
message: dummy_onion_message(),
};

let serialized = event.encode();

// LDK 0.2 will try to read field 0 as required. Since it's absent, the read will fail.
let mut reader = Cursor::new(&serialized);
let result = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader);
assert!(result.is_err(), "LDK 0.2 should fail to decode a ShortChannelId variant");
}
5 changes: 5 additions & 0 deletions lightning/src/blinded_path/message.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -275,6 +275,11 @@ pub enum NextMessageHop {
ShortChannelId(u64),
}

impl_ser_tlv_based_enum!(NextMessageHop,
{0, NodeId} => (),
{2, ShortChannelId} => (),
);

/// An intermediate node, and possibly a short channel id leading to the next node.
///
/// Note:
Expand Down
53 changes: 42 additions & 11 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ pub mod bump_transaction;

pub use bump_transaction::BumpTransactionEvent;

use crate::blinded_path::message::{BlindedMessagePath, OffersContext};
use crate::blinded_path::message::{BlindedMessagePath, NextMessageHop, OffersContext};
use crate::blinded_path::payment::{
Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef,
};
Expand DownExpand Up@@ -1836,9 +1836,13 @@ pub enum Event {
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments
BumpTransaction(BumpTransactionEvent),
/// We received an onion message that is intended to be forwarded to a peer
/// that is currently offline. This event will only be generated if the
/// `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
/// that is currently offline *or* that is intended to be forwarded along a channel with an
/// SCID unknown to us.
///
/// This event will only be generated if the `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs. The
/// [`NextMessageHop::ShortChannelId`] variant is only generated if `intercept_for_unknown_scids`
/// was set when constructing the `OnionMessenger`.
///
/// The offline peer should be awoken if possible on receipt of this event, such as via the LSPS5
/// protocol.
Expand All@@ -1852,9 +1856,21 @@ pub enum Event {
///
/// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
OnionMessageIntercepted {
/// The node id of the offline peer.
peer_node_id: PublicKey,
/// The onion message intended to be forwarded to `peer_node_id`.
/// The node id of the peer that sent the message, if known.
///
/// This is `None` when the message is sent with
/// [`MessageSendInstructions::ForwardedMessage`] (e.g., when calling
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]) rather than forwarded
/// internally by the `OnionMessenger`, as well as for events serialized prior to LDK 0.3.
/// Otherwise it is the node we received the message from.
///
/// [`MessageSendInstructions::ForwardedMessage`]: crate::onion_message::messenger::MessageSendInstructions::ForwardedMessage
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]: crate::offers::flow::OffersMessageFlow::enqueue_invoice_request_to_forward
prev_hop: Option<PublicKey>,
/// The next hop (offline peer or unknown SCID).
next_hop: NextMessageHop,
/// The onion message intended to be forwarded to the offline peer or via the unknown
/// channel once established.
message: msgs::OnionMessage,
},
/// Indicates that an onion message supporting peer has come online and any messages previously
Expand DownExpand Up@@ -2436,11 +2452,19 @@ impl Writeable for Event {
35u8.write(writer)?;
// Never write ConnectionNeeded events as buffered onion messages aren't serialized.
},
&Event::OnionMessageIntercepted { ref peer_node_id, ref message } => {
&Event::OnionMessageIntercepted { ref prev_hop, ref next_hop, ref message } => {
37u8.write(writer)?;
// 0 used to be peer_node_id in LDK v0.2 and prior; we keep writing it when the next
// hop is a node id for backwards compatibility.
let legacy_peer_node_id = match next_hop {
NextMessageHop::NodeId(node_id) => Some(node_id),
NextMessageHop::ShortChannelId(_) => None,
};
write_tlv_fields!(writer, {
(0, peer_node_id, required),
(0, legacy_peer_node_id, option),
(1, next_hop, required),
(2, message, required),
(3, prev_hop, option),
});
},
&Event::OnionMessagePeerConnected { ref peer_node_id } => {
Expand DownExpand Up@@ -3069,11 +3093,18 @@ impl MaybeReadable for Event {
37u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, required),
(0, peer_node_id, option),
(1, next_hop, option),
(2, message, required),
(3, prev_hop, option),
});

let next_hop = next_hop
.or(peer_node_id.map(NextMessageHop::NodeId))
.ok_or(msgs::DecodeError::InvalidValue)?;
Ok(Some(Event::OnionMessageIntercepted {
peer_node_id: peer_node_id.0.unwrap(),
prev_hop,
next_hop,
message: message.0.unwrap(),
}))
};
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -4799,6 +4799,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(
&chan_mgrs[i],
IgnoringMessageHandler {},
IgnoringMessageHandler {},
true,
);
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
let wallet_source = Arc::new(test_utils::TestWalletSource::new(
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Report the sending peer in `Event::OnionMessageIntercepted` by jkczyz · Pull Request #4682 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 120 additions & 1 deletion lightning-tests/src/upgrade_downgrade_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,10 @@ use lightning_0_2::ln::channelmanager::PaymentId as PaymentId_0_2;
use lightning_0_2::ln::channelmanager::RecipientOnionFields as RecipientOnionFields_0_2;
use lightning_0_2::ln::functional_test_utils as lightning_0_2_utils;
use lightning_0_2::ln::msgs::ChannelMessageHandler as _;
use lightning_0_2::ln::msgs::OnionMessage as OnionMessage_0_2;
use lightning_0_2::onion_message::packet::Packet as Packet_0_2;
use lightning_0_2::routing::router as router_0_2;
use lightning_0_2::util::ser::MaybeReadable as MaybeReadable_0_2;
use lightning_0_2::util::ser::Writeable as _;

use lightning_0_1::commitment_signed_dance as commitment_signed_dance_0_1;
Expand DownExpand Up@@ -45,23 +48,29 @@ use lightning_0_0_125::ln::msgs::ChannelMessageHandler as _;
use lightning_0_0_125::routing::router as router_0_0_125;
use lightning_0_0_125::util::ser::Writeable as _;

use lightning::blinded_path::message::NextMessageHop;
use lightning::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER};
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs;
use lightning::ln::msgs::BaseMessageHandler as _;
use lightning::ln::msgs::ChannelMessageHandler as _;
use lightning::ln::msgs::MessageSendEvent;
use lightning::ln::splicing_tests::*;
use lightning::ln::types::ChannelId;
use lightning::onion_message::packet::Packet;
use lightning::sign::OutputSpender;
use lightning::util::ser::{MaybeReadable, Writeable};
use lightning::util::wallet_utils::WalletSourceSync;

use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

use bitcoin::script::Builder;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::{opcodes, Amount, TxOut};

use lightning::io::Cursor;

use std::sync::Arc;

#[test]
Expand DownExpand Up@@ -700,3 +709,113 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) {
expect_payment_claimable!(nodes[2], pay_hash, pay_secret, 1_000_000);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], pay_preimage);
}

/// Constructs a dummy `OnionMessage` (current version) for use in serialization tests.
fn dummy_onion_message() -> msgs::OnionMessage {
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
msgs::OnionMessage {
blinding_point: pubkey,
onion_routing_packet: Packet {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

/// Constructs a dummy `OnionMessage` (0.2 version) for use in serialization tests.
fn dummy_onion_message_0_2() -> OnionMessage_0_2 {
let pubkey = bitcoin::secp256k1::PublicKey::from_secret_key(
&Secp256k1::new(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
OnionMessage_0_2 {
blinding_point: pubkey,
onion_routing_packet: Packet_0_2 {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

#[test]
fn test_onion_message_intercepted_upgrade_from_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` serialized by LDK 0.2 (which uses
// `peer_node_id: PublicKey` in TLV field 0) can be deserialized by the current version,
// producing `NextMessageHop::NodeId`.
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());

let event_0_2 = Event_0_2::OnionMessageIntercepted {
peer_node_id: pubkey,
message: dummy_onion_message_0_2(),
};

let serialized = lightning_0_2::util::ser::Writeable::encode(&event_0_2);

let mut reader = Cursor::new(&serialized);
let deserialized = <Event as MaybeReadable>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event::OnionMessageIntercepted { prev_hop, next_hop, message } => {
// LDK 0.2 did not write a `prev_hop`, so it must default to `None`.
assert_eq!(prev_hop, None);
assert_eq!(next_hop, NextMessageHop::NodeId(pubkey));
assert_eq!(message, dummy_onion_message());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_node_id_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `NodeId` next hop serialized by
// the current version can be deserialized by LDK 0.2 (which expects `peer_node_id` in TLV
// field 0 and ignores the newer `prev_hop` in TLV field 3).
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
let prev_hop =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[43; 32]).unwrap());

let event = Event::OnionMessageIntercepted {
prev_hop: Some(prev_hop),
next_hop: NextMessageHop::NodeId(pubkey),
message: dummy_onion_message(),
};

let serialized = event.encode();

let mut reader = Cursor::new(&serialized);
let deserialized = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event_0_2::OnionMessageIntercepted { peer_node_id, message } => {
assert_eq!(peer_node_id, pubkey);
assert_eq!(message, dummy_onion_message_0_2());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_scid_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `ShortChannelId` next hop
// serialized by the current version cannot be deserialized by LDK 0.2, since the
// `peer_node_id` field (0) is not written for SCID variants and LDK 0.2 requires it.
let event = Event::OnionMessageIntercepted {
prev_hop: None,
next_hop: NextMessageHop::ShortChannelId(42),
message: dummy_onion_message(),
};

let serialized = event.encode();

// LDK 0.2 will try to read field 0 as required. Since it's absent, the read will fail.
let mut reader = Cursor::new(&serialized);
let result = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader);
assert!(result.is_err(), "LDK 0.2 should fail to decode a ShortChannelId variant");
}
5 changes: 5 additions & 0 deletions lightning/src/blinded_path/message.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -275,6 +275,11 @@ pub enum NextMessageHop {
ShortChannelId(u64),
}

impl_ser_tlv_based_enum!(NextMessageHop,
{0, NodeId} => (),
{2, ShortChannelId} => (),
);

/// An intermediate node, and possibly a short channel id leading to the next node.
///
/// Note:
Expand Down
53 changes: 42 additions & 11 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ pub mod bump_transaction;

pub use bump_transaction::BumpTransactionEvent;

use crate::blinded_path::message::{BlindedMessagePath, OffersContext};
use crate::blinded_path::message::{BlindedMessagePath, NextMessageHop, OffersContext};
use crate::blinded_path::payment::{
Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef,
};
Expand DownExpand Up@@ -1836,9 +1836,13 @@ pub enum Event {
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments
BumpTransaction(BumpTransactionEvent),
/// We received an onion message that is intended to be forwarded to a peer
/// that is currently offline. This event will only be generated if the
/// `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
/// that is currently offline *or* that is intended to be forwarded along a channel with an
/// SCID unknown to us.
///
/// This event will only be generated if the `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs. The
/// [`NextMessageHop::ShortChannelId`] variant is only generated if `intercept_for_unknown_scids`
/// was set when constructing the `OnionMessenger`.
///
/// The offline peer should be awoken if possible on receipt of this event, such as via the LSPS5
/// protocol.
Expand All@@ -1852,9 +1856,21 @@ pub enum Event {
///
/// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
OnionMessageIntercepted {
/// The node id of the offline peer.
peer_node_id: PublicKey,
/// The onion message intended to be forwarded to `peer_node_id`.
/// The node id of the peer that sent the message, if known.
///
/// This is `None` when the message is sent with
/// [`MessageSendInstructions::ForwardedMessage`] (e.g., when calling
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]) rather than forwarded
/// internally by the `OnionMessenger`, as well as for events serialized prior to LDK 0.3.
/// Otherwise it is the node we received the message from.
///
/// [`MessageSendInstructions::ForwardedMessage`]: crate::onion_message::messenger::MessageSendInstructions::ForwardedMessage
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]: crate::offers::flow::OffersMessageFlow::enqueue_invoice_request_to_forward
prev_hop: Option<PublicKey>,
/// The next hop (offline peer or unknown SCID).
next_hop: NextMessageHop,
/// The onion message intended to be forwarded to the offline peer or via the unknown
/// channel once established.
message: msgs::OnionMessage,
},
/// Indicates that an onion message supporting peer has come online and any messages previously
Expand DownExpand Up@@ -2436,11 +2452,19 @@ impl Writeable for Event {
35u8.write(writer)?;
// Never write ConnectionNeeded events as buffered onion messages aren't serialized.
},
&Event::OnionMessageIntercepted { ref peer_node_id, ref message } => {
&Event::OnionMessageIntercepted { ref prev_hop, ref next_hop, ref message } => {
37u8.write(writer)?;
// 0 used to be peer_node_id in LDK v0.2 and prior; we keep writing it when the next
// hop is a node id for backwards compatibility.
let legacy_peer_node_id = match next_hop {
NextMessageHop::NodeId(node_id) => Some(node_id),
NextMessageHop::ShortChannelId(_) => None,
};
write_tlv_fields!(writer, {
(0, peer_node_id, required),
(0, legacy_peer_node_id, option),
(1, next_hop, required),
(2, message, required),
(3, prev_hop, option),
});
},
&Event::OnionMessagePeerConnected { ref peer_node_id } => {
Expand DownExpand Up@@ -3069,11 +3093,18 @@ impl MaybeReadable for Event {
37u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, required),
(0, peer_node_id, option),
(1, next_hop, option),
(2, message, required),
(3, prev_hop, option),
});

let next_hop = next_hop
.or(peer_node_id.map(NextMessageHop::NodeId))
.ok_or(msgs::DecodeError::InvalidValue)?;
Ok(Some(Event::OnionMessageIntercepted {
peer_node_id: peer_node_id.0.unwrap(),
prev_hop,
next_hop,
message: message.0.unwrap(),
}))
};
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -4799,6 +4799,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(
&chan_mgrs[i],
IgnoringMessageHandler {},
IgnoringMessageHandler {},
true,
);
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
let wallet_source = Arc::new(test_utils::TestWalletSource::new(
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Report the sending peer in `Event::OnionMessageIntercepted` by jkczyz · Pull Request #4682 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 120 additions & 1 deletion lightning-tests/src/upgrade_downgrade_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,10 @@ use lightning_0_2::ln::channelmanager::PaymentId as PaymentId_0_2;
use lightning_0_2::ln::channelmanager::RecipientOnionFields as RecipientOnionFields_0_2;
use lightning_0_2::ln::functional_test_utils as lightning_0_2_utils;
use lightning_0_2::ln::msgs::ChannelMessageHandler as _;
use lightning_0_2::ln::msgs::OnionMessage as OnionMessage_0_2;
use lightning_0_2::onion_message::packet::Packet as Packet_0_2;
use lightning_0_2::routing::router as router_0_2;
use lightning_0_2::util::ser::MaybeReadable as MaybeReadable_0_2;
use lightning_0_2::util::ser::Writeable as _;

use lightning_0_1::commitment_signed_dance as commitment_signed_dance_0_1;
Expand DownExpand Up@@ -45,23 +48,29 @@ use lightning_0_0_125::ln::msgs::ChannelMessageHandler as _;
use lightning_0_0_125::routing::router as router_0_0_125;
use lightning_0_0_125::util::ser::Writeable as _;

use lightning::blinded_path::message::NextMessageHop;
use lightning::chain::channelmonitor::{ANTI_REORG_DELAY, HTLC_FAIL_BACK_BUFFER};
use lightning::events::{ClosureReason, Event, HTLCHandlingFailureType};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs;
use lightning::ln::msgs::BaseMessageHandler as _;
use lightning::ln::msgs::ChannelMessageHandler as _;
use lightning::ln::msgs::MessageSendEvent;
use lightning::ln::splicing_tests::*;
use lightning::ln::types::ChannelId;
use lightning::onion_message::packet::Packet;
use lightning::sign::OutputSpender;
use lightning::util::ser::{MaybeReadable, Writeable};
use lightning::util::wallet_utils::WalletSourceSync;

use lightning_types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};

use bitcoin::script::Builder;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::{opcodes, Amount, TxOut};

use lightning::io::Cursor;

use std::sync::Arc;

#[test]
Expand DownExpand Up@@ -700,3 +709,113 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) {
expect_payment_claimable!(nodes[2], pay_hash, pay_secret, 1_000_000);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], pay_preimage);
}

/// Constructs a dummy `OnionMessage` (current version) for use in serialization tests.
fn dummy_onion_message() -> msgs::OnionMessage {
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
msgs::OnionMessage {
blinding_point: pubkey,
onion_routing_packet: Packet {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

/// Constructs a dummy `OnionMessage` (0.2 version) for use in serialization tests.
fn dummy_onion_message_0_2() -> OnionMessage_0_2 {
let pubkey = bitcoin::secp256k1::PublicKey::from_secret_key(
&Secp256k1::new(),
&SecretKey::from_slice(&[42; 32]).unwrap(),
);
OnionMessage_0_2 {
blinding_point: pubkey,
onion_routing_packet: Packet_0_2 {
version: 0,
public_key: pubkey,
hop_data: vec![1; 64],
hmac: [2; 32],
},
}
}

#[test]
fn test_onion_message_intercepted_upgrade_from_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` serialized by LDK 0.2 (which uses
// `peer_node_id: PublicKey` in TLV field 0) can be deserialized by the current version,
// producing `NextMessageHop::NodeId`.
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());

let event_0_2 = Event_0_2::OnionMessageIntercepted {
peer_node_id: pubkey,
message: dummy_onion_message_0_2(),
};

let serialized = lightning_0_2::util::ser::Writeable::encode(&event_0_2);

let mut reader = Cursor::new(&serialized);
let deserialized = <Event as MaybeReadable>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event::OnionMessageIntercepted { prev_hop, next_hop, message } => {
// LDK 0.2 did not write a `prev_hop`, so it must default to `None`.
assert_eq!(prev_hop, None);
assert_eq!(next_hop, NextMessageHop::NodeId(pubkey));
assert_eq!(message, dummy_onion_message());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_node_id_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `NodeId` next hop serialized by
// the current version can be deserialized by LDK 0.2 (which expects `peer_node_id` in TLV
// field 0 and ignores the newer `prev_hop` in TLV field 3).
let pubkey =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42; 32]).unwrap());
let prev_hop =
PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[43; 32]).unwrap());

let event = Event::OnionMessageIntercepted {
prev_hop: Some(prev_hop),
next_hop: NextMessageHop::NodeId(pubkey),
message: dummy_onion_message(),
};

let serialized = event.encode();

let mut reader = Cursor::new(&serialized);
let deserialized = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader).unwrap().unwrap();

match deserialized {
Event_0_2::OnionMessageIntercepted { peer_node_id, message } => {
assert_eq!(peer_node_id, pubkey);
assert_eq!(message, dummy_onion_message_0_2());
},
_ => panic!("Expected OnionMessageIntercepted event"),
}
}

#[test]
fn test_onion_message_intercepted_scid_downgrade_to_0_2() {
// Ensure that an `Event::OnionMessageIntercepted` with a `ShortChannelId` next hop
// serialized by the current version cannot be deserialized by LDK 0.2, since the
// `peer_node_id` field (0) is not written for SCID variants and LDK 0.2 requires it.
let event = Event::OnionMessageIntercepted {
prev_hop: None,
next_hop: NextMessageHop::ShortChannelId(42),
message: dummy_onion_message(),
};

let serialized = event.encode();

// LDK 0.2 will try to read field 0 as required. Since it's absent, the read will fail.
let mut reader = Cursor::new(&serialized);
let result = <Event_0_2 as MaybeReadable_0_2>::read(&mut reader);
assert!(result.is_err(), "LDK 0.2 should fail to decode a ShortChannelId variant");
}
5 changes: 5 additions & 0 deletions lightning/src/blinded_path/message.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -275,6 +275,11 @@ pub enum NextMessageHop {
ShortChannelId(u64),
}

impl_ser_tlv_based_enum!(NextMessageHop,
{0, NodeId} => (),
{2, ShortChannelId} => (),
);

/// An intermediate node, and possibly a short channel id leading to the next node.
///
/// Note:
Expand Down
53 changes: 42 additions & 11 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,7 +18,7 @@ pub mod bump_transaction;

pub use bump_transaction::BumpTransactionEvent;

use crate::blinded_path::message::{BlindedMessagePath, OffersContext};
use crate::blinded_path::message::{BlindedMessagePath, NextMessageHop, OffersContext};
use crate::blinded_path::payment::{
Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef,
};
Expand DownExpand Up@@ -1836,9 +1836,13 @@ pub enum Event {
/// [`ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments`]: crate::util::config::ChannelHandshakeConfig::negotiate_anchor_zero_fee_commitments
BumpTransaction(BumpTransactionEvent),
/// We received an onion message that is intended to be forwarded to a peer
/// that is currently offline. This event will only be generated if the
/// `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs.
/// that is currently offline *or* that is intended to be forwarded along a channel with an
/// SCID unknown to us.
///
/// This event will only be generated if the `OnionMessenger` was initialized with
/// [`OnionMessenger::new_with_offline_peer_interception`], see its docs. The
/// [`NextMessageHop::ShortChannelId`] variant is only generated if `intercept_for_unknown_scids`
/// was set when constructing the `OnionMessenger`.
///
/// The offline peer should be awoken if possible on receipt of this event, such as via the LSPS5
/// protocol.
Expand All@@ -1852,9 +1856,21 @@ pub enum Event {
///
/// [`OnionMessenger::new_with_offline_peer_interception`]: crate::onion_message::messenger::OnionMessenger::new_with_offline_peer_interception
OnionMessageIntercepted {
/// The node id of the offline peer.
peer_node_id: PublicKey,
/// The onion message intended to be forwarded to `peer_node_id`.
/// The node id of the peer that sent the message, if known.
///
/// This is `None` when the message is sent with
/// [`MessageSendInstructions::ForwardedMessage`] (e.g., when calling
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]) rather than forwarded
/// internally by the `OnionMessenger`, as well as for events serialized prior to LDK 0.3.
/// Otherwise it is the node we received the message from.
///
/// [`MessageSendInstructions::ForwardedMessage`]: crate::onion_message::messenger::MessageSendInstructions::ForwardedMessage
/// [`OffersMessageFlow::enqueue_invoice_request_to_forward`]: crate::offers::flow::OffersMessageFlow::enqueue_invoice_request_to_forward
prev_hop: Option<PublicKey>,
/// The next hop (offline peer or unknown SCID).
next_hop: NextMessageHop,
/// The onion message intended to be forwarded to the offline peer or via the unknown
/// channel once established.
message: msgs::OnionMessage,
},
/// Indicates that an onion message supporting peer has come online and any messages previously
Expand DownExpand Up@@ -2436,11 +2452,19 @@ impl Writeable for Event {
35u8.write(writer)?;
// Never write ConnectionNeeded events as buffered onion messages aren't serialized.
},
&Event::OnionMessageIntercepted { ref peer_node_id, ref message } => {
&Event::OnionMessageIntercepted { ref prev_hop, ref next_hop, ref message } => {
37u8.write(writer)?;
// 0 used to be peer_node_id in LDK v0.2 and prior; we keep writing it when the next
// hop is a node id for backwards compatibility.
let legacy_peer_node_id = match next_hop {
NextMessageHop::NodeId(node_id) => Some(node_id),
NextMessageHop::ShortChannelId(_) => None,
};
write_tlv_fields!(writer, {
(0, peer_node_id, required),
(0, legacy_peer_node_id, option),
(1, next_hop, required),
(2, message, required),
(3, prev_hop, option),
});
},
&Event::OnionMessagePeerConnected { ref peer_node_id } => {
Expand DownExpand Up@@ -3069,11 +3093,18 @@ impl MaybeReadable for Event {
37u8 => {
let mut f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, peer_node_id, required),
(0, peer_node_id, option),
(1, next_hop, option),
(2, message, required),
(3, prev_hop, option),
});

let next_hop = next_hop
.or(peer_node_id.map(NextMessageHop::NodeId))
.ok_or(msgs::DecodeError::InvalidValue)?;
Ok(Some(Event::OnionMessageIntercepted {
peer_node_id: peer_node_id.0.unwrap(),
prev_hop,
next_hop,
message: message.0.unwrap(),
}))
};
Expand Down
1 change: 1 addition & 0 deletions lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -4799,6 +4799,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(
&chan_mgrs[i],
IgnoringMessageHandler {},
IgnoringMessageHandler {},
true,
);
let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
let wallet_source = Arc::new(test_utils::TestWalletSource::new(
Expand Down
Loading