Skip to content

Commit 6d60e7f

Browse files
committed
Macro for composing custom message handlers
BOLT 1 specifies a custom message type range for use with experimental or application-specific messages. While a `CustomMessageHandler` can be defined to support more than one message type, defining such a handler requires a significant amount of boilerplate and can be error prone. Add a crate exporting a `composite_custom_message_handler` macro for easily composing pre-defined custom message handlers. The resulting handler can be further composed with other custom message handlers using the same macro. This requires a separate crate since the macro needs to support "or" patterns in macro_rules, which is only available in edition 2021. https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html Otherwise, a crate defining a handler for a set of custom messages could not easily be reused with another custom message handler. Doing so would require explicitly duplicating the reused handlers type ids, but those may change when the crate is updated.
1 parent f0c181e commit 6d60e7f

File tree

4 files changed

+315
-1
lines changed

4 files changed

+315
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ Cargo.lock
99
.idea
1010
lightning/target
1111
lightning/ldk-net_graph-*.bin
12+
lightning-custom-message/target
1213
no-std-check/target
13-

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
]
1212

1313
exclude = [
14+
"lightning-custom-message",
1415
"no-std-check",
1516
]
1617

lightning-custom-message/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "lightning-custom-message"
3+
version = "0.0.113"
4+
authors = ["Jeffrey Czyz"]
5+
license = "MIT OR Apache-2.0"
6+
repository = "http://github.com/lightningdevkit/rust-lightning"
7+
description = """
8+
Utilities for supporting custom peer-to-peer messages in LDK.
9+
"""
10+
edition = "2021"
11+
12+
[package.metadata.docs.rs]
13+
all-features = true
14+
rustdoc-args = ["--cfg", "docsrs"]
15+
16+
[dependencies]
17+
bitcoin = "0.29.0"
18+
lightning = { version = "0.0.113", path = "../lightning" }

lightning-custom-message/src/lib.rs

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
//! Utilities for supporting custom peer-to-peer messages in LDK.
2+
//!
3+
//! [BOLT 1] specifies a custom message type range for use with experimental or application-specific
4+
//! messages. While a [`CustomMessageHandler`] can be defined to support more than one message type,
5+
//! defining such a handler requires a significant amount of boilerplate and can be error prone.
6+
//!
7+
//! This crate provides the [`composite_custom_message_handler`] macro for easily composing
8+
//! pre-defined custom message handlers. The resulting handler can be further composed with other
9+
//! custom message handlers using the same macro.
10+
//!
11+
//! The following example demonstrates defining a `FooBarHandler` to compose separate handlers for
12+
//! `Foo` and `Bar` messages, and further composing it with a handler for `Baz` messages.
13+
//!
14+
//!```
15+
//! # extern crate bitcoin;
16+
//! extern crate lightning;
17+
//! #[macro_use]
18+
//! extern crate lightning_custom_message;
19+
//!
20+
//! # use bitcoin::secp256k1::PublicKey;
21+
//! # use lightning::io;
22+
//! # use lightning::ln::msgs::{DecodeError, LightningError};
23+
//! use lightning::ln::peer_handler::CustomMessageHandler;
24+
//! use lightning::ln::wire::{CustomMessageReader, self};
25+
//! use lightning::util::ser::Writeable;
26+
//! # use lightning::util::ser::Writer;
27+
//!
28+
//! #[derive(Debug)]
29+
//! pub struct Foo;
30+
//!
31+
//! macro_rules! foo_type_id {
32+
//! () => { 32768 }
33+
//! }
34+
//!
35+
//! impl wire::Type for Foo {
36+
//! fn type_id(&self) -> u16 { foo_type_id!() }
37+
//! }
38+
//! impl Writeable for Foo {
39+
//! // ...
40+
//! # fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
41+
//! # unimplemented!()
42+
//! # }
43+
//! }
44+
//!
45+
//! pub struct FooHandler;
46+
//!
47+
//! impl CustomMessageReader for FooHandler {
48+
//! // ...
49+
//! # type CustomMessage = Foo;
50+
//! # fn read<R: io::Read>(
51+
//! # &self, _message_type: u16, _buffer: &mut R
52+
//! # ) -> Result<Option<Self::CustomMessage>, DecodeError> {
53+
//! # unimplemented!()
54+
//! # }
55+
//! }
56+
//! impl CustomMessageHandler for FooHandler {
57+
//! // ...
58+
//! # fn handle_custom_message(
59+
//! # &self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey
60+
//! # ) -> Result<(), LightningError> {
61+
//! # unimplemented!()
62+
//! # }
63+
//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
64+
//! # unimplemented!()
65+
//! # }
66+
//! }
67+
//!
68+
//! #[derive(Debug)]
69+
//! pub struct Bar;
70+
//!
71+
//! macro_rules! bar_type_id {
72+
//! () => { 32769 }
73+
//! }
74+
//!
75+
//! impl wire::Type for Bar {
76+
//! fn type_id(&self) -> u16 { bar_type_id!() }
77+
//! }
78+
//! impl Writeable for Bar {
79+
//! // ...
80+
//! # fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
81+
//! # unimplemented!()
82+
//! # }
83+
//! }
84+
//!
85+
//! pub struct BarHandler;
86+
//!
87+
//! impl CustomMessageReader for BarHandler {
88+
//! // ...
89+
//! # type CustomMessage = Bar;
90+
//! # fn read<R: io::Read>(
91+
//! # &self, _message_type: u16, _buffer: &mut R
92+
//! # ) -> Result<Option<Self::CustomMessage>, DecodeError> {
93+
//! # unimplemented!()
94+
//! # }
95+
//! }
96+
//! impl CustomMessageHandler for BarHandler {
97+
//! // ...
98+
//! # fn handle_custom_message(
99+
//! # &self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey
100+
//! # ) -> Result<(), LightningError> {
101+
//! # unimplemented!()
102+
//! # }
103+
//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
104+
//! # unimplemented!()
105+
//! # }
106+
//! }
107+
//!
108+
//! #[derive(Debug)]
109+
//! pub struct Baz;
110+
//!
111+
//! macro_rules! baz_type_id {
112+
//! () => { 32770 }
113+
//! }
114+
//!
115+
//! impl wire::Type for Baz {
116+
//! fn type_id(&self) -> u16 { baz_type_id!() }
117+
//! }
118+
//! impl Writeable for Baz {
119+
//! // ...
120+
//! # fn write<W: Writer>(&self, _: &mut W) -> Result<(), io::Error> {
121+
//! # unimplemented!()
122+
//! # }
123+
//! }
124+
//!
125+
//! pub struct BazHandler;
126+
//!
127+
//! impl CustomMessageReader for BazHandler {
128+
//! // ...
129+
//! # type CustomMessage = Baz;
130+
//! # fn read<R: io::Read>(
131+
//! # &self, _message_type: u16, _buffer: &mut R
132+
//! # ) -> Result<Option<Self::CustomMessage>, DecodeError> {
133+
//! # unimplemented!()
134+
//! # }
135+
//! }
136+
//! impl CustomMessageHandler for BazHandler {
137+
//! // ...
138+
//! # fn handle_custom_message(
139+
//! # &self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey
140+
//! # ) -> Result<(), LightningError> {
141+
//! # unimplemented!()
142+
//! # }
143+
//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
144+
//! # unimplemented!()
145+
//! # }
146+
//! }
147+
//!
148+
//! # fn main() {
149+
//! composite_custom_message_handler!(
150+
//! pub struct FooBarHandler {
151+
//! foo: FooHandler,
152+
//! bar: BarHandler,
153+
//! }
154+
//!
155+
//! pub enum FooBarMessage {
156+
//! Foo(foo_type_id!()),
157+
//! Bar(bar_type_id!()),
158+
//! }
159+
//! );
160+
//!
161+
//! macro_rules! foo_bar_type_ids {
162+
//! () => { foo_type_id!() | bar_type_id!() }
163+
//! }
164+
//!
165+
//! composite_custom_message_handler!(
166+
//! pub struct FooBarBazHandler {
167+
//! foo_bar: FooBarHandler,
168+
//! baz: BazHandler,
169+
//! }
170+
//!
171+
//! pub enum FooBarBazMessage {
172+
//! FooBar(foo_bar_type_ids!()),
173+
//! Baz(baz_type_id!()),
174+
//! }
175+
//! );
176+
//! # }
177+
//!```
178+
//!
179+
//! [BOLT 1]: https://github.com/lightning/bolts/blob/master/01-messaging.md
180+
//! [`CustomMessageHandler`]: crate::lightning::ln::peer_handler::CustomMessageHandler
181+
182+
#![doc(test(no_crate_inject, attr(deny(warnings))))]
183+
184+
pub extern crate bitcoin;
185+
pub extern crate lightning;
186+
187+
/// Defines a composite type implementing [`CustomMessageHandler`] (and therefore also implementing
188+
/// [`CustomMessageReader`]), along with a corresponding enumerated custom message [`Type`], from
189+
/// one or more previously defined custom message handlers.
190+
///
191+
/// Useful for parameterizing [`PeerManager`] with custom message handling for one or more sets of
192+
/// custom messages. Message type ids may be given as a valid `match` pattern, including ranges,
193+
/// though using OR-ed literal patterns is preferred in order to catch unreachable code for
194+
/// conflicting handlers.
195+
///
196+
/// See [crate documentation] for example usage.
197+
///
198+
/// [`CustomMessageHandler`]: crate::lightning::ln::peer_handler::CustomMessageHandler
199+
/// [`CustomMessageReader`]: crate::lightning::ln::wire::CustomMessageReader
200+
/// [`Type`]: crate::lightning::ln::wire::Type
201+
/// [`PeerManager`]: crate::lightning::ln::peer_handler::PeerManager
202+
/// [crate documentation]: self
203+
#[macro_export]
204+
macro_rules! composite_custom_message_handler {
205+
(
206+
$handler_visibility:vis struct $handler:ident {
207+
$($field_visibility:vis $field:ident: $type:ty),* $(,)*
208+
}
209+
210+
$message_visibility:vis enum $message:ident {
211+
$($variant:ident($pattern:pat)),* $(,)*
212+
}
213+
) => {
214+
#[allow(missing_docs)]
215+
$handler_visibility struct $handler {
216+
$(
217+
$field_visibility $field: $type,
218+
)*
219+
}
220+
221+
#[allow(missing_docs)]
222+
#[derive(Debug)]
223+
$message_visibility enum $message {
224+
$(
225+
$variant(<$type as $crate::lightning::ln::wire::CustomMessageReader>::CustomMessage),
226+
)*
227+
}
228+
229+
impl $crate::lightning::ln::peer_handler::CustomMessageHandler for $handler {
230+
fn handle_custom_message(
231+
&self, msg: Self::CustomMessage, sender_node_id: &$crate::bitcoin::secp256k1::PublicKey
232+
) -> Result<(), $crate::lightning::ln::msgs::LightningError> {
233+
match msg {
234+
$(
235+
$message::$variant(message) => {
236+
$crate::lightning::ln::peer_handler::CustomMessageHandler::handle_custom_message(
237+
&self.$field, message, sender_node_id
238+
)
239+
},
240+
)*
241+
}
242+
}
243+
244+
fn get_and_clear_pending_msg(&self) -> Vec<($crate::bitcoin::secp256k1::PublicKey, Self::CustomMessage)> {
245+
vec![].into_iter()
246+
$(
247+
.chain(
248+
self.$field
249+
.get_and_clear_pending_msg()
250+
.into_iter()
251+
.map(|(pubkey, message)| (pubkey, $message::$variant(message)))
252+
)
253+
)*
254+
.collect()
255+
}
256+
}
257+
258+
impl $crate::lightning::ln::wire::CustomMessageReader for $handler {
259+
type CustomMessage = $message;
260+
fn read<R: $crate::lightning::io::Read>(
261+
&self, message_type: u16, buffer: &mut R
262+
) -> Result<Option<Self::CustomMessage>, $crate::lightning::ln::msgs::DecodeError> {
263+
match message_type {
264+
$(
265+
$pattern => match <$type>::read(&self.$field, message_type, buffer)? {
266+
None => unreachable!(),
267+
Some(message) => Ok(Some($message::$variant(message))),
268+
},
269+
)*
270+
_ => Ok(None),
271+
}
272+
}
273+
}
274+
275+
impl $crate::lightning::ln::wire::Type for $message {
276+
fn type_id(&self) -> u16 {
277+
match self {
278+
$(
279+
Self::$variant(message) => message.type_id(),
280+
)*
281+
}
282+
}
283+
}
284+
285+
impl $crate::lightning::util::ser::Writeable for $message {
286+
fn write<W: $crate::lightning::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::lightning::io::Error> {
287+
match self {
288+
$(
289+
Self::$variant(message) => message.write(writer),
290+
)*
291+
}
292+
}
293+
}
294+
}
295+
}

0 commit comments

Comments
 (0)