Skip to content

Commit

Permalink
feat: fixes breez#598
Browse files Browse the repository at this point in the history
  • Loading branch information
vacwmX committed Nov 14, 2023
1 parent 24d4b96 commit 98bae8c
Show file tree
Hide file tree
Showing 13 changed files with 1,351 additions and 124 deletions.
81 changes: 74 additions & 7 deletions libs/sdk-core/src/bridge_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ use crate::input_parser::LnUrlAuthRequestData;
use crate::input_parser::LnUrlErrorData;
use crate::input_parser::LnUrlPayRequestData;
use crate::input_parser::LnUrlWithdrawRequestData;
use crate::invoice::Amount;
use crate::invoice::LNInvoice;
use crate::invoice::LNOffer;
use crate::invoice::Quantity;
use crate::invoice::RouteHint;
use crate::invoice::RouteHintHop;
use crate::lnurl::pay::model::AesSuccessActionDataDecrypted;
Expand Down Expand Up @@ -830,6 +833,31 @@ impl rust2dart::IntoIntoDart<AesSuccessActionDataDecrypted> for AesSuccessAction
}
}

impl support::IntoDart for Amount {
fn into_dart(self) -> support::DartAbi {
match self {
Self::Bitcoin { amount_msats } => {
vec![0.into_dart(), amount_msats.into_into_dart().into_dart()]
}
Self::Currency {
iso4217_code,
amount,
} => vec![
1.into_dart(),
iso4217_code.into_into_dart().into_dart(),
amount.into_into_dart().into_dart(),
],
}
.into_dart()
}
}
impl support::IntoDartExceptPrimitive for Amount {}
impl rust2dart::IntoIntoDart<Amount> for Amount {
fn into_into_dart(self) -> Self {
self
}
}

impl support::IntoDart for BackupFailedData {
fn into_dart(self) -> support::DartAbi {
vec![self.error.into_into_dart().into_dart()].into_dart()
Expand Down Expand Up @@ -1071,13 +1099,13 @@ impl support::IntoDart for InputType {
vec![0.into_dart(), address.into_into_dart().into_dart()]
}
Self::Bolt11 { invoice } => vec![1.into_dart(), invoice.into_into_dart().into_dart()],
Self::Bolt12Offer { offer } => todo!(),
Self::NodeId { node_id } => vec![2.into_dart(), node_id.into_into_dart().into_dart()],
Self::Url { url } => vec![3.into_dart(), url.into_into_dart().into_dart()],
Self::LnUrlPay { data } => vec![4.into_dart(), data.into_into_dart().into_dart()],
Self::LnUrlWithdraw { data } => vec![5.into_dart(), data.into_into_dart().into_dart()],
Self::LnUrlAuth { data } => vec![6.into_dart(), data.into_into_dart().into_dart()],
Self::LnUrlError { data } => vec![7.into_dart(), data.into_into_dart().into_dart()],
Self::Bolt12Offer { offer } => vec![2.into_dart(), offer.into_into_dart().into_dart()],
Self::NodeId { node_id } => vec![3.into_dart(), node_id.into_into_dart().into_dart()],
Self::Url { url } => vec![4.into_dart(), url.into_into_dart().into_dart()],
Self::LnUrlPay { data } => vec![5.into_dart(), data.into_into_dart().into_dart()],
Self::LnUrlWithdraw { data } => vec![6.into_dart(), data.into_into_dart().into_dart()],
Self::LnUrlAuth { data } => vec![7.into_dart(), data.into_into_dart().into_dart()],
Self::LnUrlError { data } => vec![8.into_dart(), data.into_into_dart().into_dart()],
}
.into_dart()
}
Expand Down Expand Up @@ -1129,6 +1157,28 @@ impl rust2dart::IntoIntoDart<LNInvoice> for LNInvoice {
}
}

impl support::IntoDart for LNOffer {
fn into_dart(self) -> support::DartAbi {
vec![
self.chains.into_into_dart().into_dart(),
self.amount.into_dart(),
self.description.into_into_dart().into_dart(),
self.absolute_expiry.into_dart(),
self.issuer.into_dart(),
self.supported_quantity.into_into_dart().into_dart(),
self.signing_pubkey.into_into_dart().into_dart(),
self.metadata.into_dart(),
]
.into_dart()
}
}
impl support::IntoDartExceptPrimitive for LNOffer {}
impl rust2dart::IntoIntoDart<LNOffer> for LNOffer {
fn into_into_dart(self) -> Self {
self
}
}

impl support::IntoDart for LnPaymentDetails {
fn into_dart(self) -> support::DartAbi {
vec![
Expand Down Expand Up @@ -1635,6 +1685,23 @@ impl rust2dart::IntoIntoDart<PrepareSweepResponse> for PrepareSweepResponse {
}
}

impl support::IntoDart for Quantity {
fn into_dart(self) -> support::DartAbi {
match self {
Self::Bounded(field0) => vec![0.into_dart(), field0.into_into_dart().into_dart()],
Self::Unbounded => vec![1.into_dart()],
Self::One => vec![2.into_dart()],
}
.into_dart()
}
}
impl support::IntoDartExceptPrimitive for Quantity {}
impl rust2dart::IntoIntoDart<Quantity> for Quantity {
fn into_into_dart(self) -> Self {
self
}
}

impl support::IntoDart for Rate {
fn into_dart(self) -> support::DartAbi {
vec![
Expand Down
6 changes: 5 additions & 1 deletion libs/sdk-core/src/greenlight/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ impl From<tonic::Status> for NodeError {
JsonRpcErrCode::InvoiceNoDescription => Self::InvoiceNoDescription(status.into()),
JsonRpcErrCode::InvoicePreimageAlreadyExists => {
Self::InvoicePreimageAlreadyExists(status.into())
}
},
JsonRpcErrCode::OfferExpired => Self::OfferExpired(status.into()),
JsonRpcErrCode::OfferBadInvreqReply => Self::OfferReplyError(status.into()),
JsonRpcErrCode::OfferRouteNotFound => Self::RouteNotFound(status.into()),
JsonRpcErrCode::OfferTimeout => Self::OfferTimeout(status.into()),
_ => Self::Generic(status.into()),
},
_ => Self::Generic(status.into()),
Expand Down
61 changes: 28 additions & 33 deletions libs/sdk-core/src/greenlight/node_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use strum_macros::{Display, EnumString};
use tokio::sync::{mpsc, Mutex};
use tokio::time::sleep;
use tokio_stream::{Stream, StreamExt};
use tonic::{Code, Streaming};
use tonic::Streaming;

use crate::invoice::{parse_invoice, InvoiceError};
use crate::models::*;
Expand Down Expand Up @@ -995,43 +995,38 @@ impl NodeAPI for Greenlight {
#[allow(unused_variables)]
async fn fetch_invoice(
&self,
offer: String,
amount_msat: Option<u64>,
quantity: Option<u64>,
recurrence_counter: Option<u64>,
recurrence_start: Option<f64>,
recurrence_label: Option<String>,
timeout: Option<f64>,
payer_note: Option<String>,
req: FetchInvoiceRequest
) -> NodeResult<FetchInvoiceResponse> {
// Get the required pubkeys
let mut client = self.get_node_client().await?;

// Parse the offer locally, to avoid any unnecessary calls to the recipient
if let Err(parse_error) = offer.parse::<Offer>() {
return Err(NodeError::Generic(anyhow!("Invalid offer")));
if let Err(parse_error) = req.offer.parse::<Offer>() {
return Err(NodeError::InvalidOffer(anyhow!("Invalid offer")));
}

let mut client = self.get_node_client().await?;
let response = client
.fetch_invoice(cln::FetchinvoiceRequest {
offer,
amount_msat: amount_msat.map(|msat| cln::Amount { msat }),
quantity,
recurrence_counter,
recurrence_start,
recurrence_label,
timeout,
payer_note,
})
.await;

response
.map(|grpc_response| grpc_response.into())
.map_err(|status| match status.code() {
Code::NotFound => NodeError::RouteNotFound(anyhow!(status.message().to_string())),
Code::Unimplemented => NodeError::NotSupported(),
_ => NodeError::Generic(anyhow!("Could not fetch invoice")),
})
.fetch_invoice(Into::<cln::FetchinvoiceRequest>::into(req))
.await?
.into_inner();

Ok(
FetchInvoiceResponse {
invoice: response.invoice,
changes: response.changes.map(|changes| FetchInvoiceChanges {
description: changes.description,
description_appended: changes.description_appended,
vendor: changes.vendor,
vendor_removed: changes.vendor_removed,
amount_msat: changes.amount_msat.map(|amount| amount.msat),
}),
next_period: response.next_period.map(|np| FetchInvoiceNextPeriod {
counter: np.counter,
start_time: np.starttime,
end_time: np.endtime,
paywindow_start: np.paywindow_start,
paywindow_end: np.paywindow_end,
}),
}
)
}
}

Expand Down
18 changes: 7 additions & 11 deletions libs/sdk-core/src/input_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use anyhow::{anyhow, Result};
use bip21::Uri;
use bitcoin::bech32;
use bitcoin::bech32::FromBase32;
use lightning::offers::offer::Amount;
use lightning::offers::offer::Offer;
use serde::Deserialize;
use serde::Serialize;
Expand Down Expand Up @@ -178,19 +177,16 @@ pub async fn parse(input: &str) -> Result<InputType> {
if let Ok(offer) = input.parse::<Offer>() {
return Ok(Bolt12Offer {
offer: LNOffer {
chains: offer.chains(),
amount_msats: offer.amount().map(|amount| {
match amount {
Amount::Currency { amount, .. } => amount,
Amount::Bitcoin { amount_msats } => amount_msats,
}
.clone()
}),
chains: offer.chains()
.iter()
.map(|chain| chain.to_string())
.collect(),
amount: offer.amount().map(|amount| amount.clone().into()),
description: offer.description().to_string(),
absolute_expiry: offer.absolute_expiry(),
absolute_expiry: offer.absolute_expiry().map(|expiry| expiry.as_secs()),
issuer: offer.issuer().map(|s| s.to_string()),
supported_quantity: offer.supported_quantity().into(),
signing_pubkey: offer.signing_pubkey(),
signing_pubkey: offer.signing_pubkey().to_string(),
metadata: offer.metadata().cloned(),
},
});
Expand Down
31 changes: 22 additions & 9 deletions libs/sdk-core/src/invoice.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use anyhow::anyhow;
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::secp256k1::{self, PublicKey};
use hex::ToHex;
use lightning::routing::gossip::RoutingFees;
use lightning::routing::*;
use lightning_invoice::*;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::num::NonZeroU64;
use std::str::FromStr;
use std::time::{Duration, SystemTimeError, UNIX_EPOCH};
use std::time::{SystemTimeError, UNIX_EPOCH};

pub type InvoiceResult<T, E = InvoiceError> = Result<T, E>;

Expand Down Expand Up @@ -60,7 +58,7 @@ impl From<SystemTimeError> for InvoiceError {

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum Quantity {
Bounded(NonZeroU64),
Bounded(u64),
Unbounded,
One,
}
Expand All @@ -70,20 +68,35 @@ impl From<lightning::offers::offer::Quantity> for Quantity {
match value {
lightning::offers::offer::Quantity::One => Quantity::One,
lightning::offers::offer::Quantity::Unbounded => Quantity::Unbounded,
lightning::offers::offer::Quantity::Bounded(n) => Quantity::Bounded(n),
lightning::offers::offer::Quantity::Bounded(n) => Quantity::Bounded(n.into()),
}
}
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum Amount {
Bitcoin { amount_msats: u64 },
Currency { iso4217_code: [u8; 3], amount: u64 },
}

impl From<lightning::offers::offer::Amount> for Amount {
fn from(amount: lightning::offers::offer::Amount) -> Self {
match amount {
lightning::offers::offer::Amount::Bitcoin { amount_msats } => Amount::Bitcoin { amount_msats },
lightning::offers::offer::Amount::Currency { iso4217_code, amount } => Amount::Currency { iso4217_code, amount },
}
}
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct LNOffer {
pub chains: Vec<ChainHash>,
pub amount_msats: Option<u64>,
pub chains: Vec<String>,
pub amount: Option<Amount>,
pub description: String,
pub absolute_expiry: Option<Duration>,
pub absolute_expiry: Option<u64>,
pub issuer: Option<String>,
pub supported_quantity: Quantity,
pub signing_pubkey: PublicKey,
pub signing_pubkey: String,
pub metadata: Option<Vec<u8>>,
// pub features: Features<OfferContext>,
// pub paths: Vec<BlindedPath>,
Expand Down
56 changes: 30 additions & 26 deletions libs/sdk-core/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,34 @@ pub struct RefundResponse {
pub refund_tx_id: String,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct FetchInvoiceRequest {
pub offer: String,
pub amount_msat: Option<u64>,
pub quantity: Option<u64>,
pub timeout: Option<f64>,
pub payer_note: Option<String>,
// pub recurrence_counter: Option<u64>,
// pub recurrence_start: Option<f64>,
// pub recurrence_label: Option<String>,
}

impl Into<gl_client::pb::cln::FetchinvoiceRequest> for FetchInvoiceRequest {
fn into(self) -> gl_client::pb::cln::FetchinvoiceRequest {
gl_client::pb::cln::FetchinvoiceRequest {
offer: self.offer,
amount_msat: self.amount_msat.map(|msat| cln::Amount { msat }),
quantity: self.quantity,
timeout: self.timeout,
payer_note: self.payer_note,
// Not yet implemented
recurrence_counter: None,
recurrence_start: None,
recurrence_label: None,
}
}
}

#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct FetchInvoiceChanges {
pub description_appended: Option<String>,
Expand All @@ -829,8 +857,8 @@ pub struct FetchInvoiceChanges {
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct FetchInvoiceNextPeriod {
pub counter: u64,
pub starttime: u64,
pub endtime: u64,
pub start_time: u64,
pub end_time: u64,
pub paywindow_start: u64,
pub paywindow_end: u64,
}
Expand All @@ -842,30 +870,6 @@ pub struct FetchInvoiceResponse {
pub next_period: Option<FetchInvoiceNextPeriod>,
}

impl From<tonic::Response<cln::FetchinvoiceResponse>> for FetchInvoiceResponse {
fn from(response: tonic::Response<cln::FetchinvoiceResponse>) -> Self {
let response = response.into_inner();

FetchInvoiceResponse {
invoice: response.invoice,
changes: response.changes.map(|changes| FetchInvoiceChanges {
description: changes.description,
description_appended: changes.description_appended,
vendor: changes.vendor,
vendor_removed: changes.vendor_removed,
amount_msat: changes.amount_msat.map(|amount| amount.msat),
}),
next_period: response.next_period.map(|np| FetchInvoiceNextPeriod {
counter: np.counter,
starttime: np.starttime,
endtime: np.endtime,
paywindow_start: np.paywindow_start,
paywindow_end: np.paywindow_end,
}),
}
}
}

/// Dynamic fee parameters offered by the LSP for opening a new channel.
///
/// After they are received, the client shouldn't change them when calling LSP methods,
Expand Down
Loading

0 comments on commit 98bae8c

Please sign in to comment.