Skip to content

Commit

Permalink
Merge pull request #2294 from jkczyz/2023-05-onion-message-replies
Browse files Browse the repository at this point in the history
BOLT 12 Offers message handling support
  • Loading branch information
TheBlueMatt authored Jun 13, 2023
2 parents d78dd48 + 3fd6b44 commit 74a9ed9
Show file tree
Hide file tree
Showing 12 changed files with 848 additions and 139 deletions.
56 changes: 44 additions & 12 deletions fuzz/src/onion_message.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,8 +519,9 @@ use core::task;
/// # type MyUtxoLookup = dyn lightning::routing::utxo::UtxoLookup + Send + Sync;
/// # type MyFilter = dyn lightning::chain::Filter + Send + Sync;
/// # type MyLogger = dyn lightning::util::logger::Logger + Send + Sync;
/// # type MyMessageRouter = dyn lightning::onion_message::MessageRouter + Send + Sync;
/// # type MyChainMonitor = lightning::chain::chainmonitor::ChainMonitor<lightning::sign::InMemorySigner, Arc<MyFilter>, Arc<MyBroadcaster>, Arc<MyFeeEstimator>, Arc<MyLogger>, Arc<MyPersister>>;
/// # type MyPeerManager = lightning::ln::peer_handler::SimpleArcPeerManager<MySocketDescriptor, MyChainMonitor, MyBroadcaster, MyFeeEstimator, MyUtxoLookup, MyLogger>;
/// # type MyPeerManager = lightning::ln::peer_handler::SimpleArcPeerManager<MySocketDescriptor, MyChainMonitor, MyBroadcaster, MyFeeEstimator, MyUtxoLookup, MyLogger, MyMessageRouter>;
/// # type MyNetworkGraph = lightning::routing::gossip::NetworkGraph<Arc<MyLogger>>;
/// # type MyGossipSync = lightning::routing::gossip::P2PGossipSync<Arc<MyNetworkGraph>, Arc<MyUtxoLookup>, Arc<MyLogger>>;
/// # type MyChannelManager = lightning::ln::channelmanager::SimpleArcChannelManager<MyChainMonitor, MyBroadcaster, MyFeeEstimator, MyLogger>;
Expand Down
29 changes: 25 additions & 4 deletions lightning/src/ln/peer_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::util::ser::{VecWriter, Writeable, Writer};
use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep};
use crate::ln::wire;
use crate::ln::wire::{Encode, Type};
use crate::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
use crate::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, NodeAlias};
use crate::util::atomic_counter::AtomicCounter;
use crate::util::logger::Logger;
Expand Down Expand Up @@ -118,9 +118,12 @@ impl OnionMessageHandler for IgnoringMessageHandler {
InitFeatures::empty()
}
}
impl OffersMessageHandler for IgnoringMessageHandler {
fn handle_message(&self, _msg: OffersMessage) -> Option<OffersMessage> { None }
}
impl CustomOnionMessageHandler for IgnoringMessageHandler {
type CustomMessage = Infallible;
fn handle_custom_message(&self, _msg: Infallible) {
fn handle_custom_message(&self, _msg: Infallible) -> Option<Infallible> {
// Since we always return `None` in the read the handle method should never be called.
unreachable!();
}
Expand Down Expand Up @@ -604,7 +607,15 @@ impl Peer {
/// issues such as overly long function definitions.
///
/// This is not exported to bindings users as `Arc`s don't make sense in bindings.
pub type SimpleArcPeerManager<SD, M, T, F, C, L> = PeerManager<SD, Arc<SimpleArcChannelManager<M, T, F, L>>, Arc<P2PGossipSync<Arc<NetworkGraph<Arc<L>>>, Arc<C>, Arc<L>>>, Arc<SimpleArcOnionMessenger<L>>, Arc<L>, IgnoringMessageHandler, Arc<KeysManager>>;
pub type SimpleArcPeerManager<SD, M, T, F, C, L, R> = PeerManager<
SD,
Arc<SimpleArcChannelManager<M, T, F, L>>,
Arc<P2PGossipSync<Arc<NetworkGraph<Arc<L>>>, Arc<C>, Arc<L>>>,
Arc<SimpleArcOnionMessenger<L, R>>,
Arc<L>,
IgnoringMessageHandler,
Arc<KeysManager>
>;

/// SimpleRefPeerManager is a type alias for a PeerManager reference, and is the reference
/// counterpart to the SimpleArcPeerManager type alias. Use this type by default when you don't
Expand All @@ -614,7 +625,17 @@ pub type SimpleArcPeerManager<SD, M, T, F, C, L> = PeerManager<SD, Arc<SimpleArc
/// helps with issues such as long function definitions.
///
/// This is not exported to bindings users as general type aliases don't make sense in bindings.
pub type SimpleRefPeerManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, SD, M, T, F, C, L> = PeerManager<SD, SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'm, M, T, F, L>, &'f P2PGossipSync<&'g NetworkGraph<&'f L>, &'h C, &'f L>, &'i SimpleRefOnionMessenger<'j, 'k, L>, &'f L, IgnoringMessageHandler, &'c KeysManager>;
pub type SimpleRefPeerManager<
'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'l, 'm, 'n, SD, M, T, F, C, L, R
> = PeerManager<
SD,
&'n SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'm, M, T, F, L>,
&'f P2PGossipSync<&'g NetworkGraph<&'f L>, &'h C, &'f L>,
&'i SimpleRefOnionMessenger<'g, 'm, 'n, L, R>,
&'f L,
IgnoringMessageHandler,
&'c KeysManager
>;


/// A generic trait which is implemented for all [`PeerManager`]s. This makes bounding functions or
Expand Down
233 changes: 233 additions & 0 deletions lightning/src/offers/invoice_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// This file is Copyright its original authors, visible in version control
// history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.

//! Data structures and encoding for `invoice_error` messages.

use crate::io;
use crate::ln::msgs::DecodeError;
use crate::offers::parse::SemanticError;
use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, WithoutLength, Writeable, Writer};
use crate::util::string::UntrustedString;

use crate::prelude::*;

/// An error in response to an [`InvoiceRequest`] or an [`Invoice`].
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Invoice`]: crate::offers::invoice::Invoice
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct InvoiceError {
/// The field in the [`InvoiceRequest`] or the [`Invoice`] that contained an error.
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Invoice`]: crate::offers::invoice::Invoice
pub erroneous_field: Option<ErroneousField>,

/// An explanation of the error.
pub message: UntrustedString,
}

/// The field in the [`InvoiceRequest`] or the [`Invoice`] that contained an error.
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Invoice`]: crate::offers::invoice::Invoice
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct ErroneousField {
/// The type number of the TLV field containing the error.
pub tlv_fieldnum: u64,

/// A value to use for the TLV field to avoid the error.
pub suggested_value: Option<Vec<u8>>,
}

impl core::fmt::Display for InvoiceError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.message.fmt(f)
}
}

impl Writeable for InvoiceError {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
let tlv_fieldnum = self.erroneous_field.as_ref().map(|f| f.tlv_fieldnum);
let suggested_value =
self.erroneous_field.as_ref().and_then(|f| f.suggested_value.as_ref());
write_tlv_fields!(writer, {
(1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
(3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
(5, WithoutLength(&self.message), required),
});
Ok(())
}
}

impl Readable for InvoiceError {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
_init_and_read_tlv_fields!(reader, {
(1, erroneous_field, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
(3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
(5, error, (option, encoding: (UntrustedString, WithoutLength))),
});

let erroneous_field = match (erroneous_field, suggested_value) {
(None, None) => None,
(None, Some(_)) => return Err(DecodeError::InvalidValue),
(Some(tlv_fieldnum), suggested_value) => {
Some(ErroneousField { tlv_fieldnum, suggested_value })
},
};

let message = match error {
None => return Err(DecodeError::InvalidValue),
Some(error) => error,
};

Ok(InvoiceError { erroneous_field, message })
}
}

impl From<SemanticError> for InvoiceError {
fn from(error: SemanticError) -> Self {
InvoiceError {
erroneous_field: None,
message: UntrustedString(format!("{:?}", error)),
}
}
}

#[cfg(test)]
mod tests {
use super::{ErroneousField, InvoiceError};

use crate::ln::msgs::DecodeError;
use crate::util::ser::{HighZeroBytesDroppedBigSize, Readable, VecWriter, WithoutLength, Writeable};
use crate::util::string::UntrustedString;

#[test]
fn parses_invoice_error_without_erroneous_field() {
let mut writer = VecWriter(Vec::new());
let invoice_error = InvoiceError {
erroneous_field: None,
message: UntrustedString("Invalid value".to_string()),
};
invoice_error.write(&mut writer).unwrap();

let buffer = writer.0;
match InvoiceError::read(&mut &buffer[..]) {
Ok(invoice_error) => {
assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
assert_eq!(invoice_error.erroneous_field, None);
}
Err(e) => panic!("Unexpected error: {:?}", e),
}
}

#[test]
fn parses_invoice_error_with_erroneous_field() {
let mut writer = VecWriter(Vec::new());
let invoice_error = InvoiceError {
erroneous_field: Some(ErroneousField {
tlv_fieldnum: 42,
suggested_value: Some(vec![42; 32]),
}),
message: UntrustedString("Invalid value".to_string()),
};
invoice_error.write(&mut writer).unwrap();

let buffer = writer.0;
match InvoiceError::read(&mut &buffer[..]) {
Ok(invoice_error) => {
assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
assert_eq!(
invoice_error.erroneous_field,
Some(ErroneousField { tlv_fieldnum: 42, suggested_value: Some(vec![42; 32]) }),
);
}
Err(e) => panic!("Unexpected error: {:?}", e),
}
}

#[test]
fn parses_invoice_error_without_suggested_value() {
let mut writer = VecWriter(Vec::new());
let invoice_error = InvoiceError {
erroneous_field: Some(ErroneousField {
tlv_fieldnum: 42,
suggested_value: None,
}),
message: UntrustedString("Invalid value".to_string()),
};
invoice_error.write(&mut writer).unwrap();

let buffer = writer.0;
match InvoiceError::read(&mut &buffer[..]) {
Ok(invoice_error) => {
assert_eq!(invoice_error.message, UntrustedString("Invalid value".to_string()));
assert_eq!(
invoice_error.erroneous_field,
Some(ErroneousField { tlv_fieldnum: 42, suggested_value: None }),
);
}
Err(e) => panic!("Unexpected error: {:?}", e),
}
}

#[test]
fn fails_parsing_invoice_error_without_message() {
let tlv_fieldnum: Option<u64> = None;
let suggested_value: Option<&Vec<u8>> = None;
let error: Option<&String> = None;

let mut writer = VecWriter(Vec::new());
let mut write_tlv = || -> Result<(), DecodeError> {
write_tlv_fields!(&mut writer, {
(1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
(3, suggested_value, (option, encoding: (Vec<u8>, WithoutLength))),
(5, error, (option, encoding: (String, WithoutLength))),
});
Ok(())
};
write_tlv().unwrap();

let buffer = writer.0;
match InvoiceError::read(&mut &buffer[..]) {
Ok(_) => panic!("Expected error"),
Err(e) => {
assert_eq!(e, DecodeError::InvalidValue);
},
}
}

#[test]
fn fails_parsing_invoice_error_without_field() {
let tlv_fieldnum: Option<u64> = None;
let suggested_value = vec![42; 32];
let error = "Invalid value".to_string();

let mut writer = VecWriter(Vec::new());
let mut write_tlv = || -> Result<(), DecodeError> {
write_tlv_fields!(&mut writer, {
(1, tlv_fieldnum, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
(3, Some(&suggested_value), (option, encoding: (Vec<u8>, WithoutLength))),
(5, Some(&error), (option, encoding: (String, WithoutLength))),
});
Ok(())
};
write_tlv().unwrap();

let buffer = writer.0;
match InvoiceError::read(&mut &buffer[..]) {
Ok(_) => panic!("Expected error"),
Err(e) => {
assert_eq!(e, DecodeError::InvalidValue);
},
}
}
}
1 change: 1 addition & 0 deletions lightning/src/offers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//! Offers are a flexible protocol for Lightning payments.

pub mod invoice;
pub mod invoice_error;
pub mod invoice_request;
mod merkle;
pub mod offer;
Expand Down
Loading

0 comments on commit 74a9ed9

Please sign in to comment.