diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f729cecc012..a8f0aa69d94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,6 +31,8 @@ jobs: build-no-std: true build-futures: true build-tx-sync: true + - toolchain: stable + test-custom-message: true - toolchain: beta platform: macos-latest build-net-tokio: true @@ -54,6 +56,8 @@ jobs: build-no-std: true build-futures: true build-tx-sync: true + - toolchain: beta + test-custom-message: true - toolchain: 1.41.1 build-no-std: false test-log-variants: true @@ -226,6 +230,11 @@ jobs: RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client,rest-client RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always --features rpc-client,rest-client,tokio + - name: Test Custom Message Macros on Rust ${{ matrix.toolchain }} + if: "matrix.test-custom-message" + run: | + cd lightning-custom-message + cargo test --verbose --color always - name: Install deps for kcov if: matrix.coverage run: | diff --git a/.gitignore b/.gitignore index d99c5a4ddf6..7a889b5b823 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,5 @@ Cargo.lock .idea lightning/target lightning/ldk-net_graph-*.bin +lightning-custom-message/target no-std-check/target - diff --git a/Cargo.toml b/Cargo.toml index e8565e7ac09..be76477f4c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ ] exclude = [ + "lightning-custom-message", "no-std-check", ] diff --git a/lightning-custom-message/Cargo.toml b/lightning-custom-message/Cargo.toml new file mode 100644 index 00000000000..657844c0c5c --- /dev/null +++ b/lightning-custom-message/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "lightning-custom-message" +version = "0.0.113" +authors = ["Jeffrey Czyz"] +license = "MIT OR Apache-2.0" +repository = "http://github.com/lightningdevkit/rust-lightning" +description = """ +Utilities for supporting custom peer-to-peer messages in LDK. +""" +edition = "2021" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +bitcoin = "0.29.0" +lightning = { version = "0.0.113", path = "../lightning" } diff --git a/lightning-custom-message/src/lib.rs b/lightning-custom-message/src/lib.rs new file mode 100644 index 00000000000..a6e43978d47 --- /dev/null +++ b/lightning-custom-message/src/lib.rs @@ -0,0 +1,310 @@ +//! Utilities for supporting custom peer-to-peer messages in LDK. +//! +//! [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. +//! +//! This crate provides the [`composite_custom_message_handler`] macro for easily composing +//! pre-defined custom message handlers into one handler. The resulting handler can be further +//! composed with other custom message handlers using the same macro. +//! +//! The following example demonstrates defining a `FooBarHandler` to compose separate handlers for +//! `Foo` and `Bar` messages, and further composing it with a handler for `Baz` messages. +//! +//!``` +//! # extern crate bitcoin; +//! extern crate lightning; +//! #[macro_use] +//! extern crate lightning_custom_message; +//! +//! # use bitcoin::secp256k1::PublicKey; +//! # use lightning::io; +//! # use lightning::ln::msgs::{DecodeError, LightningError}; +//! use lightning::ln::peer_handler::CustomMessageHandler; +//! use lightning::ln::wire::{CustomMessageReader, self}; +//! use lightning::util::ser::Writeable; +//! # use lightning::util::ser::Writer; +//! +//! // Assume that `FooHandler` and `BarHandler` are defined in one crate and `BazHandler` is +//! // defined in another crate, handling messages `Foo`, `Bar`, and `Baz`, respectively. +//! +//! #[derive(Debug)] +//! pub struct Foo; +//! +//! macro_rules! foo_type_id { +//! () => { 32768 } +//! } +//! +//! impl wire::Type for Foo { +//! fn type_id(&self) -> u16 { foo_type_id!() } +//! } +//! impl Writeable for Foo { +//! // ... +//! # fn write(&self, _: &mut W) -> Result<(), io::Error> { +//! # unimplemented!() +//! # } +//! } +//! +//! pub struct FooHandler; +//! +//! impl CustomMessageReader for FooHandler { +//! // ... +//! # type CustomMessage = Foo; +//! # fn read( +//! # &self, _message_type: u16, _buffer: &mut R +//! # ) -> Result, DecodeError> { +//! # unimplemented!() +//! # } +//! } +//! impl CustomMessageHandler for FooHandler { +//! // ... +//! # fn handle_custom_message( +//! # &self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey +//! # ) -> Result<(), LightningError> { +//! # unimplemented!() +//! # } +//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> { +//! # unimplemented!() +//! # } +//! } +//! +//! #[derive(Debug)] +//! pub struct Bar; +//! +//! macro_rules! bar_type_id { +//! () => { 32769 } +//! } +//! +//! impl wire::Type for Bar { +//! fn type_id(&self) -> u16 { bar_type_id!() } +//! } +//! impl Writeable for Bar { +//! // ... +//! # fn write(&self, _: &mut W) -> Result<(), io::Error> { +//! # unimplemented!() +//! # } +//! } +//! +//! pub struct BarHandler; +//! +//! impl CustomMessageReader for BarHandler { +//! // ... +//! # type CustomMessage = Bar; +//! # fn read( +//! # &self, _message_type: u16, _buffer: &mut R +//! # ) -> Result, DecodeError> { +//! # unimplemented!() +//! # } +//! } +//! impl CustomMessageHandler for BarHandler { +//! // ... +//! # fn handle_custom_message( +//! # &self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey +//! # ) -> Result<(), LightningError> { +//! # unimplemented!() +//! # } +//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> { +//! # unimplemented!() +//! # } +//! } +//! +//! #[derive(Debug)] +//! pub struct Baz; +//! +//! macro_rules! baz_type_id { +//! () => { 32770 } +//! } +//! +//! impl wire::Type for Baz { +//! fn type_id(&self) -> u16 { baz_type_id!() } +//! } +//! impl Writeable for Baz { +//! // ... +//! # fn write(&self, _: &mut W) -> Result<(), io::Error> { +//! # unimplemented!() +//! # } +//! } +//! +//! pub struct BazHandler; +//! +//! impl CustomMessageReader for BazHandler { +//! // ... +//! # type CustomMessage = Baz; +//! # fn read( +//! # &self, _message_type: u16, _buffer: &mut R +//! # ) -> Result, DecodeError> { +//! # unimplemented!() +//! # } +//! } +//! impl CustomMessageHandler for BazHandler { +//! // ... +//! # fn handle_custom_message( +//! # &self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey +//! # ) -> Result<(), LightningError> { +//! # unimplemented!() +//! # } +//! # fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> { +//! # unimplemented!() +//! # } +//! } +//! +//! # fn main() { +//! // The first crate may define a handler composing `FooHandler` and `BarHandler` and export the +//! // corresponding message type ids as a macro to use in further composition. +//! +//! composite_custom_message_handler!( +//! pub struct FooBarHandler { +//! foo: FooHandler, +//! bar: BarHandler, +//! } +//! +//! pub enum FooBarMessage { +//! Foo(foo_type_id!()), +//! Bar(bar_type_id!()), +//! } +//! ); +//! +//! #[macro_export] +//! macro_rules! foo_bar_type_ids { +//! () => { foo_type_id!() | bar_type_id!() } +//! } +//! +//! // Another crate can then define a handler further composing `FooBarHandler` with `BazHandler` +//! // and similarly export the composition of message type ids as a macro. +//! +//! composite_custom_message_handler!( +//! pub struct FooBarBazHandler { +//! foo_bar: FooBarHandler, +//! baz: BazHandler, +//! } +//! +//! pub enum FooBarBazMessage { +//! FooBar(foo_bar_type_ids!()), +//! Baz(baz_type_id!()), +//! } +//! ); +//! +//! #[macro_export] +//! macro_rules! foo_bar_baz_type_ids { +//! () => { foo_bar_type_ids!() | baz_type_id!() } +//! } +//! # } +//!``` +//! +//! [BOLT 1]: https://github.com/lightning/bolts/blob/master/01-messaging.md +//! [`CustomMessageHandler`]: crate::lightning::ln::peer_handler::CustomMessageHandler + +#![doc(test(no_crate_inject, attr(deny(warnings))))] + +pub extern crate bitcoin; +pub extern crate lightning; + +/// Defines a composite type implementing [`CustomMessageHandler`] (and therefore also implementing +/// [`CustomMessageReader`]), along with a corresponding enumerated custom message [`Type`], from +/// one or more previously defined custom message handlers. +/// +/// Useful for parameterizing [`PeerManager`] with custom message handling for one or more sets of +/// custom messages. Message type ids may be given as a valid `match` pattern, including ranges, +/// though using OR-ed literal patterns is preferred in order to catch unreachable code for +/// conflicting handlers. +/// +/// See [crate documentation] for example usage. +/// +/// [`CustomMessageHandler`]: crate::lightning::ln::peer_handler::CustomMessageHandler +/// [`CustomMessageReader`]: crate::lightning::ln::wire::CustomMessageReader +/// [`Type`]: crate::lightning::ln::wire::Type +/// [`PeerManager`]: crate::lightning::ln::peer_handler::PeerManager +/// [crate documentation]: self +#[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() + } + } + + impl $crate::lightning::ln::wire::CustomMessageReader for $handler { + type CustomMessage = $message; + fn read( + &self, message_type: u16, buffer: &mut R + ) -> Result, $crate::lightning::ln::msgs::DecodeError> { + match message_type { + $( + $pattern => match <$type>::read(&self.$field, message_type, buffer)? { + None => unreachable!(), + 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(&self, writer: &mut W) -> Result<(), $crate::lightning::io::Error> { + match self { + $( + Self::$variant(message) => message.write(writer), + )* + } + } + } + } +} diff --git a/lightning/src/ln/peer_handler.rs b/lightning/src/ln/peer_handler.rs index 3775c27e4df..8c3ab968af2 100644 --- a/lightning/src/ln/peer_handler.rs +++ b/lightning/src/ln/peer_handler.rs @@ -46,16 +46,23 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::sha256::HashEngine as Sha256Engine; use bitcoin::hashes::{HashEngine, Hash}; -/// Handler for BOLT1-compliant messages. +/// A handler provided to [`PeerManager`] for reading and handling custom messages. +/// +/// [BOLT 1] specifies a custom message type range for use with experimental or application-specific +/// messages. `CustomMessageHandler` allows for user-defined handling of such types. See the +/// [`lightning_custom_message`] crate for tools useful in composing more than one custom handler. +/// +/// [BOLT 1]: https://github.com/lightning/bolts/blob/master/01-messaging.md +/// [`lightning_custom_message`]: https://docs.rs/lightning_custom_message/latest/lightning_custom_message pub trait CustomMessageHandler: wire::CustomMessageReader { - /// Called with the message type that was received and the buffer to be read. - /// Can return a `MessageHandlingError` if the message could not be handled. + /// Handles the given message sent from `sender_node_id`, possibly producing messages for + /// [`CustomMessageHandler::get_and_clear_pending_msg`] to return and thus for [`PeerManager`] + /// to send. fn handle_custom_message(&self, msg: Self::CustomMessage, sender_node_id: &PublicKey) -> Result<(), LightningError>; - /// Gets the list of pending messages which were generated by the custom message - /// handler, clearing the list in the process. The first tuple element must - /// correspond to the intended recipients node ids. If no connection to one of the - /// specified node does not exist, the message is simply not sent to it. + /// Returns the list of pending messages that were generated by the handler, clearing the list + /// in the process. Each message is paired with the node id of the intended recipient. If no + /// connection to the node exists, then the message is simply not sent. fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)>; } diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 497628607ce..f7656e7949a 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -43,15 +43,14 @@ use crate::prelude::*; /// # extern crate bitcoin; /// # use bitcoin::hashes::_export::_core::time::Duration; /// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; -/// # use lightning::chain::keysinterface::{InMemorySigner, KeysManager}; -/// # use lightning::ln::msgs::DecodeError; +/// # use lightning::chain::keysinterface::KeysManager; /// # use lightning::ln::peer_handler::IgnoringMessageHandler; /// # use lightning::onion_message::{BlindedPath, CustomOnionMessageContents, Destination, OnionMessageContents, OnionMessenger}; /// # use lightning::util::logger::{Logger, Record}; /// # use lightning::util::ser::{Writeable, Writer}; /// # use lightning::io; /// # use std::sync::Arc; -/// # struct FakeLogger {}; +/// # struct FakeLogger; /// # impl Logger for FakeLogger { /// # fn log(&self, record: &Record) { unimplemented!() } /// # } @@ -67,7 +66,7 @@ use crate::prelude::*; /// # let your_custom_message_handler = IgnoringMessageHandler {}; /// // Create the onion messenger. This must use the same `keys_manager` as is passed to your /// // ChannelManager. -/// let onion_messenger = OnionMessenger::new(&keys_manager, &keys_manager, logger, your_custom_message_handler); +/// let onion_messenger = OnionMessenger::new(&keys_manager, &keys_manager, logger, &your_custom_message_handler); /// /// # struct YourCustomMessage {} /// impl Writeable for YourCustomMessage {