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

Commit bb902df

Browse files
authored
Merge pull request #100 from tnull/2024-02-expose-request-id
2 parents 478ccf9 + 20bc6c8 commit bb902df

File tree

2 files changed

+58
-131
lines changed

2 files changed

+58
-131
lines changed

src/lsps2/client.rs

+47-127
Original file line numberDiff line numberDiff line change
@@ -27,93 +27,50 @@ use core::default::Default;
2727
use core::ops::Deref;
2828

2929
use crate::lsps2::msgs::{
30-
BuyRequest, BuyResponse, GetInfoRequest, GetInfoResponse, InterceptScid, LSPS2Message,
31-
LSPS2Request, LSPS2Response, OpeningFeeParams,
30+
BuyRequest, BuyResponse, GetInfoRequest, GetInfoResponse, LSPS2Message, LSPS2Request,
31+
LSPS2Response, OpeningFeeParams,
3232
};
3333

3434
/// Client-side configuration options for JIT channels.
3535
#[derive(Clone, Debug, Copy)]
36-
pub struct LSPS2ClientConfig {
37-
/// Trust the LSP to create a valid channel funding transaction and have it confirmed on-chain.
38-
///
39-
/// TODO: If set to `false`, we'll only release the pre-image after we see an on-chain
40-
/// confirmation of the channel's funding transaction.
41-
///
42-
/// Defaults to `true`.
43-
pub client_trusts_lsp: bool,
44-
}
36+
pub struct LSPS2ClientConfig {}
4537

4638
impl Default for LSPS2ClientConfig {
4739
fn default() -> Self {
48-
Self { client_trusts_lsp: true }
49-
}
50-
}
51-
52-
struct ChannelStateError(String);
53-
54-
impl From<ChannelStateError> for LightningError {
55-
fn from(value: ChannelStateError) -> Self {
56-
LightningError { err: value.0, action: ErrorAction::IgnoreAndLog(Level::Info) }
57-
}
58-
}
59-
60-
#[derive(PartialEq, Debug)]
61-
enum InboundJITChannelState {
62-
BuyRequested,
63-
PendingPayment { client_trusts_lsp: bool, intercept_scid: InterceptScid },
64-
}
65-
66-
impl InboundJITChannelState {
67-
fn invoice_params_received(
68-
&self, client_trusts_lsp: bool, intercept_scid: InterceptScid,
69-
) -> Result<Self, ChannelStateError> {
70-
match self {
71-
InboundJITChannelState::BuyRequested { .. } => {
72-
Ok(InboundJITChannelState::PendingPayment { client_trusts_lsp, intercept_scid })
73-
}
74-
state => Err(ChannelStateError(format!(
75-
"Invoice params received when JIT Channel was in state: {:?}",
76-
state
77-
))),
78-
}
40+
Self {}
7941
}
8042
}
8143

8244
struct InboundJITChannel {
83-
state: InboundJITChannelState,
84-
user_channel_id: u128,
8545
payment_size_msat: Option<u64>,
8646
}
8747

8848
impl InboundJITChannel {
89-
fn new(user_channel_id: u128, payment_size_msat: Option<u64>) -> Self {
90-
Self { user_channel_id, payment_size_msat, state: InboundJITChannelState::BuyRequested }
91-
}
92-
93-
fn invoice_params_received(
94-
&mut self, client_trusts_lsp: bool, intercept_scid: InterceptScid,
95-
) -> Result<(), LightningError> {
96-
self.state = self.state.invoice_params_received(client_trusts_lsp, intercept_scid)?;
97-
Ok(())
49+
fn new(payment_size_msat: Option<u64>) -> Self {
50+
Self { payment_size_msat }
9851
}
9952
}
10053

10154
struct PeerState {
102-
inbound_channels_by_id: HashMap<u128, InboundJITChannel>,
10355
pending_get_info_requests: HashSet<RequestId>,
104-
pending_buy_requests: HashMap<RequestId, u128>,
56+
pending_buy_requests: HashMap<RequestId, InboundJITChannel>,
10557
}
10658

10759
impl PeerState {
10860
fn new() -> Self {
109-
let inbound_channels_by_id = HashMap::new();
11061
let pending_get_info_requests = HashSet::new();
11162
let pending_buy_requests = HashMap::new();
112-
Self { inbound_channels_by_id, pending_get_info_requests, pending_buy_requests }
63+
Self { pending_get_info_requests, pending_buy_requests }
11364
}
11465
}
11566

11667
/// The main object allowing to send and receive LSPS2 messages.
68+
///
69+
/// Note that currently only the 'client-trusts-LSP' trust model is supported, i.e., we don't
70+
/// provide any additional API guidance to allow withholding the preimage until the channel is
71+
/// opened. Please refer to the [`LSPS2 specification`] for more information.
72+
///
73+
/// [`LSPS2 specification`]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/tree/main/LSPS2#trust-models
11774
pub struct LSPS2ClientHandler<ES: Deref>
11875
where
11976
ES::Target: EntropySource,
@@ -122,7 +79,7 @@ where
12279
pending_messages: Arc<MessageQueue>,
12380
pending_events: Arc<EventQueue>,
12481
per_peer_state: RwLock<HashMap<PublicKey, Mutex<PeerState>>>,
125-
config: LSPS2ClientConfig,
82+
_config: LSPS2ClientConfig,
12683
}
12784

12885
impl<ES: Deref> LSPS2ClientHandler<ES>
@@ -132,14 +89,14 @@ where
13289
/// Constructs an `LSPS2ClientHandler`.
13390
pub(crate) fn new(
13491
entropy_source: ES, pending_messages: Arc<MessageQueue>, pending_events: Arc<EventQueue>,
135-
config: LSPS2ClientConfig,
92+
_config: LSPS2ClientConfig,
13693
) -> Self {
13794
Self {
13895
entropy_source,
13996
pending_messages,
14097
pending_events,
14198
per_peer_state: RwLock::new(HashMap::new()),
142-
config,
99+
_config,
143100
}
144101
}
145102

@@ -155,8 +112,12 @@ where
155112
/// `token` is an optional `String` that will be provided to the LSP.
156113
/// It can be used by the LSP as an API key, coupon code, or some other way to identify a user.
157114
///
115+
/// Returns the used [`RequestId`], which will be returned via [`OpeningParametersReady`].
116+
///
158117
/// [`OpeningParametersReady`]: crate::lsps2::event::LSPS2ClientEvent::OpeningParametersReady
159-
pub fn request_opening_params(&self, counterparty_node_id: PublicKey, token: Option<String>) {
118+
pub fn request_opening_params(
119+
&self, counterparty_node_id: PublicKey, token: Option<String>,
120+
) -> RequestId {
160121
let request_id = crate::utils::generate_request_id(&self.entropy_source);
161122

162123
{
@@ -170,9 +131,14 @@ where
170131

171132
self.pending_messages.enqueue(
172133
&counterparty_node_id,
173-
LSPS2Message::Request(request_id, LSPS2Request::GetInfo(GetInfoRequest { token }))
174-
.into(),
134+
LSPS2Message::Request(
135+
request_id.clone(),
136+
LSPS2Request::GetInfo(GetInfoRequest { token }),
137+
)
138+
.into(),
175139
);
140+
141+
request_id
176142
}
177143

178144
/// Confirms a set of chosen channel opening parameters to use for the JIT channel and
@@ -182,9 +148,6 @@ where
182148
///
183149
/// The user will receive the LSP's response via an [`InvoiceParametersReady`] event.
184150
///
185-
/// The user needs to provide a locally unique `user_channel_id` which will be used for
186-
/// tracking the channel state.
187-
///
188151
/// If `payment_size_msat` is [`Option::Some`] then the invoice will be for a fixed amount
189152
/// and MPP can be used to pay it.
190153
///
@@ -197,36 +160,33 @@ where
197160
/// [`OpeningParametersReady`]: crate::lsps2::event::LSPS2ClientEvent::OpeningParametersReady
198161
/// [`InvoiceParametersReady`]: crate::lsps2::event::LSPS2ClientEvent::InvoiceParametersReady
199162
pub fn select_opening_params(
200-
&self, counterparty_node_id: PublicKey, user_channel_id: u128,
201-
payment_size_msat: Option<u64>, opening_fee_params: OpeningFeeParams,
202-
) -> Result<(), APIError> {
163+
&self, counterparty_node_id: PublicKey, payment_size_msat: Option<u64>,
164+
opening_fee_params: OpeningFeeParams,
165+
) -> Result<RequestId, APIError> {
203166
let mut outer_state_lock = self.per_peer_state.write().unwrap();
204167
let inner_state_lock =
205168
outer_state_lock.entry(counterparty_node_id).or_insert(Mutex::new(PeerState::new()));
206169
let mut peer_state_lock = inner_state_lock.lock().unwrap();
207170

208-
let jit_channel = InboundJITChannel::new(user_channel_id, payment_size_msat);
209-
if peer_state_lock.inbound_channels_by_id.insert(user_channel_id, jit_channel).is_some() {
171+
let request_id = crate::utils::generate_request_id(&self.entropy_source);
172+
173+
let jit_channel = InboundJITChannel::new(payment_size_msat);
174+
if peer_state_lock.pending_buy_requests.insert(request_id.clone(), jit_channel).is_some() {
210175
return Err(APIError::APIMisuseError {
211-
err: format!(
212-
"Failed due to duplicate user_channel_id. Please ensure its uniqueness!"
213-
),
176+
err: format!("Failed due to duplicate request_id. This should never happen!"),
214177
});
215178
}
216179

217-
let request_id = crate::utils::generate_request_id(&self.entropy_source);
218-
peer_state_lock.pending_buy_requests.insert(request_id.clone(), user_channel_id);
219-
220180
self.pending_messages.enqueue(
221181
&counterparty_node_id,
222182
LSPS2Message::Request(
223-
request_id,
183+
request_id.clone(),
224184
LSPS2Request::Buy(BuyRequest { opening_fee_params, payment_size_msat }),
225185
)
226186
.into(),
227187
);
228188

229-
Ok(())
189+
Ok(request_id)
230190
}
231191

232192
fn handle_get_info_response(
@@ -249,6 +209,7 @@ where
249209

250210
self.pending_events.enqueue(Event::LSPS2Client(
251211
LSPS2ClientEvent::OpeningParametersReady {
212+
request_id,
252213
counterparty_node_id: *counterparty_node_id,
253214
opening_fee_params_menu: result.opening_fee_params_menu,
254215
min_payment_size_msat: result.min_payment_size_msat,
@@ -304,7 +265,7 @@ where
304265
Some(inner_state_lock) => {
305266
let mut peer_state = inner_state_lock.lock().unwrap();
306267

307-
let user_channel_id =
268+
let jit_channel =
308269
peer_state.pending_buy_requests.remove(&request_id).ok_or(LightningError {
309270
err: format!(
310271
"Received buy response for an unknown request: {:?}",
@@ -313,45 +274,14 @@ where
313274
action: ErrorAction::IgnoreAndLog(Level::Info),
314275
})?;
315276

316-
let jit_channel = peer_state
317-
.inbound_channels_by_id
318-
.get_mut(&user_channel_id)
319-
.ok_or(LightningError {
320-
err: format!(
321-
"Received buy response for an unknown channel: {:?}",
322-
user_channel_id
323-
),
324-
action: ErrorAction::IgnoreAndLog(Level::Info),
325-
})?;
326-
327-
// Reject the buy response if we disallow client_trusts_lsp and the LSP requires
328-
// it.
329-
if !self.config.client_trusts_lsp && result.client_trusts_lsp {
330-
peer_state.inbound_channels_by_id.remove(&user_channel_id);
331-
return Err(LightningError {
332-
err: format!(
333-
"Aborting JIT channel flow as the LSP requires 'client_trusts_lsp' mode, which we disallow"
334-
),
335-
action: ErrorAction::IgnoreAndLog(Level::Info),
336-
});
337-
}
338-
339-
if let Err(e) = jit_channel.invoice_params_received(
340-
result.client_trusts_lsp,
341-
result.intercept_scid.clone(),
342-
) {
343-
peer_state.inbound_channels_by_id.remove(&user_channel_id);
344-
return Err(e);
345-
}
346-
347277
if let Ok(intercept_scid) = result.intercept_scid.to_scid() {
348278
self.pending_events.enqueue(Event::LSPS2Client(
349279
LSPS2ClientEvent::InvoiceParametersReady {
280+
request_id,
350281
counterparty_node_id: *counterparty_node_id,
351282
intercept_scid,
352283
cltv_expiry_delta: result.lsp_cltv_expiry_delta,
353284
payment_size_msat: jit_channel.payment_size_msat,
354-
user_channel_id: jit_channel.user_channel_id,
355285
},
356286
));
357287
} else {
@@ -385,25 +315,15 @@ where
385315
Some(inner_state_lock) => {
386316
let mut peer_state = inner_state_lock.lock().unwrap();
387317

388-
let user_channel_id =
389-
peer_state.pending_buy_requests.remove(&request_id).ok_or(LightningError {
390-
err: format!("Received buy error for an unknown request: {:?}", request_id),
391-
action: ErrorAction::IgnoreAndLog(Level::Info),
392-
})?;
318+
peer_state.pending_buy_requests.remove(&request_id).ok_or(LightningError {
319+
err: format!("Received buy error for an unknown request: {:?}", request_id),
320+
action: ErrorAction::IgnoreAndLog(Level::Info),
321+
})?;
393322

394-
peer_state.inbound_channels_by_id.remove(&user_channel_id).ok_or(
395-
LightningError {
396-
err: format!(
397-
"Received buy error for an unknown channel: {:?}",
398-
user_channel_id
399-
),
400-
action: ErrorAction::IgnoreAndLog(Level::Info),
401-
},
402-
)?;
403323
Ok(())
404324
}
405325
None => {
406-
return Err(LightningError { err: format!("Received error response for a buy request from an unknown counterparty ({:?})",counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)});
326+
return Err(LightningError { err: format!("Received error response for a buy request from an unknown counterparty ({:?})", counterparty_node_id), action: ErrorAction::IgnoreAndLog(Level::Info)});
407327
}
408328
}
409329
}

src/lsps2/event.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ pub enum LSPS2ClientEvent {
2525
///
2626
/// [`LSPS2ClientHandler::select_opening_params`]: crate::lsps2::client::LSPS2ClientHandler::select_opening_params
2727
OpeningParametersReady {
28+
/// The identifier of the issued LSPS2 `get_info` request, as returned by
29+
/// [`LSPS2ClientHandler::request_opening_params`]
30+
///
31+
/// This can be used to track which request this event corresponds to.
32+
///
33+
/// [`LSPS2ClientHandler::request_opening_params`]: crate::lsps2::client::LSPS2ClientHandler::request_opening_params
34+
request_id: RequestId,
2835
/// The node id of the LSP that provided this response.
2936
counterparty_node_id: PublicKey,
3037
/// The menu of fee parameters the LSP is offering at this time.
@@ -41,13 +48,13 @@ pub enum LSPS2ClientEvent {
4148
/// When the invoice is paid, the LSP will open a channel with the previously agreed upon
4249
/// parameters to you.
4350
InvoiceParametersReady {
44-
/// A user-specified identifier used to track the channel open.
45-
///
46-
/// This is the same value as previously passed to
51+
/// The identifier of the issued LSPS2 `buy` request, as returned by
4752
/// [`LSPS2ClientHandler::select_opening_params`].
4853
///
54+
/// This can be used to track which request this event corresponds to.
55+
///
4956
/// [`LSPS2ClientHandler::select_opening_params`]: crate::lsps2::client::LSPS2ClientHandler::select_opening_params
50-
user_channel_id: u128,
57+
request_id: RequestId,
5158
/// The node id of the LSP.
5259
counterparty_node_id: PublicKey,
5360
/// The intercept short channel id to use in the route hint.

0 commit comments

Comments
 (0)