mirror of
https://github.com/daywalker90/cln-nip47.git
synced 2026-08-13 12:33:43 +02:00
workaround for xpay-handle-pay bug: use xpay if cln version supports it
This commit is contained in:
parent
7e02035d24
commit
35f8dd9c73
4 changed files with 189 additions and 70 deletions
215
src/nwc_pay.rs
215
src/nwc_pay.rs
|
|
@ -2,7 +2,7 @@ use std::{path::Path, time::Duration};
|
|||
|
||||
use cln_plugin::Plugin;
|
||||
use cln_rpc::{
|
||||
model::requests::{DecodeRequest, PayRequest},
|
||||
model::requests::{DecodeRequest, PayRequest, XpayRequest},
|
||||
primitives::Amount,
|
||||
ClnRpc,
|
||||
};
|
||||
|
|
@ -11,7 +11,7 @@ use tokio::time;
|
|||
|
||||
use crate::{
|
||||
structs::PluginState,
|
||||
util::{budget_amount_check, load_nwc_store, update_nwc_store},
|
||||
util::{at_or_above_version, budget_amount_check, load_nwc_store, update_nwc_store},
|
||||
};
|
||||
|
||||
pub async fn pay_invoice(
|
||||
|
|
@ -110,67 +110,73 @@ pub async fn pay_invoice(
|
|||
},
|
||||
)?;
|
||||
|
||||
match rpc
|
||||
.call_typed(&PayRequest {
|
||||
amount_msat: params.amount.map(Amount::from_msat),
|
||||
description: None,
|
||||
exemptfee: None,
|
||||
label: None,
|
||||
localinvreqid: None,
|
||||
maxdelay: None,
|
||||
maxfee: None,
|
||||
maxfeepercent: None,
|
||||
partial_msat: None,
|
||||
retry_for: None,
|
||||
riskfactor: None,
|
||||
exclude: None,
|
||||
bolt11: params.invoice,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(o) => {
|
||||
if let Some(ref mut bdg) = nwc_store.budget_msat {
|
||||
*bdg = bdg.saturating_sub(o.amount_sent_msat.msat());
|
||||
update_nwc_store(&mut rpc, label, nwc_store)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Internal,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id.clone(),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
let my_version = plugin.state().config.lock().clone().my_cln_version;
|
||||
|
||||
let preimage = hex::encode(o.payment_preimage.to_vec());
|
||||
Ok((nip47::PayInvoiceResponse { preimage }, id))
|
||||
}
|
||||
Err(e) => match e.code {
|
||||
Some(c) => match c {
|
||||
201 | 207 | 219 => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Other,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
203 | 205 | 209 | 210 => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::PaymentFailed,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
206 => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::InsufficientBalance,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
_ => Err((
|
||||
if at_or_above_version(&my_version, "24.11").map_err(|e| {
|
||||
(
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Internal,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id.clone(),
|
||||
)
|
||||
})? {
|
||||
match rpc
|
||||
.call_typed(&XpayRequest {
|
||||
amount_msat: params.amount.map(Amount::from_msat),
|
||||
maxdelay: None,
|
||||
maxfee: None,
|
||||
partial_msat: None,
|
||||
retry_for: None,
|
||||
layers: None,
|
||||
invstring: params.invoice,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(o) => {
|
||||
if let Some(ref mut bdg) = nwc_store.budget_msat {
|
||||
*bdg = bdg.saturating_sub(o.amount_sent_msat.msat());
|
||||
update_nwc_store(&mut rpc, label, nwc_store)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Internal,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id.clone(),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
let preimage = hex::encode(o.payment_preimage.to_vec());
|
||||
Ok((nip47::PayInvoiceResponse { preimage }, id))
|
||||
}
|
||||
Err(e) => match e.code {
|
||||
Some(c) => match c {
|
||||
207 | 219 => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Other,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
203 | 205 | 209 => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::PaymentFailed,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
_ => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Internal,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
},
|
||||
None => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Internal,
|
||||
message: e.to_string(),
|
||||
|
|
@ -178,14 +184,85 @@ pub async fn pay_invoice(
|
|||
id,
|
||||
)),
|
||||
},
|
||||
None => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Internal,
|
||||
message: e.to_string(),
|
||||
}
|
||||
} else {
|
||||
match rpc
|
||||
.call_typed(&PayRequest {
|
||||
amount_msat: params.amount.map(Amount::from_msat),
|
||||
description: None,
|
||||
exemptfee: None,
|
||||
label: None,
|
||||
localinvreqid: None,
|
||||
maxdelay: None,
|
||||
maxfee: None,
|
||||
maxfeepercent: None,
|
||||
partial_msat: None,
|
||||
retry_for: None,
|
||||
riskfactor: None,
|
||||
exclude: None,
|
||||
bolt11: params.invoice,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(o) => {
|
||||
if let Some(ref mut bdg) = nwc_store.budget_msat {
|
||||
*bdg = bdg.saturating_sub(o.amount_sent_msat.msat());
|
||||
update_nwc_store(&mut rpc, label, nwc_store)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
(
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Internal,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id.clone(),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
let preimage = hex::encode(o.payment_preimage.to_vec());
|
||||
Ok((nip47::PayInvoiceResponse { preimage }, id))
|
||||
}
|
||||
Err(e) => match e.code {
|
||||
Some(c) => match c {
|
||||
201 | 207 | 219 => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Other,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
203 | 205 | 209 | 210 => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::PaymentFailed,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
206 => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::InsufficientBalance,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
_ => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Internal,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
},
|
||||
None => Err((
|
||||
nip47::NIP47Error {
|
||||
code: nip47::ErrorCode::Internal,
|
||||
message: e.to_string(),
|
||||
},
|
||||
id,
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
10
src/parse.rs
10
src/parse.rs
|
|
@ -1,5 +1,8 @@
|
|||
use std::path::Path;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use cln_plugin::ConfiguredPlugin;
|
||||
use cln_rpc::{model::requests::GetinfoRequest, ClnRpc};
|
||||
|
||||
use crate::{
|
||||
structs::{PluginState, TimeUnit},
|
||||
|
|
@ -25,7 +28,14 @@ pub async fn read_startup_options(
|
|||
OPT_RELAYS.name()
|
||||
));
|
||||
};
|
||||
|
||||
let mut rpc = ClnRpc::new(
|
||||
Path::new(&plugin.configuration().lightning_dir).join(&plugin.configuration().rpc_file),
|
||||
)
|
||||
.await?;
|
||||
let version = rpc.call_typed(&GetinfoRequest {}).await?.version;
|
||||
let mut config = state.config.lock();
|
||||
config.my_cln_version = version;
|
||||
for relay in relays_str.into_iter() {
|
||||
log::debug!("RELAY:{}", relay);
|
||||
config.relays.push(nostr_sdk::RelayUrl::parse(&relay)?);
|
||||
|
|
|
|||
|
|
@ -28,10 +28,14 @@ impl PluginState {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
pub relays: Vec<nostr_sdk::RelayUrl>,
|
||||
pub my_cln_version: String,
|
||||
}
|
||||
impl Config {
|
||||
pub fn default() -> Config {
|
||||
Config { relays: Vec::new() }
|
||||
Config {
|
||||
relays: Vec::new(),
|
||||
my_cln_version: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
28
src/util.rs
28
src/util.rs
|
|
@ -79,6 +79,34 @@ pub async fn update_nwc_store(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn at_or_above_version(my_version: &str, min_version: &str) -> Result<bool, anyhow::Error> {
|
||||
let clean_start_my_version = my_version
|
||||
.split_once('v')
|
||||
.ok_or_else(|| anyhow!("Could not find v in version string"))?
|
||||
.1;
|
||||
let full_clean_my_version: String = clean_start_my_version
|
||||
.chars()
|
||||
.take_while(|x| x.is_ascii_digit() || *x == '.')
|
||||
.collect();
|
||||
|
||||
let my_version_parts: Vec<&str> = full_clean_my_version.split('.').collect();
|
||||
let min_version_parts: Vec<&str> = min_version.split('.').collect();
|
||||
|
||||
if my_version_parts.len() <= 1 || my_version_parts.len() > 3 {
|
||||
return Err(anyhow!("Version string parse error: {}", my_version));
|
||||
}
|
||||
for (my, min) in my_version_parts.iter().zip(min_version_parts.iter()) {
|
||||
let my_num: u32 = my.parse()?;
|
||||
let min_num: u32 = min.parse()?;
|
||||
|
||||
if my_num != min_num {
|
||||
return Ok(my_num > min_num);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(my_version_parts.len() >= min_version_parts.len())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_budget_check() {
|
||||
assert!(budget_amount_check(Some(1), Some(1), Some(2)).is_ok());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue