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

Commit 9144558

Browse files
Merge pull request #1112 from MutinyWallet/opt-label-activity
Optimize label activity
2 parents 6623e28 + 74dd6ba commit 9144558

File tree

6 files changed

+86
-68
lines changed

6 files changed

+86
-68
lines changed

mutiny-core/src/labels.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub struct LabelItem {
2222
/// List of addresses that have this label
2323
pub addresses: HashSet<String>,
2424
/// List of invoices that have this label
25-
pub invoices: Vec<Bolt11Invoice>,
25+
pub invoices: HashSet<Bolt11Invoice>,
2626
/// Epoch time in seconds when this label was last used
2727
pub last_used_time: u64,
2828
}
@@ -234,7 +234,7 @@ impl<S: MutinyStorage> LabelStorage for S {
234234
// Create a new label item
235235
let label_item = LabelItem {
236236
addresses,
237-
invoices: vec![],
237+
invoices: HashSet::new(),
238238
last_used_time: now,
239239
};
240240
self.set_data(key, label_item, None)?;
@@ -263,9 +263,7 @@ impl<S: MutinyStorage> LabelStorage for S {
263263
Some(mut label_item) => {
264264
// Add the invoice to the label item
265265
// and sort so we can dedup the invoices
266-
label_item.invoices.push(invoice.clone());
267-
label_item.invoices.sort();
268-
label_item.invoices.dedup();
266+
label_item.invoices.insert(invoice.clone());
269267

270268
// Update the last used timestamp
271269
label_item.last_used_time = now;
@@ -281,9 +279,10 @@ impl<S: MutinyStorage> LabelStorage for S {
281279
}
282280
None => {
283281
// Create a new label item
282+
let invoices = HashSet::from_iter(vec![invoice.clone()]);
284283
let label_item = LabelItem {
285284
addresses: HashSet::new(),
286-
invoices: vec![invoice.clone()],
285+
invoices,
287286
last_used_time: now,
288287
};
289288
self.set_data(key, label_item, None)?;
@@ -574,7 +573,7 @@ mod tests {
574573
"test2".to_string(),
575574
LabelItem {
576575
addresses: HashSet::from_iter(vec!["1BitcoinEaterAddressDontSendf59kuE".to_string()]),
577-
invoices: vec![Bolt11Invoice::from_str("lnbc923720n1pj9nr6zpp5xmvlq2u5253htn52mflh2e6gn7pk5ht0d4qyhc62fadytccxw7hqhp5l4s6qwh57a7cwr7zrcz706qx0qy4eykcpr8m8dwz08hqf362egfscqzzsxqzfvsp5pr7yjvcn4ggrf6fq090zey0yvf8nqvdh2kq7fue0s0gnm69evy6s9qyyssqjyq0fwjr22eeg08xvmz88307yqu8tqqdjpycmermks822fpqyxgshj8hvnl9mkh6srclnxx0uf4ugfq43d66ak3rrz4dqcqd23vxwpsqf7dmhm").unwrap()],
576+
invoices: HashSet::from_iter(vec![Bolt11Invoice::from_str("lnbc923720n1pj9nr6zpp5xmvlq2u5253htn52mflh2e6gn7pk5ht0d4qyhc62fadytccxw7hqhp5l4s6qwh57a7cwr7zrcz706qx0qy4eykcpr8m8dwz08hqf362egfscqzzsxqzfvsp5pr7yjvcn4ggrf6fq090zey0yvf8nqvdh2kq7fue0s0gnm69evy6s9qyyssqjyq0fwjr22eeg08xvmz88307yqu8tqqdjpycmermks822fpqyxgshj8hvnl9mkh6srclnxx0uf4ugfq43d66ak3rrz4dqcqd23vxwpsqf7dmhm").unwrap()]),
578577
..Default::default()
579578
},
580579
);
@@ -888,7 +887,15 @@ mod tests {
888887

889888
let label_item = storage.get_label(&new_label).unwrap();
890889
assert!(label_item.is_some());
891-
assert_eq!(label_item.clone().unwrap().invoices, vec![invoice]);
890+
assert_eq!(
891+
label_item
892+
.clone()
893+
.unwrap()
894+
.invoices
895+
.into_iter()
896+
.collect_vec(),
897+
vec![invoice]
898+
);
892899
assert_eq!(
893900
label_item.unwrap().addresses.into_iter().collect_vec(),
894901
vec![address.to_string()]

mutiny-core/src/lib.rs

+59-7
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ pub use crate::gossip::{GOSSIP_SYNC_TIME_KEY, NETWORK_GRAPH_KEY, PROB_SCORER_KEY
4747
pub use crate::keymanager::generate_seed;
4848
pub use crate::ldkstorage::{CHANNEL_CLOSURE_PREFIX, CHANNEL_MANAGER_KEY, MONITORS_PREFIX_KEY};
4949
use crate::storage::{
50-
list_payment_info, persist_payment_info, update_nostr_contact_list, IndexItem, MutinyStorage,
51-
DEVICE_ID_KEY, EXPECTED_NETWORK_KEY, NEED_FULL_SYNC_KEY, ONCHAIN_PREFIX,
52-
PAYMENT_INBOUND_PREFIX_KEY, PAYMENT_OUTBOUND_PREFIX_KEY, SUBSCRIPTION_TIMESTAMP,
50+
get_payment_hash_from_key, list_payment_info, persist_payment_info, update_nostr_contact_list,
51+
IndexItem, MutinyStorage, DEVICE_ID_KEY, EXPECTED_NETWORK_KEY, NEED_FULL_SYNC_KEY,
52+
ONCHAIN_PREFIX, PAYMENT_INBOUND_PREFIX_KEY, PAYMENT_OUTBOUND_PREFIX_KEY,
53+
SUBSCRIPTION_TIMESTAMP,
5354
};
5455
use crate::{auth::MutinyAuthClient, hermes::HermesClient, logging::MutinyLogger};
5556
use crate::{blindauth::BlindAuthClient, cashu::CashuHttpClient};
@@ -114,6 +115,7 @@ use nostr_sdk::{NostrSigner, RelayPoolNotification};
114115
use reqwest::multipart::{Form, Part};
115116
use serde::{Deserialize, Serialize};
116117
use serde_json::Value;
118+
use std::collections::HashSet;
117119
use std::sync::Arc;
118120
#[cfg(not(target_arch = "wasm32"))]
119121
use std::time::Instant;
@@ -1720,10 +1722,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
17201722
true => PAYMENT_INBOUND_PREFIX_KEY,
17211723
false => PAYMENT_OUTBOUND_PREFIX_KEY,
17221724
};
1723-
let payment_hash_str = key
1724-
.trim_start_matches(prefix)
1725-
.splitn(2, '_') // To support the old format that had `_{node_id}` at the end
1726-
.collect::<Vec<&str>>()[0];
1725+
let payment_hash_str = get_payment_hash_from_key(key, prefix);
17271726
let hash: [u8; 32] = FromHex::from_hex(payment_hash_str)?;
17281727

17291728
return MutinyInvoice::from(info, PaymentHash(hash), inbound, labels).map(Some);
@@ -1810,6 +1809,59 @@ impl<S: MutinyStorage> MutinyWallet<S> {
18101809
Ok(activities)
18111810
}
18121811

1812+
/// Returns all the lightning activity for a given label
1813+
pub async fn get_label_activity(
1814+
&self,
1815+
label: &String,
1816+
) -> Result<Vec<ActivityItem>, MutinyError> {
1817+
let Some(label_item) = self.node_manager.get_label(label)? else {
1818+
return Ok(Vec::new());
1819+
};
1820+
1821+
// get all the payment hashes for this label
1822+
let payment_hashes: HashSet<sha256::Hash> = label_item
1823+
.invoices
1824+
.into_iter()
1825+
.map(|i| *i.payment_hash())
1826+
.collect();
1827+
1828+
let index = self.storage.activity_index();
1829+
let index = index.try_read()?.clone().into_iter().collect_vec();
1830+
1831+
let labels_map = self.storage.get_invoice_labels()?;
1832+
1833+
let mut activities = Vec::with_capacity(index.len());
1834+
for item in index {
1835+
if item.key.starts_with(PAYMENT_INBOUND_PREFIX_KEY) {
1836+
let payment_hash_str =
1837+
get_payment_hash_from_key(&item.key, PAYMENT_INBOUND_PREFIX_KEY);
1838+
let hash = sha256::Hash::from_str(payment_hash_str)?;
1839+
1840+
if payment_hashes.contains(&hash) {
1841+
if let Some(mutiny_invoice) =
1842+
self.get_invoice_internal(&item.key, true, &labels_map)?
1843+
{
1844+
activities.push(ActivityItem::Lightning(Box::new(mutiny_invoice)));
1845+
}
1846+
}
1847+
} else if item.key.starts_with(PAYMENT_OUTBOUND_PREFIX_KEY) {
1848+
let payment_hash_str =
1849+
get_payment_hash_from_key(&item.key, PAYMENT_OUTBOUND_PREFIX_KEY);
1850+
let hash = sha256::Hash::from_str(payment_hash_str)?;
1851+
1852+
if payment_hashes.contains(&hash) {
1853+
if let Some(mutiny_invoice) =
1854+
self.get_invoice_internal(&item.key, true, &labels_map)?
1855+
{
1856+
activities.push(ActivityItem::Lightning(Box::new(mutiny_invoice)));
1857+
}
1858+
}
1859+
}
1860+
}
1861+
1862+
Ok(activities)
1863+
}
1864+
18131865
pub fn list_invoices(&self) -> Result<Vec<MutinyInvoice>, MutinyError> {
18141866
let mut inbound_invoices = self.list_payment_info_from_persisters(true)?;
18151867
let mut outbound_invoices = self.list_payment_info_from_persisters(false)?;

mutiny-core/src/node.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2725,6 +2725,7 @@ mod wasm_test {
27252725
use crate::test_utils::create_node;
27262726
use crate::{error::MutinyError, storage::persist_payment_info};
27272727
use crate::{HTLCStatus, PrivacyLevel};
2728+
use itertools::Itertools;
27282729
use lightning::ln::channelmanager::PaymentId;
27292730
use lightning::ln::PaymentHash;
27302731
use lightning_invoice::Bolt11InvoiceDescription;
@@ -2789,7 +2790,7 @@ mod wasm_test {
27892790

27902791
assert!(label_item.last_used_time >= now);
27912792
assert!(label_item.addresses.is_empty());
2792-
assert_eq!(label_item.invoices, vec![invoice]);
2793+
assert_eq!(label_item.invoices.into_iter().collect_vec(), vec![invoice]);
27932794
}
27942795

27952796
#[test]

mutiny-core/src/nodemanager.rs

+2-47
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
use crate::auth::MutinyAuthClient;
2-
use crate::event::HTLCStatus;
32
use crate::labels::LabelStorage;
43
use crate::ldkstorage::CHANNEL_CLOSURE_PREFIX;
54
use crate::logging::LOGGING_KEY;
65
use crate::utils::{sleep, spawn};
7-
use crate::ActivityItem;
86
use crate::MutinyInvoice;
97
use crate::MutinyWalletConfig;
108
use crate::{
@@ -966,47 +964,6 @@ impl<S: MutinyStorage> NodeManager<S> {
966964
Ok(details_opt.map(|(d, _)| d))
967965
}
968966

969-
/// Returns all the on-chain and lightning activity for a given label
970-
pub async fn get_label_activity(
971-
&self,
972-
label: &String,
973-
) -> Result<Vec<ActivityItem>, MutinyError> {
974-
let Some(label_item) = self.get_label(label)? else {
975-
return Ok(Vec::new());
976-
};
977-
978-
let mut activity = vec![];
979-
for inv in label_item.invoices.iter() {
980-
if let Ok(ln) = self.get_invoice_by_hash(inv.payment_hash()).await {
981-
// Only show paid and in-flight invoices
982-
match ln.status {
983-
HTLCStatus::Succeeded | HTLCStatus::InFlight => {
984-
activity.push(ActivityItem::Lightning(Box::new(ln)));
985-
}
986-
HTLCStatus::Pending | HTLCStatus::Failed => {}
987-
}
988-
}
989-
}
990-
let onchain = self
991-
.list_onchain()
992-
.map_err(|e| {
993-
log_warn!(self.logger, "Failed to get bdk history: {e}");
994-
e
995-
})
996-
.unwrap_or_default();
997-
998-
for on in onchain {
999-
if on.labels.contains(label) {
1000-
activity.push(ActivityItem::OnChain(on));
1001-
}
1002-
}
1003-
1004-
// Newest first
1005-
activity.sort_by(|a, b| b.cmp(a));
1006-
1007-
Ok(activity)
1008-
}
1009-
1010967
/// Adds labels to the TransactionDetails based on the address labels.
1011968
/// This will panic if the TransactionDetails does not have a transaction.
1012969
/// Make sure you flag `include_raw` when calling `list_transactions` to
@@ -2113,10 +2070,8 @@ pub fn create_lsp_config(
21132070
mod tests {
21142071
use crate::{
21152072
encrypt::encryption_key_from_pass,
2116-
nodemanager::{
2117-
ActivityItem, ChannelClosure, MutinyInvoice, NodeManager, TransactionDetails,
2118-
},
2119-
MutinyWalletConfigBuilder, PrivacyLevel,
2073+
nodemanager::{ChannelClosure, MutinyInvoice, NodeManager, TransactionDetails},
2074+
ActivityItem, MutinyWalletConfigBuilder, PrivacyLevel,
21202075
};
21212076
use crate::{keymanager::generate_seed, nodemanager::NodeManagerBuilder};
21222077
use bdk::chain::ConfirmationTime;

mutiny-core/src/storage.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -986,10 +986,7 @@ pub(crate) fn list_payment_info<S: MutinyStorage>(
986986
Ok(map
987987
.into_iter()
988988
.map(|(key, value)| {
989-
let payment_hash_str = key
990-
.trim_start_matches(prefix)
991-
.splitn(2, '_') // To support the old format that had `_{node_id}` at the end
992-
.collect::<Vec<&str>>()[0];
989+
let payment_hash_str = get_payment_hash_from_key(key.as_str(), prefix);
993990
let hash: [u8; 32] =
994991
FromHex::from_hex(payment_hash_str).expect("key should be a sha256 hash");
995992
(PaymentHash(hash), value)
@@ -1057,6 +1054,12 @@ where
10571054
}
10581055
}
10591056

1057+
pub(crate) fn get_payment_hash_from_key<'a>(key: &'a str, prefix: &str) -> &'a str {
1058+
key.trim_start_matches(prefix)
1059+
.splitn(2, '_') // To support the old format that had `_{node_id}` at the end
1060+
.collect::<Vec<&str>>()[0]
1061+
}
1062+
10601063
#[cfg(test)]
10611064
mod tests {
10621065
use crate::test_utils::*;

mutiny-wasm/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1180,7 +1180,7 @@ impl MutinyWallet {
11801180
label: String,
11811181
) -> Result<JsValue /* Vec<ActivityItem> */, MutinyJsError> {
11821182
// get activity from the node manager
1183-
let activity = self.inner.node_manager.get_label_activity(&label).await?;
1183+
let activity = self.inner.get_label_activity(&label).await?;
11841184
let mut activity: Vec<ActivityItem> = activity.into_iter().map(|a| a.into()).collect();
11851185

11861186
// add contact to the activity item it has one, otherwise return the activity list

0 commit comments

Comments
 (0)