#![doc(test(no_crate_inject, attr(deny(warnings))))]
pub extern crate bitcoin;
pub extern crate lightning;
#[macro_export]
macro_rules! composite_custom_message_handler {
(
$handler_visibility:vis struct $handler:ident {
$($field_visibility:vis $field:ident: $type:ty),* $(,)*
}
$message_visibility:vis enum $message:ident {
$($variant:ident($pattern:pat)),* $(,)*
}
) => {
#[allow(missing_docs)]
$handler_visibility struct $handler {
$(
$field_visibility $field: $type,
)*
}
#[allow(missing_docs)]
#[derive(Debug)]
$message_visibility enum $message {
$(
$variant(<$type as $crate::lightning::ln::wire::CustomMessageReader>::CustomMessage),
)*
}
impl $crate::lightning::ln::peer_handler::CustomMessageHandler for $handler {
fn handle_custom_message(
&self, msg: Self::CustomMessage, sender_node_id: $crate::bitcoin::secp256k1::PublicKey
) -> Result<(), $crate::lightning::ln::msgs::LightningError> {
match msg {
$(
$message::$variant(message) => {
$crate::lightning::ln::peer_handler::CustomMessageHandler::handle_custom_message(
&self.$field, message, sender_node_id
)
},
)*
}
}
fn get_and_clear_pending_msg(&self) -> Vec<($crate::bitcoin::secp256k1::PublicKey, Self::CustomMessage)> {
vec![].into_iter()
$(
.chain(
self.$field
.get_and_clear_pending_msg()
.into_iter()
.map(|(pubkey, message)| (pubkey, $message::$variant(message)))
)
)*
.collect()
}
fn peer_disconnected(&self, their_node_id: $crate::bitcoin::secp256k1::PublicKey) {
$(
self.$field.peer_disconnected(their_node_id);
)*
}
fn peer_connected(&self, their_node_id: $crate::bitcoin::secp256k1::PublicKey, msg: &$crate::lightning::ln::msgs::Init, inbound: bool) -> Result<(), ()> {
$(
let $field = self.$field.peer_connected(their_node_id, msg, inbound);
)*
let any_err = false $( || $field.is_err() )*;
if any_err {
$(
if $field.is_ok() {
self.$field.peer_disconnected(their_node_id);
}
)*
Err(())
} else {
Ok(())
}
}
fn provided_node_features(&self) -> $crate::lightning::types::features::NodeFeatures {
$crate::lightning::types::features::NodeFeatures::empty()
$(
| self.$field.provided_node_features()
)*
}
fn provided_init_features(
&self, their_node_id: $crate::bitcoin::secp256k1::PublicKey
) -> $crate::lightning::types::features::InitFeatures {
$crate::lightning::types::features::InitFeatures::empty()
$(
| self.$field.provided_init_features(their_node_id)
)*
}
}
impl $crate::lightning::ln::wire::CustomMessageReader for $handler {
type CustomMessage = $message;
fn read<R: $crate::lightning::util::ser::LengthLimitedRead>(
&self, message_type: u16, buffer: &mut R
) -> Result<Option<Self::CustomMessage>, $crate::lightning::ln::msgs::DecodeError> {
match message_type {
$(
$pattern => match <$type>::read(&self.$field, message_type, buffer)? {
None => Ok(None),
Some(message) => Ok(Some($message::$variant(message))),
},
)*
_ => Ok(None),
}
}
}
impl $crate::lightning::ln::wire::Type for $message {
fn type_id(&self) -> u16 {
match self {
$(
Self::$variant(message) => message.type_id(),
)*
}
}
}
impl $crate::lightning::util::ser::Writeable for $message {
fn write<W: $crate::lightning::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::lightning::io::Error> {
match self {
$(
Self::$variant(message) => message.write(writer),
)*
}
}
}
}
}
#[cfg(test)]
mod tests {
use bitcoin::secp256k1::PublicKey;
use core::sync::atomic::{AtomicUsize, Ordering};
use lightning::io;
use lightning::ln::msgs::{DecodeError, Init, LightningError};
use lightning::ln::peer_handler::CustomMessageHandler;
use lightning::ln::wire::{CustomMessageReader, Type};
use lightning::types::features::{InitFeatures, NodeFeatures};
use lightning::util::ser::{LengthLimitedRead, Writeable, Writer};
#[derive(Debug)]
pub struct Foo;
impl Type for Foo {
fn type_id(&self) -> u16 {
32768
}
}
impl Writeable for Foo {
fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
Ok(())
}
}
pub struct CountingHandler {
pub connect_count: AtomicUsize,
}
impl CustomMessageReader for CountingHandler {
type CustomMessage = Foo;
fn read<R: LengthLimitedRead>(
&self, _t: u16, _b: &mut R,
) -> Result<Option<Foo>, DecodeError> {
Ok(None)
}
}
impl CustomMessageHandler for CountingHandler {
fn handle_custom_message(&self, _msg: Foo, _: PublicKey) -> Result<(), LightningError> {
Ok(())
}
fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Foo)> {
vec![]
}
fn peer_disconnected(&self, _: PublicKey) {
self.connect_count.fetch_sub(1, Ordering::SeqCst);
}
fn peer_connected(&self, _: PublicKey, _: &Init, _: bool) -> Result<(), ()> {
self.connect_count.fetch_add(1, Ordering::SeqCst);
Ok(())
}
fn provided_node_features(&self) -> NodeFeatures {
NodeFeatures::empty()
}
fn provided_init_features(&self, _: PublicKey) -> InitFeatures {
InitFeatures::empty()
}
}
#[derive(Debug)]
pub struct Bar;
impl Type for Bar {
fn type_id(&self) -> u16 {
32769
}
}
impl Writeable for Bar {
fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
Ok(())
}
}
pub struct ErroringHandler;
impl CustomMessageReader for ErroringHandler {
type CustomMessage = Bar;
fn read<R: LengthLimitedRead>(
&self, _t: u16, _b: &mut R,
) -> Result<Option<Bar>, DecodeError> {
Ok(None)
}
}
impl CustomMessageHandler for ErroringHandler {
fn handle_custom_message(&self, _msg: Bar, _: PublicKey) -> Result<(), LightningError> {
Ok(())
}
fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Bar)> {
vec![]
}
fn peer_disconnected(&self, _: PublicKey) {
debug_assert!(false);
}
fn peer_connected(&self, _: PublicKey, _: &Init, _: bool) -> Result<(), ()> {
Err(())
}
fn provided_node_features(&self) -> NodeFeatures {
NodeFeatures::empty()
}
fn provided_init_features(&self, _: PublicKey) -> InitFeatures {
InitFeatures::empty()
}
}
composite_custom_message_handler!(
pub struct CompositeHandler {
counting: CountingHandler,
erroring: ErroringHandler,
}
pub enum CompositeMessage {
Foo(32768),
Bar(32769),
}
);
struct ReservedBlockHandler;
impl CustomMessageReader for ReservedBlockHandler {
type CustomMessage = Foo;
fn read<R: LengthLimitedRead>(
&self, message_type: u16, _b: &mut R,
) -> Result<Option<Foo>, DecodeError> {
match message_type {
32768 => Ok(Some(Foo)),
_ => Ok(None),
}
}
}
impl CustomMessageHandler for ReservedBlockHandler {
fn handle_custom_message(&self, _msg: Foo, _: PublicKey) -> Result<(), LightningError> {
Ok(())
}
fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Foo)> {
vec![]
}
fn peer_disconnected(&self, _: PublicKey) {}
fn peer_connected(&self, _: PublicKey, _: &Init, _: bool) -> Result<(), ()> {
Ok(())
}
fn provided_node_features(&self) -> NodeFeatures {
NodeFeatures::empty()
}
fn provided_init_features(&self, _: PublicKey) -> InitFeatures {
InitFeatures::empty()
}
}
composite_custom_message_handler!(
struct ReservedBlockComposite {
proto: ReservedBlockHandler,
}
enum ReservedBlockMessage {
Proto(32768..=32777),
}
);
#[test]
fn read_treats_a_reserved_in_range_type_as_unknown() {
let composite = ReservedBlockComposite { proto: ReservedBlockHandler };
let mut buffer: &[u8] = &[];
assert!(matches!(
composite.read(32768, &mut buffer),
Ok(Some(ReservedBlockMessage::Proto(_)))
));
assert!(matches!(composite.read(32770, &mut buffer), Ok(None)));
}
#[test]
fn peer_connected_failure_does_not_leak_subhandler_state() {
let composite = CompositeHandler {
counting: CountingHandler { connect_count: AtomicUsize::new(0) },
erroring: ErroringHandler,
};
let pk_bytes = [
0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE,
0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81,
0x5B, 0x16, 0xF8, 0x17, 0x98,
];
let pk = PublicKey::from_slice(&pk_bytes).unwrap();
let init =
Init { features: InitFeatures::empty(), networks: None, remote_network_address: None };
let result = composite.peer_connected(pk, &init, true);
assert!(result.is_err(), "Composite must propagate the inner Err");
let leaked = composite.counting.connect_count.load(Ordering::SeqCst);
assert_eq!(
leaked, 0,
"CountingHandler tracked {leaked} connected peer(s) after the composite \
returned Err; this state will never be cleaned up because per the trait \
contract peer_disconnected won't be called when peer_connected returns Err.",
);
}
}