Skip to content

Commit bdcdf57

Browse files
committed
Plumb an optional fee_claim through register_node
MDK-980 added the fee_claim field to RegisterNodeRequest and the verifier that reads it, but the client still hardcoded None, so no node could ever present a claim. This lets the caller pass one. The claim rides as a per-call argument rather than a field on LSPS4ClientConfig: that config derives Copy, which an Option<String> would break, and the value belongs to the layer above (ldk-node), which already holds it and relays it on every registration. There is a single call site, so threading it as a parameter is the smaller, honest change. The value is opaque here: a lowercase-hex signed grant the LSP alone decodes and verifies. Passing None reproduces today's behavior exactly, so nothing changes until a node is configured with a claim.
1 parent 521f908 commit bdcdf57

1 file changed

Lines changed: 75 additions & 3 deletions

File tree

lightning-liquidity/src/lsps4/client.rs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,15 @@ where
8383

8484
/// Requests the LSP to register the node.
8585
///
86+
/// `fee_claim` is an optional lowercase-hex signed grant for a non-standard fee policy; the LSP
87+
/// verifies it against its configured issuer keys. `None` (or an unverifiable claim) leaves the
88+
/// node on the standard policy.
89+
///
8690
/// The user will receive the LSP's response via an [`InvoiceParametersReady`] event.
8791
///
8892
/// [`InvoiceParametersReady`]: crate::lsps4::event::LSPS4ClientEvent::InvoiceParametersReady
8993
pub fn register_node(
90-
&self, counterparty_node_id: PublicKey
94+
&self, counterparty_node_id: PublicKey, fee_claim: Option<String>,
9195
) -> Result<LSPSRequestId, APIError> {
9296
let request_id = crate::utils::generate_request_id(&self.entropy_source);
9397

@@ -109,7 +113,7 @@ where
109113
}
110114
}
111115

112-
let request = LSPS4Request::RegisterNode(RegisterNodeRequest { fee_claim: None });
116+
let request = LSPS4Request::RegisterNode(RegisterNodeRequest { fee_claim });
113117
let msg = LSPS4Message::Request(request_id.clone(), request).into();
114118
let mut message_queue_notifier = self.pending_messages.notifier();
115119
message_queue_notifier.enqueue(&counterparty_node_id, msg);
@@ -199,4 +203,72 @@ where
199203
}
200204

201205
#[cfg(test)]
202-
mod tests {}
206+
mod tests {
207+
use super::*;
208+
use crate::lsps0::ser::LSPSMessage;
209+
use bitcoin::secp256k1::{Secp256k1, SecretKey};
210+
use core::sync::atomic::{AtomicU64, Ordering};
211+
use lightning::util::persist::KVStoreSyncWrapper;
212+
use lightning::util::test_utils::TestStore;
213+
use lightning::util::wakers::Notifier;
214+
use std::collections::VecDeque;
215+
216+
struct CountingEntropy {
217+
counter: AtomicU64,
218+
}
219+
220+
impl EntropySource for CountingEntropy {
221+
fn get_secure_random_bytes(&self) -> [u8; 32] {
222+
let counter = self.counter.fetch_add(1, Ordering::SeqCst);
223+
let mut bytes = [0u8; 32];
224+
bytes[0..8].copy_from_slice(&counter.to_be_bytes());
225+
bytes
226+
}
227+
}
228+
229+
type TestKVStore = Arc<KVStoreSyncWrapper<Arc<TestStore>>>;
230+
231+
fn setup() -> (LSPS4ClientHandler<Arc<CountingEntropy>, TestKVStore>, Arc<MessageQueue>, PublicKey)
232+
{
233+
let entropy = Arc::new(CountingEntropy { counter: AtomicU64::new(1) });
234+
let message_queue = Arc::new(MessageQueue::new(Arc::new(Notifier::new())));
235+
let kv_store = Arc::new(KVStoreSyncWrapper(Arc::new(TestStore::new(false))));
236+
let event_queue =
237+
Arc::new(EventQueue::new(VecDeque::new(), kv_store, Arc::new(Notifier::new())));
238+
let client = LSPS4ClientHandler::new(
239+
entropy,
240+
Arc::clone(&message_queue),
241+
event_queue,
242+
LSPS4ClientConfig::default(),
243+
);
244+
let peer = PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&[42u8; 32]).unwrap());
245+
(client, message_queue, peer)
246+
}
247+
248+
/// The single enqueued message must be a `register_node` request; returns its `fee_claim`.
249+
fn sole_request_fee_claim(message_queue: &MessageQueue) -> Option<String> {
250+
let mut pending = message_queue.get_and_clear_pending_msgs();
251+
assert_eq!(pending.len(), 1);
252+
match pending.pop().unwrap().1 {
253+
LSPSMessage::LSPS4(LSPS4Message::Request(_, LSPS4Request::RegisterNode(req))) => {
254+
req.fee_claim
255+
},
256+
other => panic!("expected a register_node request, got {:?}", other),
257+
}
258+
}
259+
260+
#[test]
261+
fn register_node_carries_the_claim() {
262+
let (client, message_queue, peer) = setup();
263+
let claim = "deadbeef".to_string();
264+
client.register_node(peer, Some(claim.clone())).unwrap();
265+
assert_eq!(sole_request_fee_claim(&message_queue), Some(claim));
266+
}
267+
268+
#[test]
269+
fn register_node_without_a_claim_omits_it() {
270+
let (client, message_queue, peer) = setup();
271+
client.register_node(peer, None).unwrap();
272+
assert_eq!(sole_request_fee_claim(&message_queue), None);
273+
}
274+
}

0 commit comments

Comments
 (0)