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

Commit 7b78090

Browse files
Merge pull request #851 from MutinyWallet/more-tests
More NWC tests
2 parents 3900f55 + 561863f commit 7b78090

File tree

3 files changed

+140
-11
lines changed

3 files changed

+140
-11
lines changed

mutiny-core/src/nostr/nwc.rs

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,12 +1043,15 @@ mod test {
10431043
mod wasm_test {
10441044
use super::*;
10451045
use crate::event::{MillisatAmount, PaymentInfo};
1046+
use crate::logging::MutinyLogger;
10461047
use crate::nostr::ProfileType;
10471048
use crate::storage::MemoryStorage;
10481049
use crate::test_utils::{create_dummy_invoice, create_node, create_nwc_request};
10491050
use bitcoin::hashes::Hash;
1051+
use bitcoin::secp256k1::ONE_KEY;
10501052
use bitcoin::Network;
10511053
use nostr::key::SecretKey;
1054+
use std::sync::Arc;
10521055
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
10531056

10541057
wasm_bindgen_test_configure!(run_in_browser);
@@ -1101,7 +1104,8 @@ mod wasm_test {
11011104
let uri = nwc.get_nwc_uri().unwrap();
11021105

11031106
// test hodl invoice
1104-
let invoice = "lnbc10u1pj40d0ypp5gpupr2ce8rq2uh850ylkmugnn8dfey0ugqp72ttjq3gc6gz20vpqhp5d60tc9xkcdzwjst3jmzg3flfu662hjy3yyu4eyhfhm03pzurrsgscqz8qxqrrsssp5r74mhdtu7mr799wmjckukw5unh6phvdevw8cym7ppxfk8d52r3ks9qyyssqzefsn7a8sl8f35hvv2tkye074ml8am42unrrgvedmkxlxrr9xsxkvyyldjt8kr8trn0lzudjjd0ugy0dc9wk9dwapz4m23u8anlyldcp0azm0s".to_string();
1107+
let invoice =
1108+
create_dummy_invoice(Some(10_000), Network::Regtest, Some(ONE_KEY)).to_string();
11051109
let event = create_nwc_request(&uri, invoice.clone());
11061110
let result = nwc
11071111
.handle_nwc_request(event.clone(), &node, &nostr_manager)
@@ -1189,7 +1193,7 @@ mod wasm_test {
11891193
check_no_pending_invoices(&storage);
11901194

11911195
// test amount-less invoice
1192-
let invoice = create_dummy_invoice(None, Network::Regtest);
1196+
let invoice = create_dummy_invoice(None, Network::Regtest, None);
11931197
let event = create_nwc_request(&uri, invoice.to_string());
11941198
let result = nwc.handle_nwc_request(event, &node, &nostr_manager).await;
11951199
check_nwc_error_response(
@@ -1203,7 +1207,9 @@ mod wasm_test {
12031207
check_no_pending_invoices(&storage);
12041208

12051209
// test hodl invoice
1206-
let event = create_nwc_request(&uri, "lnbc10u1pj40d0ypp5gpupr2ce8rq2uh850ylkmugnn8dfey0ugqp72ttjq3gc6gz20vpqhp5d60tc9xkcdzwjst3jmzg3flfu662hjy3yyu4eyhfhm03pzurrsgscqz8qxqrrsssp5r74mhdtu7mr799wmjckukw5unh6phvdevw8cym7ppxfk8d52r3ks9qyyssqzefsn7a8sl8f35hvv2tkye074ml8am42unrrgvedmkxlxrr9xsxkvyyldjt8kr8trn0lzudjjd0ugy0dc9wk9dwapz4m23u8anlyldcp0azm0s".to_string());
1210+
let invoice =
1211+
create_dummy_invoice(Some(10_000), Network::Regtest, Some(ONE_KEY)).to_string();
1212+
let event = create_nwc_request(&uri, invoice);
12071213
let result = nwc.handle_nwc_request(event, &node, &nostr_manager).await;
12081214
check_nwc_error_response(
12091215
result.unwrap().unwrap(),
@@ -1216,7 +1222,7 @@ mod wasm_test {
12161222
check_no_pending_invoices(&storage);
12171223

12181224
// test in-flight payment
1219-
let invoice = create_dummy_invoice(Some(1_000), Network::Regtest);
1225+
let invoice = create_dummy_invoice(Some(1_000), Network::Regtest, None);
12201226
let payment_info = PaymentInfo {
12211227
preimage: None,
12221228
secret: Some(invoice.payment_secret().0),
@@ -1236,7 +1242,7 @@ mod wasm_test {
12361242
check_no_pending_invoices(&storage);
12371243

12381244
// test completed payment
1239-
let invoice = create_dummy_invoice(Some(1_000), Network::Regtest);
1245+
let invoice = create_dummy_invoice(Some(1_000), Network::Regtest, None);
12401246
let payment_info = PaymentInfo {
12411247
preimage: None,
12421248
secret: Some(invoice.payment_secret().0),
@@ -1256,7 +1262,7 @@ mod wasm_test {
12561262
check_no_pending_invoices(&storage);
12571263

12581264
// test it goes to pending
1259-
let invoice = create_dummy_invoice(Some(1_000), Network::Regtest);
1265+
let invoice = create_dummy_invoice(Some(1_000), Network::Regtest, None);
12601266
let event = create_nwc_request(&uri, invoice.to_string());
12611267
let result = nwc
12621268
.handle_nwc_request(event.clone(), &node, &nostr_manager)
@@ -1273,4 +1279,114 @@ mod wasm_test {
12731279
assert_eq!(pending[0].index, nwc.profile.index);
12741280
assert_eq!(pending[0].pubkey, event.pubkey);
12751281
}
1282+
1283+
#[test]
1284+
async fn test_clear_expired_pending_invoices() {
1285+
let storage = MemoryStorage::default();
1286+
let xprivkey = ExtendedPrivKey::new_master(Network::Regtest, &[0; 64]).unwrap();
1287+
let nostr_manager = NostrManager::from_mnemonic(
1288+
xprivkey,
1289+
storage.clone(),
1290+
Arc::new(MutinyLogger::default()),
1291+
)
1292+
.unwrap();
1293+
1294+
// check we start with no pending invoices
1295+
let pending = nostr_manager.get_pending_nwc_invoices().unwrap();
1296+
assert_eq!(pending.len(), 0);
1297+
1298+
// add an expired invoice
1299+
let expired = PendingNwcInvoice {
1300+
index: 0,
1301+
invoice: Bolt11Invoice::from_str(INVOICE).unwrap(),
1302+
event_id: EventId::all_zeros(),
1303+
pubkey: nostr_manager.primary_key.public_key(),
1304+
};
1305+
// add an unexpired invoice
1306+
let unexpired = PendingNwcInvoice {
1307+
index: 0,
1308+
invoice: create_dummy_invoice(Some(1_000), Network::Regtest, None),
1309+
event_id: EventId::all_zeros(),
1310+
pubkey: nostr_manager.primary_key.public_key(),
1311+
};
1312+
storage
1313+
.set_data(
1314+
PENDING_NWC_EVENTS_KEY,
1315+
vec![expired, unexpired.clone()],
1316+
None,
1317+
)
1318+
.unwrap();
1319+
// make sure we added them
1320+
let pending = nostr_manager.get_pending_nwc_invoices().unwrap();
1321+
assert_eq!(pending.len(), 2);
1322+
1323+
// check that the expired invoice is cleared
1324+
nostr_manager.clear_expired_nwc_invoices().await.unwrap();
1325+
let pending = nostr_manager.get_pending_nwc_invoices().unwrap();
1326+
assert_eq!(pending.len(), 1);
1327+
assert_eq!(pending[0], unexpired);
1328+
}
1329+
1330+
#[test]
1331+
async fn test_process_nwc_event_budget() {
1332+
let storage = MemoryStorage::default();
1333+
let node = create_node(storage.clone()).await;
1334+
1335+
let xprivkey = ExtendedPrivKey::new_master(Network::Regtest, &[0; 64]).unwrap();
1336+
let nostr_manager =
1337+
NostrManager::from_mnemonic(xprivkey, storage.clone(), node.logger.clone()).unwrap();
1338+
1339+
let budget = 10_000;
1340+
let profile = nostr_manager
1341+
.create_new_profile(
1342+
ProfileType::Normal {
1343+
name: "test".to_string(),
1344+
},
1345+
SpendingConditions::Budget(BudgetedSpendingConditions {
1346+
budget,
1347+
single_max: None,
1348+
payments: vec![],
1349+
period: BudgetPeriod::Seconds(10),
1350+
}),
1351+
NwcProfileTag::General,
1352+
)
1353+
.unwrap();
1354+
1355+
let secp = Secp256k1::new();
1356+
let mut nwc = NostrWalletConnect::new(&secp, xprivkey, profile.profile()).unwrap();
1357+
let uri = nwc.get_nwc_uri().unwrap();
1358+
1359+
// test failed payment goes to pending, we have no channels so it will fail
1360+
let invoice = create_dummy_invoice(Some(10), Network::Regtest, None);
1361+
let event = create_nwc_request(&uri, invoice.to_string());
1362+
let result = nwc
1363+
.handle_nwc_request(event.clone(), &node, &nostr_manager)
1364+
.await;
1365+
assert!(result.unwrap().is_some()); // should get a error response
1366+
let pending = nostr_manager.get_pending_nwc_invoices().unwrap();
1367+
assert_eq!(pending.len(), 1);
1368+
assert_eq!(pending[0].invoice, invoice);
1369+
assert_eq!(pending[0].event_id, event.id);
1370+
assert_eq!(pending[0].index, nwc.profile.index);
1371+
assert_eq!(pending[0].pubkey, event.pubkey);
1372+
1373+
// clear pending
1374+
nostr_manager.deny_all_pending_nwc().await.unwrap();
1375+
1376+
// test over budget payment goes to pending
1377+
let invoice = create_dummy_invoice(Some(budget + 1), Network::Regtest, None);
1378+
let event = create_nwc_request(&uri, invoice.to_string());
1379+
let result = nwc
1380+
.handle_nwc_request(event.clone(), &node, &nostr_manager)
1381+
.await;
1382+
assert!(result.unwrap().is_some()); // should get a error response
1383+
let pending = nostr_manager.get_pending_nwc_invoices().unwrap();
1384+
assert_eq!(pending.len(), 1);
1385+
assert_eq!(pending[0].invoice, invoice);
1386+
assert_eq!(pending[0].event_id, event.id);
1387+
assert_eq!(pending[0].index, nwc.profile.index);
1388+
assert_eq!(pending[0].pubkey, event.pubkey);
1389+
1390+
// todo test successful payment is added to budget
1391+
}
12761392
}

mutiny-core/src/test_utils.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,23 @@ pub(crate) async fn create_node<S: MutinyStorage>(storage: S) -> Node<S> {
126126
.unwrap()
127127
}
128128

129-
pub fn create_dummy_invoice(msats: Option<u64>, network: Network) -> Bolt11Invoice {
129+
pub fn create_dummy_invoice(
130+
msats: Option<u64>,
131+
network: Network,
132+
sk: Option<SecretKey>,
133+
) -> Bolt11Invoice {
130134
let hash = &mut [0u8; 32];
131135
getrandom::getrandom(hash).unwrap();
132136
let invoice_hash = sha256::Hash::from_slice(hash).unwrap();
133137

134138
let payment_secret = &mut [0u8; 32];
135139
getrandom::getrandom(payment_secret).unwrap();
136140

137-
let priv_key_bytes = &mut [0u8; 32];
138-
getrandom::getrandom(priv_key_bytes).unwrap();
139-
let private_key = SecretKey::from_slice(priv_key_bytes).unwrap();
141+
let sk = sk.unwrap_or_else(|| {
142+
let priv_key_bytes = &mut [0u8; 32];
143+
getrandom::getrandom(priv_key_bytes).unwrap();
144+
SecretKey::from_slice(priv_key_bytes).unwrap()
145+
});
140146

141147
let secp = Secp256k1::new();
142148
let builder = InvoiceBuilder::new(network.into())
@@ -153,7 +159,7 @@ pub fn create_dummy_invoice(msats: Option<u64>, network: Network) -> Bolt11Invoi
153159
};
154160

155161
builder
156-
.build_signed(|hash| secp.sign_ecdsa_recoverable(hash, &private_key))
162+
.build_signed(|hash| secp.sign_ecdsa_recoverable(hash, &sk))
157163
.unwrap()
158164
}
159165

mutiny-core/src/utils.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,5 +183,12 @@ pub fn get_monitor_version(bytes: &[u8]) -> u64 {
183183
u64::from_be_bytes(bytes[2..10].try_into().unwrap())
184184
}
185185

186+
#[cfg(not(test))]
186187
pub const HODL_INVOICE_NODES: [&str; 1] =
187188
["031b301307574bbe9b9ac7b79cbe1700e31e544513eae0b5d7497483083f99e581"];
189+
190+
#[cfg(test)]
191+
pub const HODL_INVOICE_NODES: [&str; 2] = [
192+
"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", // pubkey of ONE_KEY
193+
"031b301307574bbe9b9ac7b79cbe1700e31e544513eae0b5d7497483083f99e581",
194+
];

0 commit comments

Comments
 (0)