Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom onion messages #1748

Merged
merged 9 commits into from
Oct 19, 2022
58 changes: 46 additions & 12 deletions fuzz/src/onion_message.rs

Large diffs are not rendered by default.

21 changes: 19 additions & 2 deletions lightning/src/ln/peer_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ use ln::features::{InitFeatures, NodeFeatures};
use ln::msgs;
use ln::msgs::{ChannelMessageHandler, LightningError, NetAddress, OnionMessageHandler, RoutingMessageHandler};
use ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager};
use util::ser::{VecWriter, Writeable, Writer};
use util::ser::{MaybeReadableArgs, VecWriter, Writeable, Writer};
use ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep};
use ln::wire;
use ln::wire::Encode;
use onion_message::{SimpleArcOnionMessenger, SimpleRefOnionMessenger};
use onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
use routing::gossip::{NetworkGraph, P2PGossipSync};
use util::atomic_counter::AtomicCounter;
use util::crypto::sign;
Expand Down Expand Up @@ -95,6 +95,23 @@ impl OnionMessageHandler for IgnoringMessageHandler {
InitFeatures::empty()
}
}
impl CustomOnionMessageHandler for IgnoringMessageHandler {
type CustomMessage = Infallible;
fn handle_custom_message(&self, _msg: Self::CustomMessage) {
// Since we always return `None` in the read the handle method should never be called.
unreachable!();
}
}
impl MaybeReadableArgs<u64> for Infallible {
fn read<R: io::Read>(_buffer: &mut R, _msg_type: u64) -> Result<Option<Self>, msgs::DecodeError> where Self: Sized {
Ok(None)
}
}

impl CustomOnionMessageContents for Infallible {
fn tlv_type(&self) -> u64 { unreachable!(); }
}

impl Deref for IgnoringMessageHandler {
type Target = IgnoringMessageHandler;
fn deref(&self) -> &Self { self }
Expand Down
108 changes: 91 additions & 17 deletions lightning/src/onion_message/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@

use chain::keysinterface::{KeysInterface, Recipient};
use ln::features::InitFeatures;
use ln::msgs::{self, OnionMessageHandler};
use super::{BlindedRoute, Destination, OnionMessenger, SendError};
use ln::msgs::{self, DecodeError, OnionMessageHandler};
use super::{BlindedRoute, CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessageContents, OnionMessenger, SendError};
use util::enforcing_trait_impls::EnforcingSigner;
use util::ser::{MaybeReadableArgs, Writeable, Writer};
use util::test_utils;

use bitcoin::network::constants::Network;
use bitcoin::secp256k1::{PublicKey, Secp256k1};

use io;
use sync::Arc;

struct MessengerNode {
keys_manager: Arc<test_utils::TestKeysInterface>,
messenger: OnionMessenger<EnforcingSigner, Arc<test_utils::TestKeysInterface>, Arc<test_utils::TestLogger>>,
messenger: OnionMessenger<EnforcingSigner, Arc<test_utils::TestKeysInterface>, Arc<test_utils::TestLogger>, Arc<TestCustomMessageHandler>>,
logger: Arc<test_utils::TestLogger>,
}

Expand All @@ -34,6 +36,43 @@ impl MessengerNode {
}
}

#[derive(Clone)]
struct TestCustomMessage {}

const CUSTOM_MESSAGE_TYPE: u64 = 4242;
const CUSTOM_MESSAGE_CONTENTS: [u8; 32] = [42; 32];

impl CustomOnionMessageContents for TestCustomMessage {
fn tlv_type(&self) -> u64 {
CUSTOM_MESSAGE_TYPE
}
}

impl Writeable for TestCustomMessage {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
Ok(CUSTOM_MESSAGE_CONTENTS.write(w)?)
}
}

impl MaybeReadableArgs<u64> for TestCustomMessage {
fn read<R: io::Read>(buffer: &mut R, message_type: u64) -> Result<Option<Self>, DecodeError> where Self: Sized {
if message_type == CUSTOM_MESSAGE_TYPE {
let mut buf = Vec::new();
buffer.read_to_end(&mut buf)?;
assert_eq!(buf, CUSTOM_MESSAGE_CONTENTS);
return Ok(Some(TestCustomMessage {}))
}
Ok(None)
}
}

struct TestCustomMessageHandler {}

impl CustomOnionMessageHandler for TestCustomMessageHandler {
type CustomMessage = TestCustomMessage;
fn handle_custom_message(&self, _msg: Self::CustomMessage) {}
}

fn create_nodes(num_messengers: u8) -> Vec<MessengerNode> {
let mut nodes = Vec::new();
for i in 0..num_messengers {
Expand All @@ -42,7 +81,7 @@ fn create_nodes(num_messengers: u8) -> Vec<MessengerNode> {
let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&seed, Network::Testnet));
nodes.push(MessengerNode {
keys_manager: keys_manager.clone(),
messenger: OnionMessenger::new(keys_manager, logger.clone()),
messenger: OnionMessenger::new(keys_manager, logger.clone(), Arc::new(TestCustomMessageHandler {})),
logger,
});
}
Expand Down Expand Up @@ -80,103 +119,138 @@ fn pass_along_path(path: &Vec<MessengerNode>, expected_path_id: Option<[u8; 32]>
#[test]
fn one_hop() {
let nodes = create_nodes(2);
let test_msg = OnionMessageContents::Custom(TestCustomMessage {});

nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), None).unwrap();
nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), test_msg, None).unwrap();
pass_along_path(&nodes, None);
}

#[test]
fn two_unblinded_hops() {
let nodes = create_nodes(3);
let test_msg = OnionMessageContents::Custom(TestCustomMessage {});

nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk()), None).unwrap();
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk()], Destination::Node(nodes[2].get_node_pk()), test_msg, None).unwrap();
pass_along_path(&nodes, None);
}

#[test]
fn two_unblinded_two_blinded() {
let nodes = create_nodes(5);
let test_msg = OnionMessageContents::Custom(TestCustomMessage {});

let secp_ctx = Secp256k1::new();
let blinded_route = BlindedRoute::new(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap();

nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedRoute(blinded_route), None).unwrap();
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedRoute(blinded_route), test_msg, None).unwrap();
pass_along_path(&nodes, None);
}

#[test]
fn three_blinded_hops() {
let nodes = create_nodes(4);
let test_msg = OnionMessageContents::Custom(TestCustomMessage {});

let secp_ctx = Secp256k1::new();
let blinded_route = BlindedRoute::new(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();

nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), None).unwrap();
nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), test_msg, None).unwrap();
pass_along_path(&nodes, None);
}

#[test]
fn too_big_packet_error() {
// Make sure we error as expected if a packet is too big to send.
let nodes = create_nodes(2);
let test_msg = OnionMessageContents::Custom(TestCustomMessage {});

let hop_node_id = nodes[1].get_node_pk();
let hops = [hop_node_id; 400];
let err = nodes[0].messenger.send_onion_message(&hops, Destination::Node(hop_node_id), None).unwrap_err();
let err = nodes[0].messenger.send_onion_message(&hops, Destination::Node(hop_node_id), test_msg, None).unwrap_err();
assert_eq!(err, SendError::TooBigPacket);
}

#[test]
fn invalid_blinded_route_error() {
// Make sure we error as expected if a provided blinded route has 0 or 1 hops.
let nodes = create_nodes(3);
let test_msg = TestCustomMessage {};

// 0 hops
let secp_ctx = Secp256k1::new();
let mut blinded_route = BlindedRoute::new(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
blinded_route.blinded_hops.clear();
let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), None).unwrap_err();
let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), OnionMessageContents::Custom(test_msg.clone()), None).unwrap_err();
assert_eq!(err, SendError::TooFewBlindedHops);

// 1 hop
let mut blinded_route = BlindedRoute::new(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
blinded_route.blinded_hops.remove(0);
assert_eq!(blinded_route.blinded_hops.len(), 1);
let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), None).unwrap_err();
let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), OnionMessageContents::Custom(test_msg), None).unwrap_err();
assert_eq!(err, SendError::TooFewBlindedHops);
}

#[test]
fn reply_path() {
let nodes = create_nodes(4);
let test_msg = TestCustomMessage {};
let secp_ctx = Secp256k1::new();

// Destination::Node
let reply_path = BlindedRoute::new(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::Node(nodes[3].get_node_pk()), Some(reply_path)).unwrap();
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::Node(nodes[3].get_node_pk()), OnionMessageContents::Custom(test_msg.clone()), Some(reply_path)).unwrap();
pass_along_path(&nodes, None);
// Make sure the last node successfully decoded the reply path.
nodes[3].logger.assert_log_contains(
"lightning::onion_message::messenger".to_string(),
format!("Received an onion message with path_id: None and reply_path").to_string(), 1);
format!("Received an onion message with path_id None and a reply_path").to_string(), 1);

// Destination::BlindedRoute
let blinded_route = BlindedRoute::new(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
let reply_path = BlindedRoute::new(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();

nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), Some(reply_path)).unwrap();
nodes[0].messenger.send_onion_message(&[], Destination::BlindedRoute(blinded_route), OnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap();
pass_along_path(&nodes, None);
nodes[3].logger.assert_log_contains(
"lightning::onion_message::messenger".to_string(),
format!("Received an onion message with path_id: None and reply_path").to_string(), 2);
format!("Received an onion message with path_id None and a reply_path").to_string(), 2);
}

#[test]
fn invalid_custom_message_type() {
let nodes = create_nodes(2);

struct InvalidCustomMessage{}
impl CustomOnionMessageContents for InvalidCustomMessage {
fn tlv_type(&self) -> u64 {
// Onion message contents must have a TLV >= 64.
63
}
}

impl Writeable for InvalidCustomMessage {
fn write<W: Writer>(&self, _w: &mut W) -> Result<(), io::Error> { unreachable!() }
}

impl MaybeReadableArgs<u64> for InvalidCustomMessage {
fn read<R: io::Read>(_buffer: &mut R, _message_type: u64) -> Result<Option<Self>, DecodeError> where Self: Sized {
unreachable!()
}
}

let test_msg = OnionMessageContents::Custom(InvalidCustomMessage {});
let err = nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), test_msg, None).unwrap_err();
assert_eq!(err, SendError::InvalidMessage);
}

#[test]
fn peer_buffer_full() {
let nodes = create_nodes(2);
let test_msg = TestCustomMessage {};
for _ in 0..188 { // Based on MAX_PER_PEER_BUFFER_SIZE in OnionMessenger
nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), None).unwrap();
nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), OnionMessageContents::Custom(test_msg.clone()), None).unwrap();
}
let err = nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), None).unwrap_err();
let err = nodes[0].messenger.send_onion_message(&[], Destination::Node(nodes[1].get_node_pk()), OnionMessageContents::Custom(test_msg), None).unwrap_err();
assert_eq!(err, SendError::BufferFull);
}
Loading