Skip to content

Commit 4983a27

Browse files
committed
Add lightning-liquidity crate to the workspace
We upstream the `lightning-liquidity` into the `rust-lightning` workspace. Files are copied over as per commit c80eb75f5a31bea5c2b73e41c50ca382ec0020f8.
1 parent 797993c commit 4983a27

32 files changed

+7835
-0
lines changed

lightning-liquidity/Cargo.toml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
[package]
2+
name = "lightning-liquidity"
3+
version = "0.1.0-alpha.6"
4+
authors = ["John Cantrell <johncantrell97@gmail.com>", "Elias Rohrer <dev@tnull.de>"]
5+
homepage = "https://lightningdevkit.org/"
6+
license = "MIT OR Apache-2.0"
7+
edition = "2021"
8+
description = "Types and primitives to integrate a spec-compliant LSP with an LDK-based node."
9+
repository = "https://github.com/lightningdevkit/lightning-liquidity/"
10+
readme = "README.md"
11+
keywords = ["bitcoin", "lightning", "ldk", "bdk"]
12+
categories = ["cryptography::cryptocurrencies"]
13+
14+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
15+
16+
[features]
17+
default = ["std"]
18+
std = ["lightning/std", "bitcoin/std", "lightning-invoice/std"]
19+
no-std = ["hashbrown", "lightning/no-std"]
20+
21+
[dependencies]
22+
lightning = { version = "0.0.125", default-features = false, features = ["max_level_trace"] }
23+
lightning-types = { version = "0.1", default-features = false }
24+
lightning-invoice = { version = "0.32.0", default-features = false, features = ["serde"] }
25+
#lightning = { path = "../rust-lightning/lightning", default-features = false, features = ["max_level_trace"] }
26+
#lightning-types = { path = "../rust-lightning/lightning-types", default-features = false }
27+
#lightning-invoice = { path = "../rust-lightning/lightning-invoice", default-features = false, features = ["serde"] }
28+
29+
bitcoin = { version = "0.32.2", default-features = false, features = ["serde"] }
30+
hashbrown = { version = "0.8", optional = true }
31+
32+
chrono = { version = "0.4", default-features = false, features = ["serde", "alloc"] }
33+
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
34+
serde_json = "1.0"
35+
36+
[dev-dependencies]
37+
lightning = { version = "0.0.125", default-features = false, features = ["_test_utils"] }
38+
lightning-persister = { version = "0.0.125", default-features = false }
39+
lightning-background-processor = { version = "0.0.125", default-features = false, features = ["std"] }
40+
# lightning = { path = "../rust-lightning/lightning", default-features = false, features = ["max_level_trace", "_test_utils"] }
41+
# lightning-persister = { path = "../rust-lightning/lightning-persister", default-features = false }
42+
# lightning-background-processor = { path = "../rust-lightning/lightning-background-processor", default-features = false, features = ["std"] }
43+
44+
proptest = "1.0.0"
45+
tokio = { version = "1.35", default-features = false, features = [ "rt-multi-thread", "time", "sync", "macros" ] }
46+
47+
[lints.rust.unexpected_cfgs]
48+
level = "forbid"
49+
# When adding a new cfg attribute, ensure that it is added to this list.
50+
check-cfg = [
51+
"cfg(lsps1_service)",
52+
]

lightning-liquidity/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# `lightning-liquidity`
2+
3+
Types and primitives to integrate a [spec-compliant](https://github.com/BitcoinAndLightningLayerSpecs/lsp) LSP with an LDK-based node.

lightning-liquidity/src/events.rs

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! Events are surfaced by the library to indicate some action must be taken
11+
//! by the end-user.
12+
//!
13+
//! Because we don't have a built-in runtime, it's up to the end-user to poll
14+
//! [`LiquidityManager::get_and_clear_pending_events`] to receive events.
15+
//!
16+
//! [`LiquidityManager::get_and_clear_pending_events`]: crate::LiquidityManager::get_and_clear_pending_events
17+
18+
use crate::lsps0;
19+
use crate::lsps1;
20+
use crate::lsps2;
21+
use crate::prelude::{Vec, VecDeque};
22+
use crate::sync::{Arc, Mutex};
23+
24+
use core::future::Future;
25+
use core::task::{Poll, Waker};
26+
27+
pub(crate) struct EventQueue {
28+
queue: Arc<Mutex<VecDeque<Event>>>,
29+
waker: Arc<Mutex<Option<Waker>>>,
30+
#[cfg(feature = "std")]
31+
condvar: std::sync::Condvar,
32+
}
33+
34+
impl EventQueue {
35+
pub fn new() -> Self {
36+
let queue = Arc::new(Mutex::new(VecDeque::new()));
37+
let waker = Arc::new(Mutex::new(None));
38+
#[cfg(feature = "std")]
39+
{
40+
let condvar = std::sync::Condvar::new();
41+
Self { queue, waker, condvar }
42+
}
43+
#[cfg(not(feature = "std"))]
44+
Self { queue, waker }
45+
}
46+
47+
pub fn enqueue(&self, event: Event) {
48+
{
49+
let mut queue = self.queue.lock().unwrap();
50+
queue.push_back(event);
51+
}
52+
53+
if let Some(waker) = self.waker.lock().unwrap().take() {
54+
waker.wake();
55+
}
56+
#[cfg(feature = "std")]
57+
self.condvar.notify_one();
58+
}
59+
60+
pub fn next_event(&self) -> Option<Event> {
61+
self.queue.lock().unwrap().pop_front()
62+
}
63+
64+
pub async fn next_event_async(&self) -> Event {
65+
EventFuture { event_queue: Arc::clone(&self.queue), waker: Arc::clone(&self.waker) }.await
66+
}
67+
68+
#[cfg(feature = "std")]
69+
pub fn wait_next_event(&self) -> Event {
70+
let mut queue =
71+
self.condvar.wait_while(self.queue.lock().unwrap(), |queue| queue.is_empty()).unwrap();
72+
73+
let event = queue.pop_front().expect("non-empty queue");
74+
let should_notify = !queue.is_empty();
75+
76+
drop(queue);
77+
78+
if should_notify {
79+
if let Some(waker) = self.waker.lock().unwrap().take() {
80+
waker.wake();
81+
}
82+
83+
self.condvar.notify_one();
84+
}
85+
86+
event
87+
}
88+
89+
pub fn get_and_clear_pending_events(&self) -> Vec<Event> {
90+
self.queue.lock().unwrap().drain(..).collect()
91+
}
92+
}
93+
94+
/// An event which you should probably take some action in response to.
95+
#[derive(Debug, Clone, PartialEq, Eq)]
96+
pub enum Event {
97+
/// An LSPS0 client event.
98+
LSPS0Client(lsps0::event::LSPS0ClientEvent),
99+
/// An LSPS1 (Channel Request) client event.
100+
LSPS1Client(lsps1::event::LSPS1ClientEvent),
101+
/// An LSPS1 (Channel Request) server event.
102+
#[cfg(lsps1_service)]
103+
LSPS1Service(lsps1::event::LSPS1ServiceEvent),
104+
/// An LSPS2 (JIT Channel) client event.
105+
LSPS2Client(lsps2::event::LSPS2ClientEvent),
106+
/// An LSPS2 (JIT Channel) server event.
107+
LSPS2Service(lsps2::event::LSPS2ServiceEvent),
108+
}
109+
110+
struct EventFuture {
111+
event_queue: Arc<Mutex<VecDeque<Event>>>,
112+
waker: Arc<Mutex<Option<Waker>>>,
113+
}
114+
115+
impl Future for EventFuture {
116+
type Output = Event;
117+
118+
fn poll(
119+
self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>,
120+
) -> core::task::Poll<Self::Output> {
121+
if let Some(event) = self.event_queue.lock().unwrap().pop_front() {
122+
Poll::Ready(event)
123+
} else {
124+
*self.waker.lock().unwrap() = Some(cx.waker().clone());
125+
Poll::Pending
126+
}
127+
}
128+
}
129+
130+
#[cfg(test)]
131+
mod tests {
132+
#[tokio::test]
133+
#[cfg(feature = "std")]
134+
async fn event_queue_works() {
135+
use super::*;
136+
use crate::lsps0::event::LSPS0ClientEvent;
137+
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
138+
use core::sync::atomic::{AtomicU16, Ordering};
139+
use std::sync::Arc;
140+
use std::time::Duration;
141+
142+
let event_queue = Arc::new(EventQueue::new());
143+
assert_eq!(event_queue.next_event(), None);
144+
145+
let secp_ctx = Secp256k1::new();
146+
let counterparty_node_id =
147+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
148+
let expected_event = Event::LSPS0Client(LSPS0ClientEvent::ListProtocolsResponse {
149+
counterparty_node_id,
150+
protocols: Vec::new(),
151+
});
152+
153+
for _ in 0..3 {
154+
event_queue.enqueue(expected_event.clone());
155+
}
156+
157+
assert_eq!(event_queue.wait_next_event(), expected_event);
158+
assert_eq!(event_queue.next_event_async().await, expected_event);
159+
assert_eq!(event_queue.next_event(), Some(expected_event.clone()));
160+
assert_eq!(event_queue.next_event(), None);
161+
162+
// Check `next_event_async` won't return if the queue is empty and always rather timeout.
163+
tokio::select! {
164+
_ = tokio::time::sleep(Duration::from_millis(10)) => {
165+
// Timeout
166+
}
167+
_ = event_queue.next_event_async() => {
168+
panic!();
169+
}
170+
}
171+
assert_eq!(event_queue.next_event(), None);
172+
173+
// Check we get the expected number of events when polling/enqueuing concurrently.
174+
let enqueued_events = AtomicU16::new(0);
175+
let received_events = AtomicU16::new(0);
176+
let mut delayed_enqueue = false;
177+
178+
for _ in 0..25 {
179+
event_queue.enqueue(expected_event.clone());
180+
enqueued_events.fetch_add(1, Ordering::SeqCst);
181+
}
182+
183+
loop {
184+
tokio::select! {
185+
_ = tokio::time::sleep(Duration::from_millis(10)), if !delayed_enqueue => {
186+
event_queue.enqueue(expected_event.clone());
187+
enqueued_events.fetch_add(1, Ordering::SeqCst);
188+
delayed_enqueue = true;
189+
}
190+
e = event_queue.next_event_async() => {
191+
assert_eq!(e, expected_event);
192+
received_events.fetch_add(1, Ordering::SeqCst);
193+
194+
event_queue.enqueue(expected_event.clone());
195+
enqueued_events.fetch_add(1, Ordering::SeqCst);
196+
}
197+
e = event_queue.next_event_async() => {
198+
assert_eq!(e, expected_event);
199+
received_events.fetch_add(1, Ordering::SeqCst);
200+
}
201+
}
202+
203+
if delayed_enqueue
204+
&& received_events.load(Ordering::SeqCst) == enqueued_events.load(Ordering::SeqCst)
205+
{
206+
break;
207+
}
208+
}
209+
assert_eq!(event_queue.next_event(), None);
210+
211+
// Check we operate correctly, even when mixing and matching blocking and async API calls.
212+
let (tx, mut rx) = tokio::sync::watch::channel(());
213+
let thread_queue = Arc::clone(&event_queue);
214+
let thread_event = expected_event.clone();
215+
std::thread::spawn(move || {
216+
let e = thread_queue.wait_next_event();
217+
assert_eq!(e, thread_event);
218+
tx.send(()).unwrap();
219+
});
220+
221+
let thread_queue = Arc::clone(&event_queue);
222+
let thread_event = expected_event.clone();
223+
std::thread::spawn(move || {
224+
// Sleep a bit before we enqueue the events everybody is waiting for.
225+
std::thread::sleep(Duration::from_millis(20));
226+
thread_queue.enqueue(thread_event.clone());
227+
thread_queue.enqueue(thread_event.clone());
228+
});
229+
230+
let e = event_queue.next_event_async().await;
231+
assert_eq!(e, expected_event.clone());
232+
233+
rx.changed().await.unwrap();
234+
assert_eq!(event_queue.next_event(), None);
235+
}
236+
}

lightning-liquidity/src/lib.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
#![crate_name = "lightning_liquidity"]
10+
11+
//! # `lightning-liquidity`
12+
//! Types and primitives to integrate a spec-compliant LSP with an LDK-based node.
13+
#![deny(missing_docs)]
14+
#![deny(rustdoc::broken_intra_doc_links)]
15+
#![deny(rustdoc::private_intra_doc_links)]
16+
#![allow(bare_trait_objects)]
17+
#![allow(ellipsis_inclusive_range_patterns)]
18+
#![allow(clippy::drop_non_drop)]
19+
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
20+
#![cfg_attr(not(feature = "std"), no_std)]
21+
#[cfg(not(any(feature = "std", feature = "no-std")))]
22+
compile_error!("at least one of the `std` or `no-std` features must be enabled");
23+
24+
#[macro_use]
25+
extern crate alloc;
26+
27+
mod prelude {
28+
#![allow(unused_imports)]
29+
#[cfg(feature = "hashbrown")]
30+
extern crate hashbrown;
31+
32+
#[cfg(feature = "hashbrown")]
33+
pub use self::hashbrown::{hash_map, HashMap, HashSet};
34+
pub use alloc::{boxed::Box, collections::VecDeque, string::String, vec, vec::Vec};
35+
#[cfg(not(feature = "hashbrown"))]
36+
pub use std::collections::{hash_map, HashMap, HashSet};
37+
38+
pub use alloc::borrow::ToOwned;
39+
pub use alloc::string::ToString;
40+
}
41+
42+
pub mod events;
43+
pub mod lsps0;
44+
pub mod lsps1;
45+
pub mod lsps2;
46+
mod manager;
47+
pub mod message_queue;
48+
mod sync;
49+
#[cfg(test)]
50+
mod tests;
51+
mod utils;
52+
53+
pub use manager::{LiquidityClientConfig, LiquidityManager, LiquidityServiceConfig};

0 commit comments

Comments
 (0)