Skip to content

Commit

Permalink
Introduce ReceivedOnionMessage Enum
Browse files Browse the repository at this point in the history
Reasoning:

1. To expose `reply_path` and `path_id` to message handlers for various operations within functions.

Logic:

1. The `ReceivedOnionMessage` enum, with variants `WithReplyPath` and `WithoutReplyPath`, exposes the message and path_id, providing a responder interface.
2. The introduced enum is passed to offers and custom message handlers.

Other Changes:

1. Moved the `handle_onion_message_response` declaration to the `OnionMessageHandler` trait for use with the introduced enum and simplified function input values.
2. Revised the code flow to ensure `handle_message_response` is called only when there's a response to send and a valid `reply_path`.
  • Loading branch information
shaavan committed Feb 21, 2024
1 parent d70124c commit f75e088
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 151 deletions.
6 changes: 4 additions & 2 deletions fuzz/src/onion_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,10 @@ struct TestCustomMessageHandler {}

impl CustomOnionMessageHandler for TestCustomMessageHandler {
type CustomMessage = TestCustomMessage;
fn handle_custom_message(&self, _msg: Self::CustomMessage) -> Option<Self::CustomMessage> {
Some(TestCustomMessage {})
fn handle_custom_message<OMH: OnionMessageHandler, T: OnionMessageContents>(&self, received_onion_message: &ReceivedOnionMessage<OMH, T>) {
if let ReceivedOnionMessage::WithReplyPath(responder) = received_onion_message {
responder.respond(TestCustomMessage {})
}
}
fn read_custom_message<R: io::Read>(&self, _message_type: u64, buffer: &mut R) -> Result<Option<Self::CustomMessage>, msgs::DecodeError> {
let mut buf = Vec::new();
Expand Down
208 changes: 108 additions & 100 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use crate::offers::merkle::SignError;
use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
use crate::offers::parse::Bolt12SemanticError;
use crate::offers::refund::{Refund, RefundBuilder};
use crate::onion_message::messenger::{Destination, MessageRouter, PendingOnionMessage, new_pending_onion_message};
use crate::onion_message::messenger::{new_pending_onion_message, Destination, MessageRouter, PendingOnionMessage, ReceivedOnionMessage};
use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
use crate::sign::{EntropySource, NodeSigner, Recipient, SignerProvider};
use crate::sign::ecdsa::WriteableEcdsaChannelSigner;
Expand All @@ -75,6 +75,8 @@ use crate::util::string::UntrustedString;
use crate::util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer, VecWriter};
use crate::util::logger::{Level, Logger, WithContext};
use crate::util::errors::APIError;
use super::msgs::OnionMessageHandler;

#[cfg(not(c_bindings))]
use {
crate::routing::router::DefaultRouter,
Expand Down Expand Up @@ -9261,124 +9263,130 @@ where
}
}

impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
OffersMessageHandler for ChannelManager<M, T, ES, NS, SP, F, R, L>
impl<M: Deref, BI: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
OffersMessageHandler for ChannelManager<M, BI, ES, NS, SP, F, R, L>
where
M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
T::Target: BroadcasterInterface,
BI::Target: BroadcasterInterface,
ES::Target: EntropySource,
NS::Target: NodeSigner,
SP::Target: SignerProvider,
F::Target: FeeEstimator,
R::Target: Router,
L::Target: Logger,
{
fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage> {
fn handle_message<OMH: OnionMessageHandler>(&self, received_onion_message: &ReceivedOnionMessage<OMH, OffersMessage>) {
let secp_ctx = &self.secp_ctx;
let expanded_key = &self.inbound_payment_key;

match message {
OffersMessage::InvoiceRequest(invoice_request) => {
let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
&invoice_request
) {
Ok(amount_msats) => amount_msats,
Err(error) => return Some(OffersMessage::InvoiceError(error.into())),
};
let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
Ok(invoice_request) => invoice_request,
Err(()) => {
let error = Bolt12SemanticError::InvalidMetadata;
return Some(OffersMessage::InvoiceError(error.into()));
},
};

let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
let (payment_hash, payment_secret) = match self.create_inbound_payment(
Some(amount_msats), relative_expiry, None
) {
Ok((payment_hash, payment_secret)) => (payment_hash, payment_secret),
Err(()) => {
let error = Bolt12SemanticError::InvalidAmount;
return Some(OffersMessage::InvoiceError(error.into()));
},
};
if let ReceivedOnionMessage::WithReplyPath(responder) = received_onion_message {
let response_option = match &responder.message {
OffersMessage::InvoiceRequest(invoice_request) => {
let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
&invoice_request
) {
Ok(amount_msats) => amount_msats,
Err(error) => return responder.respond(OffersMessage::InvoiceError(error.into())),
};
let invoice_request = match invoice_request.clone().verify(expanded_key, secp_ctx) {
Ok(invoice_request) => invoice_request,
Err(()) => {
let error = Bolt12SemanticError::InvalidMetadata;
return responder.respond(OffersMessage::InvoiceError(error.into()));
},
};

let payment_paths = match self.create_blinded_payment_paths(
amount_msats, payment_secret
) {
Ok(payment_paths) => payment_paths,
Err(()) => {
let error = Bolt12SemanticError::MissingPaths;
return Some(OffersMessage::InvoiceError(error.into()));
},
};
let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
let (payment_hash, payment_secret) = match self.create_inbound_payment(
Some(amount_msats), relative_expiry, None
) {
Ok((payment_hash, payment_secret)) => (payment_hash, payment_secret),
Err(()) => {
let error = Bolt12SemanticError::InvalidAmount;
return responder.respond(OffersMessage::InvoiceError(error.into()));
},
};

#[cfg(not(feature = "std"))]
let created_at = Duration::from_secs(
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
);
let payment_paths = match self.create_blinded_payment_paths(
amount_msats, payment_secret
) {
Ok(payment_paths) => payment_paths,
Err(()) => {
let error = Bolt12SemanticError::MissingPaths;
return responder.respond(OffersMessage::InvoiceError(error.into()));
},
};

if invoice_request.keys.is_some() {
#[cfg(feature = "std")]
let builder = invoice_request.respond_using_derived_keys(
payment_paths, payment_hash
);
#[cfg(not(feature = "std"))]
let builder = invoice_request.respond_using_derived_keys_no_std(
payment_paths, payment_hash, created_at
let created_at = Duration::from_secs(
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
);
match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
Err(error) => Some(OffersMessage::InvoiceError(error.into())),

if invoice_request.keys.is_some() {
#[cfg(feature = "std")]
let builder = invoice_request.respond_using_derived_keys(
payment_paths, payment_hash
);
#[cfg(not(feature = "std"))]
let builder = invoice_request.respond_using_derived_keys_no_std(
payment_paths, payment_hash, created_at
);
match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
Err(error) => Some(OffersMessage::InvoiceError(error.into())),
}
} else {
#[cfg(feature = "std")]
let builder = invoice_request.respond_with(payment_paths, payment_hash);
#[cfg(not(feature = "std"))]
let builder = invoice_request.respond_with_no_std(
payment_paths, payment_hash, created_at
);
let response = builder.and_then(|builder| builder.allow_mpp().build())
.map_err(|e| OffersMessage::InvoiceError(e.into()))
.and_then(|invoice|
match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
InvoiceError::from_string("Failed signing invoice".to_string())
)),
Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
InvoiceError::from_string("Failed invoice signature verification".to_string())
)),
});
match response {
Ok(invoice) => Some(invoice),
Err(error) => Some(error),
}
}
} else {
#[cfg(feature = "std")]
let builder = invoice_request.respond_with(payment_paths, payment_hash);
#[cfg(not(feature = "std"))]
let builder = invoice_request.respond_with_no_std(
payment_paths, payment_hash, created_at
);
let response = builder.and_then(|builder| builder.allow_mpp().build())
.map_err(|e| OffersMessage::InvoiceError(e.into()))
.and_then(|invoice|
match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
InvoiceError::from_string("Failed signing invoice".to_string())
)),
Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
InvoiceError::from_string("Failed invoice signature verification".to_string())
)),
});
match response {
Ok(invoice) => Some(invoice),
Err(error) => Some(error),
},
OffersMessage::Invoice(invoice) => {
match invoice.verify(expanded_key, secp_ctx) {
Err(()) => {
Some(OffersMessage::InvoiceError(InvoiceError::from_string("Unrecognized invoice".to_owned())))
},
Ok(_) if invoice.invoice_features().requires_unknown_bits_from(&self.bolt12_invoice_features()) => {
Some(OffersMessage::InvoiceError(Bolt12SemanticError::UnknownRequiredFeatures.into()))
},
Ok(payment_id) => {
if let Err(e) = self.send_payment_for_bolt12_invoice(&invoice, payment_id) {
log_trace!(self.logger, "Failed paying invoice: {:?}", e);
Some(OffersMessage::InvoiceError(InvoiceError::from_string(format!("{:?}", e))))
} else {
None
}
},
}
}
},
OffersMessage::Invoice(invoice) => {
match invoice.verify(expanded_key, secp_ctx) {
Err(()) => {
Some(OffersMessage::InvoiceError(InvoiceError::from_string("Unrecognized invoice".to_owned())))
},
Ok(_) if invoice.invoice_features().requires_unknown_bits_from(&self.bolt12_invoice_features()) => {
Some(OffersMessage::InvoiceError(Bolt12SemanticError::UnknownRequiredFeatures.into()))
},
Ok(payment_id) => {
if let Err(e) = self.send_payment_for_bolt12_invoice(&invoice, payment_id) {
log_trace!(self.logger, "Failed paying invoice: {:?}", e);
Some(OffersMessage::InvoiceError(InvoiceError::from_string(format!("{:?}", e))))
} else {
None
}
},
}
},
OffersMessage::InvoiceError(invoice_error) => {
log_trace!(self.logger, "Received invoice_error: {}", invoice_error);
None
},
},
OffersMessage::InvoiceError(invoice_error) => {
log_trace!(self.logger, "Received invoice_error: {}", invoice_error);
None
},
};

if let Some(response) = response_option {
responder.respond(response);
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions lightning/src/ln/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ use bitcoin::blockdata::script::ScriptBuf;
use bitcoin::hash_types::Txid;

use crate::blinded_path::payment::{BlindedPaymentTlvs, ForwardTlvs, ReceiveTlvs};
use crate::blinded_path::BlindedPath;
use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
use crate::ln::onion_utils;
use crate::onion_message;
use crate::onion_message::packet::OnionMessageContents;
use crate::sign::{NodeSigner, Recipient};

use crate::prelude::*;
Expand Down Expand Up @@ -1590,6 +1592,8 @@ pub trait OnionMessageHandler: EventsProvider {
/// Handle an incoming `onion_message` message from the given peer.
fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage);

fn handle_onion_message_response<T: OnionMessageContents>(&self, response: T, reply_path: BlindedPath, log_suffix: fmt::Arguments);

/// Returns the next pending onion message for the peer with the given node id.
fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option<OnionMessage>;

Expand Down
11 changes: 8 additions & 3 deletions lightning/src/ln/peer_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::secp256k1::{self, Secp256k1, SecretKey, PublicKey};

use crate::blinded_path::BlindedPath;
use crate::sign::{NodeSigner, Recipient};
use crate::events::{EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider};
use crate::ln::ChannelId;
Expand All @@ -28,7 +29,7 @@ use crate::util::ser::{VecWriter, Writeable, Writer};
use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor, NextNoiseStep, MessageBuf, MSG_BUF_ALLOC_SIZE};
use crate::ln::wire;
use crate::ln::wire::{Encode, Type};
use crate::onion_message::messenger::{CustomOnionMessageHandler, PendingOnionMessage};
use crate::onion_message::messenger::{CustomOnionMessageHandler, PendingOnionMessage, ReceivedOnionMessage};
use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
use crate::onion_message::packet::OnionMessageContents;
use crate::routing::gossip::{NodeId, NodeAlias};
Expand Down Expand Up @@ -120,8 +121,10 @@ impl RoutingMessageHandler for IgnoringMessageHandler {
}
fn processing_queue_high(&self) -> bool { false }
}

impl OnionMessageHandler for IgnoringMessageHandler {
fn handle_onion_message(&self, _their_node_id: &PublicKey, _msg: &msgs::OnionMessage) {}
fn handle_onion_message_response<T: OnionMessageContents>(&self, _response: T, _reply_path: BlindedPath, _log_suffix: fmt::Arguments) {}
fn next_onion_message_for_peer(&self, _peer_node_id: PublicKey) -> Option<msgs::OnionMessage> { None }
fn peer_connected(&self, _their_node_id: &PublicKey, _init: &msgs::Init, _inbound: bool) -> Result<(), ()> { Ok(()) }
fn peer_disconnected(&self, _their_node_id: &PublicKey) {}
Expand All @@ -131,12 +134,13 @@ impl OnionMessageHandler for IgnoringMessageHandler {
InitFeatures::empty()
}
}

impl OffersMessageHandler for IgnoringMessageHandler {
fn handle_message(&self, _msg: OffersMessage) -> Option<OffersMessage> { None }
fn handle_message<OMH: OnionMessageHandler>(&self, _received_onion_message: &ReceivedOnionMessage<OMH, OffersMessage>) {}
}
impl CustomOnionMessageHandler for IgnoringMessageHandler {
type CustomMessage = Infallible;
fn handle_custom_message(&self, _msg: Infallible) -> Option<Infallible> {
fn handle_custom_message<OMH: OnionMessageHandler>(&self, _received_onion_message: &ReceivedOnionMessage<OMH, Self::CustomMessage>) {
// Since we always return `None` in the read the handle method should never be called.
unreachable!();
}
Expand All @@ -150,6 +154,7 @@ impl CustomOnionMessageHandler for IgnoringMessageHandler {

impl OnionMessageContents for Infallible {
fn tlv_type(&self) -> u64 { unreachable!(); }
fn msg_type(&self) -> &'static str { unreachable!(); }
}

impl Deref for IgnoringMessageHandler {
Expand Down

0 comments on commit f75e088

Please sign in to comment.