Skip to content

Commit

Permalink
Refactor getting invoice by hash
Browse files Browse the repository at this point in the history
  • Loading branch information
TonyGiorgio committed Apr 26, 2024
1 parent 95af72c commit 396c62a
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 166 deletions.
86 changes: 2 additions & 84 deletions mutiny-core/src/federation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ use crate::{
key::{create_root_child_key, ChildKey},
logging::MutinyLogger,
onchain::coin_type_from_network,
storage::{
get_payment_info, list_payment_info, persist_payment_info, MutinyStorage, VersionedValue,
},
storage::{list_payment_info, persist_payment_info, MutinyStorage, VersionedValue},
utils::sleep,
HTLCStatus, MutinyInvoice, DEFAULT_PAYMENT_TIMEOUT,
};
Expand All @@ -17,7 +15,6 @@ use bip39::Mnemonic;
use bitcoin::secp256k1::{SecretKey, ThirtyTwoByteHash};
use bitcoin::{
bip32::{ChildNumber, DerivationPath, ExtendedPrivKey},
hashes::sha256,
secp256k1::Secp256k1,
Network,
};
Expand Down Expand Up @@ -57,9 +54,7 @@ use fedimint_wallet_client::{WalletClientInit, WalletClientModule};
use futures::{select, FutureExt};
use futures_util::{pin_mut, StreamExt};
use hex_conservative::{DisplayHex, FromHex};
use lightning::{
ln::PaymentHash, log_debug, log_error, log_info, log_trace, log_warn, util::logger::Logger,
};
use lightning::{log_debug, log_error, log_info, log_trace, log_warn, util::logger::Logger};
use lightning_invoice::Bolt11Invoice;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(not(target_arch = "wasm32"))]
Expand All @@ -76,12 +71,6 @@ use std::{
#[cfg(target_arch = "wasm32")]
use web_time::Instant;

// The amount of time in milliseconds to wait for
// checking the status of a fedimint payment. This
// is to work around their stream status checking
// when wanting just the current status.
const FEDIMINT_STATUS_TIMEOUT_CHECK_MS: u64 = 30;

// The maximum amount of operations we try to pull
// from fedimint when we need to search through
// their internal list.
Expand Down Expand Up @@ -516,77 +505,6 @@ impl<S: MutinyStorage> FederationClient<S> {
Ok(())
}

pub async fn get_invoice_by_hash(
&self,
hash: &sha256::Hash,
) -> Result<MutinyInvoice, MutinyError> {
log_trace!(self.logger, "get_invoice_by_hash");

// Try to get the invoice from storage first
let (invoice, inbound) = match get_payment_info(&self.storage, hash, &self.logger) {
Ok(i) => i,
Err(e) => {
log_error!(self.logger, "could not get invoice by hash: {e}");
return Err(e);
}
};

log_trace!(self.logger, "retrieved invoice by hash");

if matches!(invoice.status, HTLCStatus::InFlight | HTLCStatus::Pending) {
log_trace!(self.logger, "invoice still in flight, getting operations");
// If the invoice is InFlight or Pending, check the operation log for updates
let lightning_module = Arc::new(
self.fedimint_client
.get_first_module::<LightningClientModule>(),
);

let operations = self
.fedimint_client
.operation_log()
.list_operations(FEDIMINT_OPERATIONS_LIST_MAX, None)
.await;

log_trace!(
self.logger,
"going to go through {} operations",
operations.len()
);
for (key, entry) in operations {
if entry.operation_module_kind() == LightningCommonInit::KIND.as_str() {
if let Some(updated_invoice) = process_operation_until_timeout(
self.logger.clone(),
entry.meta(),
hash.into_32(),
key.operation_id,
&lightning_module,
Some(FEDIMINT_STATUS_TIMEOUT_CHECK_MS),
self.stop.clone(),
)
.await
{
self.maybe_update_after_checking_fedimint(updated_invoice.clone())?;
return Ok(updated_invoice);
}
} else {
log_warn!(
self.logger,
"Unsupported module: {}",
entry.operation_module_kind()
);
}
}
} else {
// If the invoice is not InFlight or Pending, return it directly
log_trace!(self.logger, "returning final invoice");
// TODO labels
return MutinyInvoice::from(invoice, PaymentHash(hash.into_32()), inbound, vec![]);
}

log_debug!(self.logger, "could not find invoice");
Err(MutinyError::NotFound)
}

pub(crate) async fn pay_invoice(
&self,
invoice: Bolt11Invoice,
Expand Down
20 changes: 5 additions & 15 deletions mutiny-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ mod test_utils;
pub use crate::gossip::{GOSSIP_SYNC_TIME_KEY, NETWORK_GRAPH_KEY, PROB_SCORER_KEY};
pub use crate::keymanager::generate_seed;
pub use crate::ldkstorage::{CHANNEL_CLOSURE_PREFIX, CHANNEL_MANAGER_KEY, MONITORS_PREFIX_KEY};
use crate::nostr::primal::{PrimalApi, PrimalClient};
use crate::storage::{
get_payment_hash_from_key, list_payment_info, persist_payment_info, update_nostr_contact_list,
IndexItem, MutinyStorage, DEVICE_ID_KEY, EXPECTED_NETWORK_KEY, NEED_FULL_SYNC_KEY,
Expand Down Expand Up @@ -79,6 +78,10 @@ use crate::{
nostr::nwc::{BudgetPeriod, BudgetedSpendingConditions, NwcProfileTag, SpendingConditions},
subscription::MutinySubscriptionClient,
};
use crate::{
nostr::primal::{PrimalApi, PrimalClient},
storage::get_invoice_by_hash,
};
use crate::{nostr::NostrManager, utils::sleep};
use ::nostr::nips::nip47::Method;
use ::nostr::nips::nip57;
Expand Down Expand Up @@ -1968,20 +1971,7 @@ impl<S: MutinyStorage> MutinyWallet<S> {
&self,
hash: &sha256::Hash,
) -> Result<MutinyInvoice, MutinyError> {
// First, try to find the invoice in the node manager
if let Ok(invoice) = self.node_manager.get_invoice_by_hash(hash).await {
return Ok(invoice);
}

// If not found in node manager, search in federations
let federations = self.federations.read().await;
for (_fed_id, federation) in federations.iter() {
if let Ok(invoice) = federation.get_invoice_by_hash(hash).await {
return Ok(invoice);
}
}

Err(MutinyError::NotFound)
get_invoice_by_hash(hash, &self.storage, &self.logger)
}

/// Checks whether or not the user is subscribed to Mutiny+.
Expand Down
62 changes: 13 additions & 49 deletions mutiny-core/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1336,23 +1336,6 @@ impl<S: MutinyStorage> Node<S> {
Ok(())
}

pub fn get_invoice_by_hash(&self, payment_hash: &Sha256) -> Result<MutinyInvoice, MutinyError> {
let (payment_info, inbound) = self.get_payment_info_from_persisters(payment_hash)?;
let labels_map = self.persister.storage.get_invoice_labels()?;
let labels = payment_info
.bolt11
.as_ref()
.and_then(|inv| labels_map.get(inv).cloned())
.unwrap_or_default();

MutinyInvoice::from(
payment_info,
PaymentHash(payment_hash.into_32()),
inbound,
labels,
)
}

/// Gets all the closed channels for this node
pub fn get_channel_closure(
&self,
Expand All @@ -1366,32 +1349,6 @@ impl<S: MutinyStorage> Node<S> {
self.persister.list_channel_closures()
}

pub fn get_payment_info_from_persisters(
&self,
payment_hash: &bitcoin::hashes::sha256::Hash,
) -> Result<(PaymentInfo, bool), MutinyError> {
// try inbound first
if let Some(payment_info) = read_payment_info(
&self.persister.storage,
&payment_hash.into_32(),
true,
&self.logger,
) {
return Ok((payment_info, true));
}

// if no inbound check outbound
match read_payment_info(
&self.persister.storage,
&payment_hash.into_32(),
false,
&self.logger,
) {
Some(payment_info) => Ok((payment_info, false)),
None => Err(MutinyError::NotFound),
}
}

fn retry_strategy() -> Retry {
Retry::Attempts(15)
}
Expand Down Expand Up @@ -2431,6 +2388,7 @@ pub(crate) fn default_user_config(accept_underpaying_htlcs: bool) -> UserConfig
#[cfg(not(target_arch = "wasm32"))]
mod tests {
use super::*;
use crate::get_invoice_by_hash;
use crate::node::{map_sending_failure, parse_peer_info};
use crate::storage::MemoryStorage;
use crate::test_utils::*;
Expand Down Expand Up @@ -2589,6 +2547,7 @@ mod tests {
async fn test_create_invoice() {
let storage = MemoryStorage::default();
let node = create_node(storage.clone()).await;
let logger = Arc::new(MutinyLogger::default());

let now = crate::utils::now().as_secs();

Expand All @@ -2607,8 +2566,8 @@ mod tests {
_ => panic!("unexpected invoice description"),
}

let from_storage = node.get_invoice_by_hash(invoice.payment_hash()).unwrap();
let by_hash = node.get_invoice_by_hash(invoice.payment_hash()).unwrap();
let from_storage = get_invoice_by_hash(invoice.payment_hash(), &storage, &logger).unwrap();
let by_hash = get_invoice_by_hash(invoice.payment_hash(), &storage, &logger).unwrap();

assert_eq!(from_storage, by_hash);
assert_eq!(from_storage.bolt11, Some(invoice.clone()));
Expand Down Expand Up @@ -2723,16 +2682,20 @@ mod tests {
#[cfg(test)]
#[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::{error::MutinyError, storage::persist_payment_info};
use crate::{
event::{MillisatAmount, PaymentInfo},
storage::get_invoice_by_hash,
};
use crate::{labels::LabelStorage, logging::MutinyLogger};
use crate::{HTLCStatus, PrivacyLevel};
use itertools::Itertools;
use lightning::ln::channelmanager::PaymentId;
use lightning::ln::PaymentHash;
use lightning_invoice::Bolt11InvoiceDescription;
use std::sync::Arc;
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};

wasm_bindgen_test_configure!(run_in_browser);
Expand All @@ -2748,6 +2711,7 @@ mod wasm_test {
async fn test_create_invoice() {
let storage = MemoryStorage::default();
let node = create_node(storage.clone()).await;
let logger = Arc::new(MutinyLogger::default());

let now = crate::utils::now().as_secs();

Expand All @@ -2768,8 +2732,8 @@ mod wasm_test {
_ => panic!("unexpected invoice description"),
}

let from_storage = node.get_invoice_by_hash(invoice.payment_hash()).unwrap();
let by_hash = node.get_invoice_by_hash(invoice.payment_hash()).unwrap();
let from_storage = get_invoice_by_hash(invoice.payment_hash(), &storage, &logger).unwrap();
let by_hash = get_invoice_by_hash(invoice.payment_hash(), &storage, &logger).unwrap();

assert_eq!(from_storage, by_hash);
assert_eq!(from_storage.bolt11, Some(invoice.clone()));
Expand Down
17 changes: 0 additions & 17 deletions mutiny-core/src/nodemanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ use bitcoin::address::NetworkUnchecked;
use bitcoin::bip32::ExtendedPrivKey;
use bitcoin::blockdata::script;
use bitcoin::hashes::hex::FromHex;
use bitcoin::hashes::sha256;
use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::secp256k1::PublicKey;
use bitcoin::{Address, Network, OutPoint, Transaction, Txid};
Expand Down Expand Up @@ -1415,22 +1414,6 @@ impl<S: MutinyStorage> NodeManager<S> {
.await
}

/// Gets an invoice from the node manager.
/// This includes sent and received invoices.
pub(crate) async fn get_invoice_by_hash(
&self,
hash: &sha256::Hash,
) -> Result<MutinyInvoice, MutinyError> {
let nodes = self.nodes.read().await;
for (_, node) in nodes.iter() {
if let Ok(inv) = node.get_invoice_by_hash(hash) {
return Ok(inv);
}
}

Err(MutinyError::NotFound)
}

pub async fn get_channel_closure(
&self,
user_channel_id: u128,
Expand Down
19 changes: 18 additions & 1 deletion mutiny-core/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::event::HTLCStatus;
use crate::labels::LabelStorage;
use crate::nodemanager::{ChannelClosure, NodeStorage};
use crate::utils::{now, spawn};
use crate::vss::{MutinyVssClient, VssKeyValueItem};
Expand All @@ -12,6 +12,7 @@ use crate::{
error::{MutinyError, MutinyStorageError},
event::PaymentInfo,
};
use crate::{event::HTLCStatus, MutinyInvoice};
use crate::{ldkstorage::CHANNEL_MANAGER_KEY, utils::sleep};
use async_trait::async_trait;
use bdk::chain::{Append, PersistBackend};
Expand Down Expand Up @@ -934,6 +935,22 @@ pub(crate) fn persist_payment_info<S: MutinyStorage>(
Ok(())
}

pub(crate) fn get_invoice_by_hash<S: MutinyStorage>(
hash: &bitcoin::hashes::sha256::Hash,
storage: &S,
logger: &MutinyLogger,
) -> Result<MutinyInvoice, MutinyError> {
let (payment_info, inbound) = get_payment_info(storage, hash, logger)?;
let labels_map = storage.get_invoice_labels()?;
let labels = payment_info
.bolt11
.as_ref()
.and_then(|inv| labels_map.get(inv).cloned())
.unwrap_or_default();

MutinyInvoice::from(payment_info, PaymentHash(hash.into_32()), inbound, labels)
}

pub(crate) fn get_payment_info<S: MutinyStorage>(
storage: &S,
payment_hash: &bitcoin::hashes::sha256::Hash,
Expand Down

0 comments on commit 396c62a

Please sign in to comment.