[PATCH + BUGFIX] Multinut LND re-query and use_mission_control (#746)

* patch LND re-query and use mission control + bugfix on the melt verification

* remove unusued import

* chore: fmt

---------

Co-authored-by: thesimplekid <tsk@thesimplekid.com>
This commit is contained in:
lollerfirst
2025-05-06 14:19:32 +02:00
committed by GitHub
parent 5a3a274875
commit a4c2454e94
2 changed files with 76 additions and 69 deletions

View File

@@ -28,7 +28,7 @@ use cdk::{mint, Bolt11Invoice};
use error::Error;
use fedimint_tonic_lnd::lnrpc::fee_limit::Limit;
use fedimint_tonic_lnd::lnrpc::payment::PaymentStatus;
use fedimint_tonic_lnd::lnrpc::{FeeLimit, Hop, HtlcAttempt, MppRecord};
use fedimint_tonic_lnd::lnrpc::{FeeLimit, Hop, MppRecord};
use fedimint_tonic_lnd::tonic::Code;
use fedimint_tonic_lnd::Client;
use futures::{Stream, StreamExt};
@@ -52,6 +52,9 @@ pub struct Lnd {
}
impl Lnd {
/// Maximum number of attempts at a partial payment
pub const MAX_ROUTE_RETRIES: usize = 50;
/// Create new [`Lnd`]
pub async fn new(
address: String,
@@ -282,51 +285,50 @@ impl MintPayment for Lnd {
let payer_addr = invoice.payment_secret().0.to_vec();
let payment_hash = invoice.payment_hash();
// Create a request for the routes
let route_req = fedimint_tonic_lnd::lnrpc::QueryRoutesRequest {
pub_key: hex::encode(pub_key.serialize()),
amt_msat: u64::from(partial_amount_msat) as i64,
fee_limit: max_fee.map(|f| {
let limit = Limit::Fixed(u64::from(f) as i64);
FeeLimit { limit: Some(limit) }
}),
..Default::default()
};
for attempt in 0..Self::MAX_ROUTE_RETRIES {
// Create a request for the routes
let route_req = fedimint_tonic_lnd::lnrpc::QueryRoutesRequest {
pub_key: hex::encode(pub_key.serialize()),
amt_msat: u64::from(partial_amount_msat) as i64,
fee_limit: max_fee.map(|f| {
let limit = Limit::Fixed(u64::from(f) as i64);
FeeLimit { limit: Some(limit) }
}),
use_mission_control: true,
..Default::default()
};
// Query the routes
let routes_response: fedimint_tonic_lnd::lnrpc::QueryRoutesResponse = self
.client
.lock()
.await
.lightning()
.query_routes(route_req)
.await
.map_err(Error::LndError)?
.into_inner();
// Query the routes
let mut routes_response: fedimint_tonic_lnd::lnrpc::QueryRoutesResponse = self
.client
.lock()
.await
.lightning()
.query_routes(route_req)
.await
.map_err(Error::LndError)?
.into_inner();
let mut payment_response: HtlcAttempt = HtlcAttempt {
..Default::default()
};
// For each route:
// update its MPP record,
// attempt it and check the result
for mut route in routes_response.routes.into_iter() {
let last_hop: &mut Hop = route.hops.last_mut().ok_or(Error::MissingLastHop)?;
// update its MPP record,
// attempt it and check the result
let last_hop: &mut Hop = routes_response.routes[0]
.hops
.last_mut()
.ok_or(Error::MissingLastHop)?;
let mpp_record = MppRecord {
payment_addr: payer_addr.clone(),
total_amt_msat: amount_msat as i64,
};
last_hop.mpp_record = Some(mpp_record);
tracing::debug!("sendToRouteV2 needle");
payment_response = self
let payment_response = self
.client
.lock()
.await
.router()
.send_to_route_v2(fedimint_tonic_lnd::routerrpc::SendToRouteRequest {
payment_hash: payment_hash.to_byte_array().to_vec(),
route: Some(route),
route: Some(routes_response.routes[0].clone()),
..Default::default()
})
.await
@@ -335,38 +337,44 @@ impl MintPayment for Lnd {
if let Some(failure) = payment_response.failure {
if failure.code == 15 {
// Try a different route
tracing::debug!(
"Attempt number {}: route has failed. Re-querying...",
attempt + 1
);
continue;
}
} else {
break;
}
// Get status and maybe the preimage
let (status, payment_preimage) = match payment_response.status {
0 => (MeltQuoteState::Pending, None),
1 => (
MeltQuoteState::Paid,
Some(hex::encode(payment_response.preimage)),
),
2 => (MeltQuoteState::Unpaid, None),
_ => (MeltQuoteState::Unknown, None),
};
// Get the actual amount paid in sats
let mut total_amt: u64 = 0;
if let Some(route) = payment_response.route {
total_amt = (route.total_amt_msat / 1000) as u64;
}
return Ok(MakePaymentResponse {
payment_lookup_id: hex::encode(payment_hash),
payment_proof: payment_preimage,
status,
total_spent: total_amt.into(),
unit: CurrencyUnit::Sat,
});
}
// Get status and maybe the preimage
let (status, payment_preimage) = match payment_response.status {
0 => (MeltQuoteState::Pending, None),
1 => (
MeltQuoteState::Paid,
Some(hex::encode(payment_response.preimage)),
),
2 => (MeltQuoteState::Unpaid, None),
_ => (MeltQuoteState::Unknown, None),
};
// Get the actual amount paid in sats
let mut total_amt: u64 = 0;
if let Some(route) = payment_response.route {
total_amt = (route.total_amt_msat / 1000) as u64;
}
Ok(MakePaymentResponse {
payment_lookup_id: hex::encode(payment_hash),
payment_proof: payment_preimage,
status,
total_spent: total_amt.into(),
unit: CurrencyUnit::Sat,
})
// "We have exhausted all tactical options" -- STEM, Upgrade (2018)
// The payment was not possible within 50 retries.
tracing::error!("Limit of retries reached, payment couldn't succeed.");
Err(Error::PaymentFailed.into())
}
None => {
let pay_req = fedimint_tonic_lnd::lnrpc::SendRequest {

View File

@@ -250,13 +250,10 @@ impl Mint {
};
let partial_amount = match invoice_amount_msats > quote_msats {
true => {
let partial_msats = invoice_amount_msats - quote_msats;
Some(
to_unit(partial_msats, &CurrencyUnit::Msat, &melt_quote.unit)
.map_err(|_| Error::UnsupportedUnit)?,
)
}
true => Some(
to_unit(quote_msats, &CurrencyUnit::Msat, &melt_quote.unit)
.map_err(|_| Error::UnsupportedUnit)?,
),
false => None,
};
@@ -273,9 +270,11 @@ impl Mint {
if amount_to_pay + melt_quote.fee_reserve > inputs_amount_quote_unit {
tracing::debug!(
"Not enough inputs provided: {} msats needed {} msats",
"Not enough inputs provided: {} {} needed {} {}",
inputs_amount_quote_unit,
amount_to_pay
melt_quote.unit,
amount_to_pay,
melt_quote.unit
);
return Err(Error::TransactionUnbalanced(