Skip to content

Commit 050d890

Browse files
committed
f: add tests
1 parent 514676c commit 050d890

File tree

3 files changed

+207
-9
lines changed

3 files changed

+207
-9
lines changed

src/event.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ where
15201520
LdkEvent::StaticInvoiceRequested { recipient_id, invoice_slot, reply_path } => {
15211521
let invoice = self
15221522
.static_invoice_store
1523-
.handle_static_invoice_requested(recipient_id, invoice_slot)
1523+
.handle_static_invoice_requested(&recipient_id, invoice_slot)
15241524
.await;
15251525

15261526
match invoice {

src/payment/rate_limiter.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,28 @@ impl RateLimiter {
6060
self.users.retain(|_, bucket| now.duration_since(bucket.last_refill) < max_idle);
6161
}
6262
}
63+
64+
#[cfg(test)]
65+
mod tests {
66+
use crate::payment::rate_limiter::RateLimiter;
67+
68+
use std::time::Duration;
69+
70+
#[test]
71+
fn rate_limiter_test() {
72+
// Test
73+
let mut rate_limiter =
74+
RateLimiter::new(3, Duration::from_millis(100), Duration::from_secs(1));
75+
76+
assert!(rate_limiter.allow(b"user1"));
77+
assert!(rate_limiter.allow(b"user1"));
78+
assert!(rate_limiter.allow(b"user1"));
79+
assert!(!rate_limiter.allow(b"user1"));
80+
assert!(rate_limiter.allow(b"user2"));
81+
82+
std::thread::sleep(Duration::from_millis(150));
83+
84+
assert!(rate_limiter.allow(b"user1"));
85+
assert!(rate_limiter.allow(b"user2"));
86+
}
87+
}

src/payment/static_invoice_store.rs

Lines changed: 181 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,42 +50,215 @@ impl StaticInvoiceStore {
5050
}
5151

5252
pub(crate) async fn handle_static_invoice_requested(
53-
&self, recipient_id: Vec<u8>, invoice_slot: u16,
53+
&self, recipient_id: &[u8], invoice_slot: u16,
5454
) -> Result<Option<StaticInvoice>, lightning::io::Error> {
5555
Self::check_rate_limit(&self.request_rate_limiter, &recipient_id)?;
5656

5757
let (secondary_namespace, key) = Self::get_storage_location(invoice_slot, recipient_id);
5858

59-
self.kv_store.read(STATIC_INVOICES_PRIMARY_NAMESPACE, &secondary_namespace, &key).and_then(
60-
|data| {
59+
self.kv_store
60+
.read(STATIC_INVOICES_PRIMARY_NAMESPACE, &secondary_namespace, &key)
61+
.and_then(|data| {
6162
data.try_into().map(Some).map_err(|e| {
6263
lightning::io::Error::new(
6364
lightning::io::ErrorKind::InvalidData,
6465
format!("Failed to parse static invoice: {:?}", e),
6566
)
6667
})
67-
},
68-
)
68+
})
69+
.or_else(
70+
|e| {
71+
if e.kind() == lightning::io::ErrorKind::NotFound {
72+
Ok(None)
73+
} else {
74+
Err(e)
75+
}
76+
},
77+
)
6978
}
7079

7180
pub(crate) async fn handle_persist_static_invoice(
7281
&self, invoice: StaticInvoice, invoice_slot: u16, recipient_id: Vec<u8>,
7382
) -> Result<(), lightning::io::Error> {
7483
Self::check_rate_limit(&self.persist_rate_limiter, &recipient_id)?;
7584

76-
let (secondary_namespace, key) = Self::get_storage_location(invoice_slot, recipient_id);
85+
let (secondary_namespace, key) = Self::get_storage_location(invoice_slot, &recipient_id);
7786

7887
let mut buf = Vec::new();
7988
invoice.write(&mut buf)?;
8089

8190
self.kv_store.write(STATIC_INVOICES_PRIMARY_NAMESPACE, &secondary_namespace, &key, buf)
8291
}
8392

84-
fn get_storage_location(invoice_slot: u16, recipient_id: Vec<u8>) -> (String, String) {
85-
let hash = Sha256::hash(&recipient_id).to_byte_array();
93+
fn get_storage_location(invoice_slot: u16, recipient_id: &[u8]) -> (String, String) {
94+
let hash = Sha256::hash(recipient_id).to_byte_array();
8695
let secondary_namespace = hex_utils::to_string(&hash);
8796

8897
let key = format!("{:05}", invoice_slot);
8998
(secondary_namespace, key)
9099
}
91100
}
101+
102+
#[cfg(test)]
103+
mod tests {
104+
use std::{sync::Arc, time::Duration};
105+
106+
use bitcoin::{
107+
key::{Keypair, Secp256k1},
108+
secp256k1::{PublicKey, SecretKey},
109+
};
110+
use lightning::blinded_path::{
111+
message::BlindedMessagePath,
112+
payment::{BlindedPayInfo, BlindedPaymentPath},
113+
BlindedHop,
114+
};
115+
use lightning::ln::inbound_payment::ExpandedKey;
116+
use lightning::offers::{
117+
nonce::Nonce,
118+
offer::OfferBuilder,
119+
static_invoice::{StaticInvoice, StaticInvoiceBuilder},
120+
};
121+
use lightning::sign::EntropySource;
122+
use lightning::util::test_utils::TestStore;
123+
use lightning_types::features::BlindedHopFeatures;
124+
125+
use crate::{payment::static_invoice_store::StaticInvoiceStore, types::DynStore};
126+
127+
#[tokio::test]
128+
async fn static_invoice_store_test() {
129+
let store: Arc<DynStore> = Arc::new(TestStore::new(false));
130+
let static_invoice_store = StaticInvoiceStore::new(Arc::clone(&store));
131+
132+
let static_invoice = invoice();
133+
let recipient_id = vec![1, 1, 1];
134+
assert!(static_invoice_store
135+
.handle_persist_static_invoice(static_invoice.clone(), 0, recipient_id.clone())
136+
.await
137+
.is_ok());
138+
139+
let requested_invoice =
140+
static_invoice_store.handle_static_invoice_requested(&recipient_id, 0).await.unwrap();
141+
142+
assert_eq!(requested_invoice.unwrap(), static_invoice);
143+
144+
assert!(static_invoice_store
145+
.handle_static_invoice_requested(&recipient_id, 1)
146+
.await
147+
.unwrap()
148+
.is_none());
149+
150+
assert!(static_invoice_store
151+
.handle_static_invoice_requested(&[2, 2, 2], 0)
152+
.await
153+
.unwrap()
154+
.is_none());
155+
}
156+
157+
fn invoice() -> StaticInvoice {
158+
let node_id = recipient_pubkey();
159+
let payment_paths = payment_paths();
160+
let now = now();
161+
let expanded_key = ExpandedKey::new([42; 32]);
162+
let entropy = FixedEntropy {};
163+
let nonce = Nonce::from_entropy_source(&entropy);
164+
let secp_ctx = Secp256k1::new();
165+
166+
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
167+
.path(blinded_path())
168+
.build()
169+
.unwrap();
170+
171+
StaticInvoiceBuilder::for_offer_using_derived_keys(
172+
&offer,
173+
payment_paths.clone(),
174+
vec![blinded_path()],
175+
now,
176+
&expanded_key,
177+
nonce,
178+
&secp_ctx,
179+
)
180+
.unwrap()
181+
.build_and_sign(&secp_ctx)
182+
.unwrap()
183+
}
184+
185+
fn now() -> Duration {
186+
std::time::SystemTime::now()
187+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
188+
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH")
189+
}
190+
191+
fn payment_paths() -> Vec<BlindedPaymentPath> {
192+
vec![
193+
BlindedPaymentPath::from_blinded_path_and_payinfo(
194+
pubkey(40),
195+
pubkey(41),
196+
vec![
197+
BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
198+
BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
199+
],
200+
BlindedPayInfo {
201+
fee_base_msat: 1,
202+
fee_proportional_millionths: 1_000,
203+
cltv_expiry_delta: 42,
204+
htlc_minimum_msat: 100,
205+
htlc_maximum_msat: 1_000_000_000_000,
206+
features: BlindedHopFeatures::empty(),
207+
},
208+
),
209+
BlindedPaymentPath::from_blinded_path_and_payinfo(
210+
pubkey(40),
211+
pubkey(41),
212+
vec![
213+
BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
214+
BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
215+
],
216+
BlindedPayInfo {
217+
fee_base_msat: 1,
218+
fee_proportional_millionths: 1_000,
219+
cltv_expiry_delta: 42,
220+
htlc_minimum_msat: 100,
221+
htlc_maximum_msat: 1_000_000_000_000,
222+
features: BlindedHopFeatures::empty(),
223+
},
224+
),
225+
]
226+
}
227+
228+
fn blinded_path() -> BlindedMessagePath {
229+
BlindedMessagePath::from_blinded_path(
230+
pubkey(40),
231+
pubkey(41),
232+
vec![
233+
BlindedHop { blinded_node_id: pubkey(42), encrypted_payload: vec![0; 43] },
234+
BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 44] },
235+
],
236+
)
237+
}
238+
239+
fn pubkey(byte: u8) -> PublicKey {
240+
let secp_ctx = Secp256k1::new();
241+
PublicKey::from_secret_key(&secp_ctx, &privkey(byte))
242+
}
243+
244+
fn privkey(byte: u8) -> SecretKey {
245+
SecretKey::from_slice(&[byte; 32]).unwrap()
246+
}
247+
248+
fn recipient_keys() -> Keypair {
249+
let secp_ctx = Secp256k1::new();
250+
Keypair::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap())
251+
}
252+
253+
fn recipient_pubkey() -> PublicKey {
254+
recipient_keys().public_key()
255+
}
256+
257+
struct FixedEntropy;
258+
259+
impl EntropySource for FixedEntropy {
260+
fn get_secure_random_bytes(&self) -> [u8; 32] {
261+
[42; 32]
262+
}
263+
}
264+
}

0 commit comments

Comments
 (0)