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

Fix labeling #1006

Merged
merged 1 commit into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 15 additions & 22 deletions mutiny-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1204,18 +1204,16 @@ impl<S: MutinyStorage> MutinyWallet<S> {
.get(federation_id)
.ok_or(MutinyError::NotFound)?;

let labels = vec![SWAP_LABEL.to_string()];

// if the user provided amount, this is easy
if let Some(amt) = amount {
let (inv, fee) = self.node_manager.create_invoice(amt).await?;
self.storage.set_invoice_labels(
inv.bolt11.clone().expect("just created"),
vec![SWAP_LABEL.to_string()],
)?;
let (inv, fee) = self
.node_manager
.create_invoice(amt, labels.clone())
.await?;
let pay_res = fedimint_client
.pay_invoice(
inv.bolt11.expect("create inv had one job"),
vec![SWAP_LABEL.to_string()],
)
.pay_invoice(inv.bolt11.expect("create inv had one job"), labels.clone())
.await?;
let total_fees_paid = pay_res.fees_paid.unwrap_or(0) + fee;

Expand All @@ -1239,7 +1237,10 @@ impl<S: MutinyStorage> MutinyWallet<S> {
log_debug!(self.logger, "max spendable: {}", amt);

// try to get an invoice for this exact amount
let (inv, fee) = self.node_manager.create_invoice(amt).await?;
let (inv, fee) = self
.node_manager
.create_invoice(amt, labels.clone())
.await?;

// check if we can afford that invoice
let inv_amt = inv.amount_sats.ok_or(MutinyError::BadAmountError)?;
Expand All @@ -1253,22 +1254,15 @@ impl<S: MutinyStorage> MutinyWallet<S> {
// if invoice amount changed, create a new invoice
let (inv_to_pay, fee) = if first_invoice_amount != inv_amt {
self.node_manager
.create_invoice(first_invoice_amount)
.create_invoice(first_invoice_amount, labels.clone())
.await?
} else {
(inv.clone(), fee)
};
self.storage.set_invoice_labels(
inv_to_pay.bolt11.clone().expect("just created"),
vec![SWAP_LABEL.to_string()],
)?;

log_debug!(self.logger, "attempting payment from fedimint client");
let first_invoice_res = fedimint_client
.pay_invoice(
inv_to_pay.bolt11.expect("create inv had one job"),
vec![SWAP_LABEL.to_string()],
)
.pay_invoice(inv_to_pay.bolt11.expect("create inv had one job"), labels)
.await?;

let remaining_balance = fedimint_client.get_balance().await?;
Expand Down Expand Up @@ -1362,9 +1356,8 @@ impl<S: MutinyStorage> MutinyWallet<S> {
}

// Fallback to node_manager invoice creation if no federation invoice created
let (inv, _fee) = self.node_manager.create_invoice(amount).await?;
self.storage
.set_invoice_labels(inv.bolt11.clone().expect("just created"), labels)?;
let (inv, _fee) = self.node_manager.create_invoice(amount, labels).await?;

Ok(inv)
}

Expand Down
50 changes: 42 additions & 8 deletions mutiny-core/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,7 @@ impl<S: MutinyStorage> Node<S> {
&self,
amount_sat: u64,
route_hints: Option<Vec<PhantomRouteHints>>,
labels: Vec<String>,
) -> Result<(Bolt11Invoice, u64), MutinyError> {
match self.lsp_client.as_ref() {
Some(lsp) => {
Expand Down Expand Up @@ -1093,6 +1094,7 @@ impl<S: MutinyStorage> Node<S> {
Some(amount_minus_fee),
Some(lsp_fee.fee_amount_msat),
route_hints,
labels,
)
.await?;

Expand Down Expand Up @@ -1129,8 +1131,13 @@ impl<S: MutinyStorage> Node<S> {
AnyLsp::Lsps(client) => {
if has_inbound_capacity {
Ok((
self.create_internal_invoice(Some(amount_sat), None, route_hints)
.await?,
self.create_internal_invoice(
Some(amount_sat),
None,
route_hints,
labels,
)
.await?,
0,
))
} else {
Expand All @@ -1147,6 +1154,7 @@ impl<S: MutinyStorage> Node<S> {
invoice.clone(),
Some(amount_sat * 1_000),
Some(lsp_fee.fee_amount_msat),
labels,
)
.await?;

Expand All @@ -1163,7 +1171,7 @@ impl<S: MutinyStorage> Node<S> {
}
}
None => Ok((
self.create_internal_invoice(Some(amount_sat), None, route_hints)
self.create_internal_invoice(Some(amount_sat), None, route_hints, labels)
.await?,
0,
)),
Expand All @@ -1175,6 +1183,7 @@ impl<S: MutinyStorage> Node<S> {
amount_sat: Option<u64>,
fee_amount_msat: Option<u64>,
route_hints: Option<Vec<PhantomRouteHints>>,
labels: Vec<String>,
) -> Result<Bolt11Invoice, MutinyError> {
let amount_msat = amount_sat.map(|s| s * 1_000);
// Set description to empty string to make smallest possible invoice/QR code
Expand Down Expand Up @@ -1228,7 +1237,7 @@ impl<S: MutinyStorage> Node<S> {
MutinyError::InvoiceCreationFailed
})?;

self.save_invoice_payment_info(invoice.clone(), amount_msat, fee_amount_msat)
self.save_invoice_payment_info(invoice.clone(), amount_msat, fee_amount_msat, labels)
.await?;

log_info!(self.logger, "SUCCESS: generated invoice: {invoice}");
Expand All @@ -1241,6 +1250,7 @@ impl<S: MutinyStorage> Node<S> {
invoice: Bolt11Invoice,
amount_msat: Option<u64>,
fee_amount_msat: Option<u64>,
labels: Vec<String>,
) -> Result<(), MutinyError> {
let last_update = utils::now().as_secs();
let payment_hash = PaymentHash(invoice.payment_hash().into_inner());
Expand All @@ -1265,6 +1275,8 @@ impl<S: MutinyStorage> Node<S> {
MutinyError::InvoiceCreationFailed
})?;

self.persister.storage.set_invoice_labels(invoice, labels)?;

Ok(())
}

Expand Down Expand Up @@ -2473,7 +2485,10 @@ mod tests {

let amount_sats = 1_000;

let invoice = node.create_invoice(amount_sats, None).await.unwrap().0;
let (invoice, _) = node
.create_invoice(amount_sats, None, vec![])
.await
.unwrap();

assert_eq!(invoice.amount_milli_satoshis(), Some(amount_sats * 1000));
match invoice.description() {
Expand Down Expand Up @@ -2504,7 +2519,7 @@ mod tests {
let storage = MemoryStorage::default();
let node = create_node(storage).await;

let invoice = node.create_invoice(10_000, None).await.unwrap().0;
let invoice = node.create_invoice(10_000, None, vec![]).await.unwrap().0;

let result = node
.pay_invoice_with_timeout(&invoice, None, None, vec![])
Expand Down Expand Up @@ -2599,6 +2614,7 @@ mod tests {
#[cfg(target_arch = "wasm32")]
mod wasm_test {
use crate::event::{MillisatAmount, PaymentInfo};
use crate::labels::LabelStorage;
use crate::storage::MemoryStorage;
use crate::test_utils::create_node;
use crate::HTLCStatus;
Expand Down Expand Up @@ -2626,8 +2642,13 @@ mod wasm_test {
let now = crate::utils::now().as_secs();

let amount_sats = 1_000;
let label = "test".to_string();
let labels = vec![label.clone()];

let invoice = node.create_invoice(amount_sats, None).await.unwrap().0;
let (invoice, _) = node
.create_invoice(amount_sats, None, labels.clone())
.await
.unwrap();

assert_eq!(invoice.amount_milli_satoshis(), Some(amount_sats * 1000));
match invoice.description() {
Expand All @@ -2649,16 +2670,29 @@ mod wasm_test {
assert_eq!(from_storage.amount_sats, Some(amount_sats));
assert_eq!(from_storage.status, HTLCStatus::Pending);
assert_eq!(from_storage.fees_paid, None);
assert_eq!(from_storage.labels, labels.clone());
assert!(from_storage.inbound);
assert!(from_storage.last_updated >= now);

// check labels

let invoice_labels = storage.get_invoice_labels().unwrap();
assert_eq!(invoice_labels.len(), 1);
assert_eq!(invoice_labels.get(&invoice).cloned(), Some(labels));

let label_item = storage.get_label("test").unwrap().unwrap();

assert!(label_item.last_used_time >= now);
assert!(label_item.addresses.is_empty());
assert_eq!(label_item.invoices, vec![invoice]);
}

#[test]
async fn test_fail_own_invoice() {
let storage = MemoryStorage::default();
let node = create_node(storage).await;

let invoice = node.create_invoice(10_000, None).await.unwrap().0;
let invoice = node.create_invoice(10_000, None, vec![]).await.unwrap().0;

let result = node
.pay_invoice_with_timeout(&invoice, None, None, vec![])
Expand Down
10 changes: 8 additions & 2 deletions mutiny-core/src/nodemanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1369,7 +1369,11 @@ impl<S: MutinyStorage> NodeManager<S> {
///
/// If the manager has more than one node it will create a phantom invoice.
/// If there is only one node it will create an invoice just for that node.
pub async fn create_invoice(&self, amount: u64) -> Result<(MutinyInvoice, u64), MutinyError> {
pub async fn create_invoice(
&self,
amount: u64,
labels: Vec<String>,
) -> Result<(MutinyInvoice, u64), MutinyError> {
let nodes = self.nodes.lock().await;
let use_phantom = nodes.len() > 1 && self.lsp_config.is_none();
if nodes.len() == 0 {
Expand All @@ -1392,7 +1396,9 @@ impl<S: MutinyStorage> NodeManager<S> {
} else {
return Err(MutinyError::WalletOperationFailed);
};
let invoice = first_node.create_invoice(amount, route_hints).await?;
let invoice = first_node
.create_invoice(amount, route_hints, labels)
.await?;

Ok((invoice.0.into(), invoice.1))
}
Expand Down
19 changes: 8 additions & 11 deletions mutiny-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,20 +1087,17 @@ impl MutinyWallet {
let activity = self.inner.node_manager.get_label_activity(&label).await?;
let mut activity: Vec<ActivityItem> = activity.into_iter().map(|a| a.into()).collect();

// add contact to the activity item if it is one
let Some(contact) = self.inner.node_manager.get_contact(&label)? else {
return Ok(JsValue::from_serde(&activity)?);
// add contact to the activity item it has one, otherwise return the activity list
let contact = match self.inner.node_manager.get_contact(&label)? {
Some(contact) => contact,
None => return Ok(JsValue::from_serde(&activity)?),
};

// if we have a contact, add it to the activity item, remove it as a label
// This is the same as we do in get_activity
for a in activity.iter_mut() {
// find labels that have a contact and add them to the item
for a_label in a.labels.iter() {
if label == *a_label {
a.contacts
.push(TagItem::from((a_label.clone(), contact.clone())));
}
}
// remove labels that have the contact to prevent duplicates
a.contacts
.push(TagItem::from((label.clone(), contact.clone())));
a.labels.retain(|l| l != &label);
}

Expand Down