Skip to content

Commit 5e9e68a

Browse files
authored
Merge pull request #1832 from jkczyz/2022-11-composite-handler
Macro for composing custom message handlers
2 parents 2edb3f1 + 9876a08 commit 5e9e68a

File tree

7 files changed

+356
-12
lines changed

7 files changed

+356
-12
lines changed

.github/workflows/build.yml

+9
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ jobs:
3131
build-no-std: true
3232
build-futures: true
3333
build-tx-sync: true
34+
- toolchain: stable
35+
test-custom-message: true
3436
- toolchain: beta
3537
platform: macos-latest
3638
build-net-tokio: true
@@ -54,6 +56,8 @@ jobs:
5456
build-no-std: true
5557
build-futures: true
5658
build-tx-sync: true
59+
- toolchain: beta
60+
test-custom-message: true
5761
- toolchain: 1.41.1
5862
build-no-std: false
5963
test-log-variants: true
@@ -226,6 +230,11 @@ jobs:
226230
RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client
227231
RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client,rest-client
228232
RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client,rest-client,tokio
233+
- name: Test Custom Message Macros on Rust ${{ matrix.toolchain }}
234+
if: "matrix.test-custom-message"
235+
run: |
236+
cd lightning-custom-message
237+
cargo test --verbose --color always
229238
- name: Install deps for kcov
230239
if: matrix.coverage
231240
run: |

.gitignore

+1-1
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

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ members = [
1212
]
1313

1414
exclude = [
15+
"lightning-custom-message",
1516
"no-std-check",
1617
]
1718

lightning-custom-message/Cargo.toml

+18
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

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

0 commit comments

Comments
 (0)