Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit aaf2fcb

Browse files
authored
Merge pull request #4 from johncantrell97/lsps0
LSPS0 message handling
2 parents 8487be4 + d91037d commit aaf2fcb

File tree

9 files changed

+847
-5
lines changed

9 files changed

+847
-5
lines changed

.github/workflows/build.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ jobs:
66
build:
77
strategy:
88
matrix:
9+
platform: [ ubuntu-latest ]
910
toolchain: [ stable, beta ]
1011
include:
1112
- toolchain: stable
1213
check-fmt: true
13-
runs-on: ubuntu-latest
14+
- toolchain: 1.48.0
15+
platform: ubuntu-latest
16+
msrv: true
17+
18+
runs-on: ${{ matrix.platform }}
1419
steps:
1520
- name: Checkout source code
1621
uses: actions/checkout@v2
@@ -20,6 +25,19 @@ jobs:
2025
toolchain: ${{ matrix.toolchain }}
2126
override: true
2227
profile: minimal
28+
- name: Pin tokio for MSRV
29+
if: matrix.msrv
30+
run: cargo update -p tokio --precise "1.14.1" --verbose
31+
- name: Pin serde for MSRV
32+
if: matrix.msrv
33+
run: cargo update -p serde --precise "1.0.156" --verbose
34+
- name: Pin log for MSRV
35+
if: matrix.msrv
36+
run: cargo update -p log --precise "0.4.18" --verbose
37+
- name: Cargo check
38+
run: cargo check --release
39+
- name: Check documentation
40+
run: cargo doc --release
2341
- name: Build on Rust ${{ matrix.toolchain }}
2442
run: cargo build --verbose --color always
2543
- name: Check formatting

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/target
22
/Cargo.lock
3+
.vscode

Cargo.toml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ description = "Types and primitives to integrate a spec-compliant LSP with an LD
88
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
99

1010
[dependencies]
11-
lightning = { version = "0.0.114", features = ["max_level_trace", "std"] }
12-
lightning-invoice = { version = "0.22" }
13-
lightning-net-tokio = { version = "0.0.114" }
11+
lightning = { git = "https://github.com/lightningdevkit/rust-lightning.git", rev = "498f2331459d8031031ef151a44c90d700aa8c7e", features = ["max_level_trace", "std"] }
12+
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning.git", rev = "498f2331459d8031031ef151a44c90d700aa8c7e" }
13+
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning.git", rev = "498f2331459d8031031ef151a44c90d700aa8c7e" }
1414

15-
bitcoin = "0.29.2"
15+
bitcoin = "0.29.0"
16+
17+
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
18+
serde_json = "1.0"

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
#![deny(private_intra_doc_links)]
1616
#![allow(bare_trait_objects)]
1717
#![allow(ellipsis_inclusive_range_patterns)]
18+
#![allow(clippy::drop_non_drop)]
1819
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
1920

2021
mod channel_request;
2122
mod jit_channel;
2223
mod transport;
24+
mod utils;
25+
26+
pub use transport::message_handler::{LiquidityManager, LiquidityProviderConfig};

src/transport/message_handler.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
use crate::transport::msgs::{LSPSMessage, RawLSPSMessage, LSPS_MESSAGE_TYPE};
2+
use crate::transport::protocol::LSPS0MessageHandler;
3+
4+
use bitcoin::secp256k1::PublicKey;
5+
use lightning::ln::features::{InitFeatures, NodeFeatures};
6+
use lightning::ln::msgs::{ErrorAction, LightningError};
7+
use lightning::ln::peer_handler::CustomMessageHandler;
8+
use lightning::ln::wire::CustomMessageReader;
9+
use lightning::sign::EntropySource;
10+
use lightning::util::logger::Level;
11+
use lightning::util::ser::Readable;
12+
use std::collections::HashMap;
13+
use std::convert::TryFrom;
14+
use std::io;
15+
use std::ops::Deref;
16+
use std::sync::{Arc, Mutex};
17+
18+
const LSPS_FEATURE_BIT: usize = 729;
19+
20+
/// A trait used to implement a specific LSPS protocol.
21+
///
22+
/// The messages the protocol uses need to be able to be mapped
23+
/// from and into [`LSPSMessage`].
24+
pub(crate) trait ProtocolMessageHandler {
25+
type ProtocolMessage: TryFrom<LSPSMessage> + Into<LSPSMessage>;
26+
const PROTOCOL_NUMBER: Option<u16>;
27+
28+
fn handle_message(
29+
&self, message: Self::ProtocolMessage, counterparty_node_id: &PublicKey,
30+
) -> Result<(), LightningError>;
31+
}
32+
33+
/// A configuration for [`LiquidityManager`].
34+
///
35+
/// Allows end-user to configure options when using the [`LiquidityManager`]
36+
/// to provide liquidity services to clients.
37+
pub struct LiquidityProviderConfig;
38+
39+
/// The main interface into LSP functionality.
40+
///
41+
/// Should be used as a [`CustomMessageHandler`] for your
42+
/// [`lightning::ln::peer_handler::PeerManager`]'s [`lightning::ln::peer_handler::MessageHandler`].
43+
pub struct LiquidityManager<ES: Deref>
44+
where
45+
ES::Target: EntropySource,
46+
{
47+
pending_messages: Arc<Mutex<Vec<(PublicKey, LSPSMessage)>>>,
48+
request_id_to_method_map: Mutex<HashMap<String, String>>,
49+
lsps0_message_handler: LSPS0MessageHandler<ES>,
50+
provider_config: Option<LiquidityProviderConfig>,
51+
}
52+
53+
impl<ES: Deref> LiquidityManager<ES>
54+
where
55+
ES::Target: EntropySource,
56+
{
57+
/// Constructor for the LiquidityManager
58+
///
59+
/// Sets up the required protocol message handlers based on the given [`LiquidityProviderConfig`].
60+
pub fn new(entropy_source: ES, provider_config: Option<LiquidityProviderConfig>) -> Self {
61+
let pending_messages = Arc::new(Mutex::new(vec![]));
62+
63+
let lsps0_message_handler =
64+
LSPS0MessageHandler::new(entropy_source, vec![], Arc::clone(&pending_messages));
65+
66+
Self {
67+
pending_messages,
68+
request_id_to_method_map: Mutex::new(HashMap::new()),
69+
lsps0_message_handler,
70+
provider_config,
71+
}
72+
}
73+
74+
fn handle_lsps_message(
75+
&self, msg: LSPSMessage, sender_node_id: &PublicKey,
76+
) -> Result<(), lightning::ln::msgs::LightningError> {
77+
match msg {
78+
LSPSMessage::Invalid => {
79+
return Err(LightningError { err: format!("{} did not understand a message we previously sent, maybe they don't support a protocol we are trying to use?", sender_node_id), action: ErrorAction::IgnoreAndLog(Level::Error)});
80+
}
81+
LSPSMessage::LSPS0(msg) => {
82+
self.lsps0_message_handler.handle_message(msg, sender_node_id)?;
83+
}
84+
}
85+
Ok(())
86+
}
87+
88+
fn enqueue_message(&self, node_id: PublicKey, msg: LSPSMessage) {
89+
let mut pending_msgs = self.pending_messages.lock().unwrap();
90+
pending_msgs.push((node_id, msg));
91+
}
92+
}
93+
94+
impl<ES: Deref> CustomMessageReader for LiquidityManager<ES>
95+
where
96+
ES::Target: EntropySource,
97+
{
98+
type CustomMessage = RawLSPSMessage;
99+
100+
fn read<R: io::Read>(
101+
&self, message_type: u16, buffer: &mut R,
102+
) -> Result<Option<Self::CustomMessage>, lightning::ln::msgs::DecodeError> {
103+
match message_type {
104+
LSPS_MESSAGE_TYPE => Ok(Some(RawLSPSMessage::read(buffer)?)),
105+
_ => Ok(None),
106+
}
107+
}
108+
}
109+
110+
impl<ES: Deref> CustomMessageHandler for LiquidityManager<ES>
111+
where
112+
ES::Target: EntropySource,
113+
{
114+
fn handle_custom_message(
115+
&self, msg: Self::CustomMessage, sender_node_id: &PublicKey,
116+
) -> Result<(), lightning::ln::msgs::LightningError> {
117+
let mut request_id_to_method_map = self.request_id_to_method_map.lock().unwrap();
118+
119+
match LSPSMessage::from_str_with_id_map(&msg.payload, &mut request_id_to_method_map) {
120+
Ok(msg) => self.handle_lsps_message(msg, sender_node_id),
121+
Err(_) => {
122+
self.enqueue_message(*sender_node_id, LSPSMessage::Invalid);
123+
Ok(())
124+
}
125+
}
126+
}
127+
128+
fn get_and_clear_pending_msg(&self) -> Vec<(PublicKey, Self::CustomMessage)> {
129+
let mut request_id_to_method_map = self.request_id_to_method_map.lock().unwrap();
130+
self.pending_messages
131+
.lock()
132+
.unwrap()
133+
.drain(..)
134+
.map(|(public_key, lsps_message)| {
135+
if let Some((request_id, method_name)) = lsps_message.get_request_id_and_method() {
136+
request_id_to_method_map.insert(request_id, method_name);
137+
}
138+
(
139+
public_key,
140+
RawLSPSMessage { payload: serde_json::to_string(&lsps_message).unwrap() },
141+
)
142+
})
143+
.collect()
144+
}
145+
146+
fn provided_node_features(&self) -> NodeFeatures {
147+
let mut features = NodeFeatures::empty();
148+
149+
if self.provider_config.is_some() {
150+
features.set_optional_custom_bit(LSPS_FEATURE_BIT).unwrap();
151+
}
152+
153+
features
154+
}
155+
156+
fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures {
157+
let mut features = InitFeatures::empty();
158+
159+
if self.provider_config.is_some() {
160+
features.set_optional_custom_bit(LSPS_FEATURE_BIT).unwrap();
161+
}
162+
163+
features
164+
}
165+
}

src/transport/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@
88
// licenses.
99

1010
//! Types and primitives that implement the LSPS0: Transport Layer specification.
11+
12+
pub mod message_handler;
13+
pub mod msgs;
14+
pub mod protocol;

0 commit comments

Comments
 (0)