From 073332a3fe50b2fb4d06801e4d8e258869c94c01 Mon Sep 17 00:00:00 2001 From: Dolu Date: Wed, 17 Aug 2022 13:28:17 +0200 Subject: [PATCH 01/11] feat: add get_invoice for EclairRest backend --- core/src/backends/cln/grpc/node.rs | 8 ++- core/src/backends/eclair/rest/node.rs | 21 +++++- core/src/backends/eclair/rest/types.rs | 96 ++++++++++++++++++++++++++ core/src/backends/lnd/rest/node.rs | 8 ++- core/src/lib.rs | 1 + core/src/node.rs | 7 +- core/src/tools.rs | 7 ++ schemas/src/main.rs | 7 +- src/cli.rs | 20 ++++++ 9 files changed, 168 insertions(+), 7 deletions(-) create mode 100644 core/src/tools.rs diff --git a/core/src/backends/cln/grpc/node.rs b/core/src/backends/cln/grpc/node.rs index db3ee94..58fbf64 100644 --- a/core/src/backends/cln/grpc/node.rs +++ b/core/src/backends/cln/grpc/node.rs @@ -3,7 +3,7 @@ use tonic::transport::{Certificate, Channel, ClientTlsConfig, Endpoint, Identity use crate::error::Error; use crate::node::NodeMethods; use crate::types::{ - CreateInvoiceParams, CreateInvoiceResult, NodeInfo, PayInvoiceParams, PayInvoiceResult, + CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, PayInvoiceParams, PayInvoiceResult, }; use super::config::ClnGrpcConfig; @@ -68,6 +68,12 @@ impl NodeMethods for ClnGrpc { Ok(result) } + async fn get_invoice(&self, _payment_hash: String) -> Result { + Err(Error::UnknownError(String::from( + "get_invoice() not implemented yet", + ))) + } + async fn pay_invoice(&self, invoice: PayInvoiceParams) -> Result { let mut client = self.get_client().await?; diff --git a/core/src/backends/eclair/rest/node.rs b/core/src/backends/eclair/rest/node.rs index 3782c6b..f4bb447 100644 --- a/core/src/backends/eclair/rest/node.rs +++ b/core/src/backends/eclair/rest/node.rs @@ -1,13 +1,15 @@ +use std::collections::HashMap; + use crate::error::Error; use crate::node::NodeMethods; use crate::types::{ - CreateInvoiceParams, CreateInvoiceResult, NodeInfo, PayInvoiceParams, PayInvoiceResult, + CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, PayInvoiceParams, PayInvoiceResult, }; use super::config::EclairRestConfig; use super::types::{ ApiError, ChannelState, CreateInvoiceRequest, CreateInvoiceResponse, GetChannelsResponse, - GetInfoResponse, PayInvoiceRequest, PayInvoiceResponse, + GetInfoResponse, PayInvoiceRequest, PayInvoiceResponse, InvoiceResponse, }; pub struct EclairRest { @@ -119,4 +121,19 @@ impl NodeMethods for EclairRest { Ok(data.try_into()?) } + + async fn get_invoice(&self, payment_hash: String) -> Result { + let url = format!("{}/getreceivedinfo", self.config.url); + + println!("{}", payment_hash); + + let mut params = HashMap::new(); + params.insert("paymentHash", &payment_hash); + + let mut response = self.client.post(&url).form(¶ms).send().await?; + response = Self::on_response(response).await?; + let data: InvoiceResponse = response.json().await?; + + Ok(data.into()) + } } diff --git a/core/src/backends/eclair/rest/types.rs b/core/src/backends/eclair/rest/types.rs index 57f12ab..ab225bc 100644 --- a/core/src/backends/eclair/rest/types.rs +++ b/core/src/backends/eclair/rest/types.rs @@ -1,5 +1,6 @@ #![allow(clippy::from_over_into)] +use crate::{tools::msat_to_sat, types::*}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -84,6 +85,101 @@ pub enum ChannelState { Pending, } +#[derive(Debug, Deserialize)] +pub struct Chain { + pub chain: String, + pub network: String, +} + +#[derive(Debug, Deserialize)] +pub struct Feature { + pub name: String, + pub is_required: bool, + pub is_known: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct InvoiceResponse { + pub payment_request: PayementRequestResponse, + pub payment_preimage: Option, + pub status: InvoiceStatusResponse, + pub created_at: InvoiceDateResponse, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PayementRequestResponse { + pub prefix: String, + pub timestamp: i64, + pub node_id: String, + pub serialized: String, + pub description: String, + pub payment_hash: String, + pub payment_metadata: String, + pub expiry: i32, + pub min_final_cltv_expiry: u32, + pub amount: u64, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum InvoiceStatusTypeResponse { + Received, + Pending, + Expired, +} + +#[derive(Debug, Deserialize)] +pub struct InvoiceDateResponse { + pub unix: i64, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct InvoiceStatusResponse { + // "type" is a reserved keyword. Fix it with an _ + pub _type: InvoiceStatusTypeResponse, + pub amount: Option, + pub received_at: Option, +} + +impl Into for InvoiceResponse { + fn into(self) -> Invoice { + let mut settle_date: Option = None; + let settled = match self.status._type { + InvoiceStatusTypeResponse::Received => match self.status.received_at { + Some(e) => { + settle_date = Some(e.unix); + true + } + None => true, + }, + _ => false, + }; + + let status = match self.status._type { + InvoiceStatusTypeResponse::Received => crate::types::InvoiceStatus::Settled, + InvoiceStatusTypeResponse::Pending => crate::types::InvoiceStatus::Pending, + InvoiceStatusTypeResponse::Expired => crate::types::InvoiceStatus::Cancelled, + }; + + Invoice { + bolt11: self.payment_request.serialized, + memo: self.payment_request.description, + amount: msat_to_sat(&self.payment_request.amount), + amount_msat: self.payment_request.amount, + pre_image: self.payment_preimage, + payment_hash: self.payment_request.payment_hash, + settled, + settle_date, + creation_date: self.created_at.unix, + expiry: self.payment_request.expiry, + status, + } + } +} + impl Into for GetInfoResponse { fn into(self) -> NodeInfo { let network = match self.network.as_ref() { diff --git a/core/src/backends/lnd/rest/node.rs b/core/src/backends/lnd/rest/node.rs index d972bad..ad58edf 100644 --- a/core/src/backends/lnd/rest/node.rs +++ b/core/src/backends/lnd/rest/node.rs @@ -1,7 +1,7 @@ use crate::error::Error; use crate::node::NodeMethods; use crate::types::{ - CreateInvoiceParams, CreateInvoiceResult, NodeInfo, PayInvoiceParams, PayInvoiceResult, + CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, PayInvoiceParams, PayInvoiceResult, }; use super::config::LndRestConfig; @@ -96,4 +96,10 @@ impl NodeMethods for LndRest { Ok(data.try_into()?) } + + async fn get_invoice(&self, _payment_hash: String) -> Result { + Err(Error::UnknownError(String::from( + "get_invoice() not implemented yet", + ))) + } } diff --git a/core/src/lib.rs b/core/src/lib.rs index bce2008..2beb4ca 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,5 +1,6 @@ pub mod backends; pub mod error; pub mod node; +pub mod tools; pub mod types; pub mod utils; diff --git a/core/src/node.rs b/core/src/node.rs index fc5dab3..bb49fc0 100644 --- a/core/src/node.rs +++ b/core/src/node.rs @@ -3,7 +3,7 @@ use crate::backends::eclair::rest::node::EclairRest; use crate::backends::lnd::rest::node::LndRest; use crate::error::Error; use crate::types::{ - Backend, CreateInvoiceParams, CreateInvoiceResult, NodeConfig, NodeInfo, PayInvoiceParams, + Backend, CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, PayInvoiceParams, PayInvoiceResult, }; @@ -15,6 +15,7 @@ pub trait NodeMethods { ) -> Result; async fn get_info(&self) -> Result; async fn pay_invoice(&self, invoice: PayInvoiceParams) -> Result; + async fn get_invoice(&self, payment_hash: String) -> Result; } pub struct Node { @@ -67,4 +68,8 @@ impl NodeMethods for Node { async fn pay_invoice(&self, invoice: PayInvoiceParams) -> Result { self.node.pay_invoice(invoice).await } + + async fn get_invoice(&self, payment_hash: String) -> Result { + self.node.get_invoice(payment_hash).await + } } diff --git a/core/src/tools.rs b/core/src/tools.rs new file mode 100644 index 0000000..0ed8957 --- /dev/null +++ b/core/src/tools.rs @@ -0,0 +1,7 @@ +pub fn sat_to_msat(sat: &u64) -> u64 { + sat * 1000 +} + +pub fn msat_to_sat(msat: &u64) -> u64 { + msat / 1000 +} diff --git a/schemas/src/main.rs b/schemas/src/main.rs index 2e04039..04284a8 100644 --- a/schemas/src/main.rs +++ b/schemas/src/main.rs @@ -2,8 +2,8 @@ use schemars::{schema::RootSchema, schema_for}; use std::env; use una_core::types::{ - Backend, ChannelStats, CreateInvoiceParams, CreateInvoiceResult, Network, NodeConfig, NodeInfo, - PayInvoiceParams, PayInvoiceResult, + Backend, ChannelStats, CreateInvoiceParams, CreateInvoiceResult, Invoice, Network, NodeConfig, + NodeInfo, PayInvoiceParams, PayInvoiceResult, }; fn write_schema(dir: &std::path::Path, name: &str, schema: &RootSchema) -> std::io::Result<()> { @@ -42,6 +42,9 @@ fn main() { let schema = schema_for!(CreateInvoiceResult); write_schema(&dir, "create_invoice_result", &schema).unwrap(); + let schema = schema_for!(Invoice); + write_schema(&dir, "invoice", &schema).unwrap(); + let schema = schema_for!(PayInvoiceParams); write_schema(&dir, "pay_invoice_params", &schema).unwrap(); diff --git a/src/cli.rs b/src/cli.rs index 1652d83..100d267 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -87,6 +87,16 @@ async fn main() -> Result<(), Box> { .help("description"), ), ) + .subcommand( + Command::new("getinvoice") + .about("get invoice infos from the node") + .arg( + Arg::new("payment_hash") + .required(true) + .index(1) + .help("payment hash in hex format"), + ), + ) .get_matches(); let backend: Backend = matches @@ -142,6 +152,16 @@ async fn main() -> Result<(), Box> { println!("{:}", serde_json::to_string_pretty(&invoice).unwrap()); } + "getinvoice" => { + let args = command_args; + let payment_hash: &str = args + .value_of("payment_hash") + .expect("payment_hash is a required field"); + + let invoice = node.get_invoice(String::from(payment_hash)).await.unwrap(); + + println!("{:}", serde_json::to_string_pretty(&invoice).unwrap()); + } _ => { println!("invalid command. use una-cli --help to see usage instructions.") } From 9f7ce147d5576e7f4df75966f8b3ab443806073d Mon Sep 17 00:00:00 2001 From: Dolu Date: Wed, 17 Aug 2022 17:18:05 +0200 Subject: [PATCH 02/11] feat(lnd-rest): add get_invoice for LndRest backend --- core/src/backends/lnd/rest/node.rs | 16 ++++--- core/src/backends/lnd/rest/types.rs | 69 ++++++++++++++++++++++++++++- core/src/tools.rs | 18 ++++++++ 3 files changed, 96 insertions(+), 7 deletions(-) diff --git a/core/src/backends/lnd/rest/node.rs b/core/src/backends/lnd/rest/node.rs index ad58edf..5c6d0ea 100644 --- a/core/src/backends/lnd/rest/node.rs +++ b/core/src/backends/lnd/rest/node.rs @@ -7,7 +7,7 @@ use crate::types::{ use super::config::LndRestConfig; use super::types::{ ApiError, CreateInvoiceRequest, CreateInvoiceResponse, GetInfoResponse, SendPaymentSyncRequest, - SendPaymentSyncResponse, + SendPaymentSyncResponse, InvoiceResponse }; pub struct LndRest { @@ -97,9 +97,15 @@ impl NodeMethods for LndRest { Ok(data.try_into()?) } - async fn get_invoice(&self, _payment_hash: String) -> Result { - Err(Error::UnknownError(String::from( - "get_invoice() not implemented yet", - ))) + async fn get_invoice(&self, payment_hash: String) -> Result { + let url = format!("{0}/v1/invoice/{1}", self.config.url, payment_hash); + + let mut response = self.client.get(&url).send().await?; + + response = Self::on_response(response).await?; + + let data: InvoiceResponse = response.json().await?; + + Ok(data.try_into()?) } } diff --git a/core/src/backends/lnd/rest/types.rs b/core/src/backends/lnd/rest/types.rs index 7b6724e..8463ba6 100644 --- a/core/src/backends/lnd/rest/types.rs +++ b/core/src/backends/lnd/rest/types.rs @@ -1,12 +1,13 @@ #![allow(clippy::from_over_into)] -use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use crate::error::Error; +use crate::tools::convert_base64_to_hex; use crate::{types::*, utils}; pub type Base64String = String; +use crate::{error::Error, tools::parse_number}; +use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize)] pub struct ApiError { @@ -132,6 +133,70 @@ impl Into for GetInfoResponse { } } +#[derive(Debug, Deserialize)] +pub struct InvoiceResponse { + memo: String, + r_preimage: String, + r_hash: String, + value: String, + value_msat: String, + settled: bool, + creation_date: String, + settle_date: String, + payment_request: String, + expiry: String, + state: InvoiceStateResponse, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "UPPERCASE")] +pub enum InvoiceStateResponse { + Open, + Settled, + Cancelled, + Accepted, +} + +impl TryInto for InvoiceResponse { + type Error = Error; + + fn try_into(self) -> Result { + let mut settle_date: Option = None; + if self.settled { + settle_date = + Some(parse_number(&self.settle_date).expect("settle_date should be a number")); + } + + let status = match self.state { + InvoiceStateResponse::Open => crate::types::InvoiceStatus::Pending, + InvoiceStateResponse::Settled => crate::types::InvoiceStatus::Settled, + InvoiceStateResponse::Cancelled => crate::types::InvoiceStatus::Cancelled, + InvoiceStateResponse::Accepted => crate::types::InvoiceStatus::Accepted, + }; + + let invoice = Invoice { + bolt11: self.payment_request, + memo: self.memo, + amount: parse_number(&self.value).expect("amt_paid_sat should be a number"), + amount_msat: parse_number(&self.value_msat).expect("amt_paid_msat should be a number"), + pre_image: Some( + convert_base64_to_hex(&self.r_preimage) + .expect("coudln't convert r_preimage from base64 to hex"), + ), + payment_hash: convert_base64_to_hex(&self.r_hash) + .expect("coudln't convert r_hash from base64 to hex"), + settled: self.settled, + settle_date, + creation_date: parse_number(&self.creation_date) + .expect("creation_date should be a number"), + expiry: parse_number(&self.expiry).expect("expiry should be a number"), + status, + }; + + Ok(invoice) + } +} + #[derive(Debug, Serialize)] pub struct FeeLimit { pub fixed: Option, diff --git a/core/src/tools.rs b/core/src/tools.rs index 0ed8957..bfbf6d7 100644 --- a/core/src/tools.rs +++ b/core/src/tools.rs @@ -1,3 +1,7 @@ +use std::str::FromStr; + +use crate::error::Error; + pub fn sat_to_msat(sat: &u64) -> u64 { sat * 1000 } @@ -5,3 +9,17 @@ pub fn sat_to_msat(sat: &u64) -> u64 { pub fn msat_to_sat(msat: &u64) -> u64 { msat / 1000 } + +pub fn convert_base64_to_hex(hex: &str) -> Result { + let base64_decoded = base64::decode(hex); + match base64_decoded { + Ok(e) => Ok(hex::encode(e)), + Err(_) => Err(Error::ConversionError(String::from( + "coudln't convert from base64 to hex", + ))), + } +} + +pub fn parse_number(text: &str) -> Result { + text.trim().parse::() +} From 0bce1a23d29629fe9a7ccbef1e51debbd9e54579 Mon Sep 17 00:00:00 2001 From: Dolu Date: Fri, 19 Aug 2022 10:35:46 +0200 Subject: [PATCH 03/11] style: format code + gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c86c3ca..0bfe4a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /Cargo.lock /.DS_Store +.vscode/ From 5c4d562ff2234a05fd7ab6bad0d0de67f0fe578f Mon Sep 17 00:00:00 2001 From: Dolu Date: Fri, 19 Aug 2022 15:30:53 +0200 Subject: [PATCH 04/11] feat(cln-grpc): add get_invoice for ClnGrpc backend --- core/Cargo.toml | 1 + core/src/backends/cln/grpc/node.rs | 23 +++++++-- core/src/backends/cln/grpc/pb.rs | 65 +++++++++++++++++++++++++- core/src/backends/eclair/rest/types.rs | 6 +-- core/src/backends/lnd/rest/types.rs | 2 +- core/src/types.rs | 6 +-- 6 files changed, 90 insertions(+), 13 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 20e5e2f..0183ba7 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,6 +17,7 @@ tonic = { version = "0.8.0", features = ["tls"] } prost = "0.11" cuid = "1.2.0" http = "0.2.8" +lightning-invoice = "0.18.0" regex = "1.6.0" [build-dependencies] diff --git a/core/src/backends/cln/grpc/node.rs b/core/src/backends/cln/grpc/node.rs index 58fbf64..f296469 100644 --- a/core/src/backends/cln/grpc/node.rs +++ b/core/src/backends/cln/grpc/node.rs @@ -7,7 +7,7 @@ use crate::types::{ }; use super::config::ClnGrpcConfig; -use super::pb::{node_client::NodeClient, GetinfoRequest, InvoiceRequest, PayRequest}; +use super::pb::{node_client::NodeClient, GetinfoRequest, InvoiceRequest, PayRequest, ListinvoicesRequest}; pub struct ClnGrpc { endpoint: Endpoint, @@ -68,10 +68,23 @@ impl NodeMethods for ClnGrpc { Ok(result) } - async fn get_invoice(&self, _payment_hash: String) -> Result { - Err(Error::UnknownError(String::from( - "get_invoice() not implemented yet", - ))) + async fn get_invoice(&self, payment_hash: String) -> Result { + let mut client = self.get_client().await?; + + let payment_hash_hex = match hex::decode(payment_hash) { + Ok(hex) => hex, + Err(_) => return Err(Error::ApiError(String::from("Invalid payment_hash"))), + }; + + let request = ListinvoicesRequest { + payment_hash: Some(payment_hash_hex), + label: None, + invstring: None, + offer_id: None, + }; + let response = client.list_invoices(request).await?.into_inner(); + + Ok(response.try_into()?) } async fn pay_invoice(&self, invoice: PayInvoiceParams) -> Result { diff --git a/core/src/backends/cln/grpc/pb.rs b/core/src/backends/cln/grpc/pb.rs index bf53bd9..43a406b 100644 --- a/core/src/backends/cln/grpc/pb.rs +++ b/core/src/backends/cln/grpc/pb.rs @@ -1,6 +1,8 @@ #![allow(clippy::from_over_into)] -use crate::{types::*, utils}; +use std::time::UNIX_EPOCH; + +use crate::{error::Error, tools::msat_to_sat, types::*, utils}; use cuid; tonic::include_proto!("cln"); @@ -67,6 +69,67 @@ impl Into for GetinfoResponse { } } +impl TryInto for ListinvoicesResponse { + type Error = Error; + + fn try_into(self) -> Result { + println!("{:?}", self); + let invoice: &ListinvoicesInvoices = self.invoices.get(0).expect("No invoice found"); + // let bolt11 = (&invoice.bolt11).ok_or_else(|| Error::ApiError(String::from("bolt11 missing")))?.as_ref(); + let bolt11 = match &invoice.bolt11 { + Some(bolt11) => bolt11.as_ref(), + None => return Err(Error::ApiError(String::from("bolt11 missing"))), + }; + + let decoded_invoice = str::parse::(bolt11) + .expect("bolt11 is not a valid invoice"); + + let mut settle_date: Option = None; + let settled = match invoice.status { + 1 => match invoice.paid_at { + Some(e) => { + settle_date = Some(e); + true + } + None => true, + }, + _ => false, + }; + + let status = match invoice.status { + 0 => crate::types::InvoiceStatus::Pending, + 1 => crate::types::InvoiceStatus::Settled, + 2 => crate::types::InvoiceStatus::Cancelled, + _ => crate::types::InvoiceStatus::Accepted, + }; + + let amount_msat = &decoded_invoice + .amount_milli_satoshis() + .ok_or_else(|| Error::ApiError(String::from("amount_milli_satoshis missing")))?; + + Ok(Invoice { + bolt11: String::from(bolt11), + memo: match &invoice.description { + Some(description) => String::from(description), + None => return Err(Error::ApiError(String::from("description missing"))), + }, + amount: msat_to_sat(amount_msat), + amount_msat: *amount_msat, + pre_image: invoice.payment_preimage.as_ref().map(hex::encode), + payment_hash: decoded_invoice.payment_hash().to_string(), + settled, + settle_date, + creation_date: decoded_invoice + .timestamp() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(), + expiry: decoded_invoice.expiry_time().as_secs(), + status, + }) + } +} + impl From for PayRequest { fn from(params: PayInvoiceParams) -> Self { let amount_msat = diff --git a/core/src/backends/eclair/rest/types.rs b/core/src/backends/eclair/rest/types.rs index ab225bc..deec566 100644 --- a/core/src/backends/eclair/rest/types.rs +++ b/core/src/backends/eclair/rest/types.rs @@ -117,7 +117,7 @@ pub struct PayementRequestResponse { pub description: String, pub payment_hash: String, pub payment_metadata: String, - pub expiry: i32, + pub expiry: u64, pub min_final_cltv_expiry: u32, pub amount: u64, } @@ -132,7 +132,7 @@ pub enum InvoiceStatusTypeResponse { #[derive(Debug, Deserialize)] pub struct InvoiceDateResponse { - pub unix: i64, + pub unix: u64, } #[derive(Debug, Deserialize)] @@ -146,7 +146,7 @@ pub struct InvoiceStatusResponse { impl Into for InvoiceResponse { fn into(self) -> Invoice { - let mut settle_date: Option = None; + let mut settle_date: Option = None; let settled = match self.status._type { InvoiceStatusTypeResponse::Received => match self.status.received_at { Some(e) => { diff --git a/core/src/backends/lnd/rest/types.rs b/core/src/backends/lnd/rest/types.rs index 8463ba6..efd5b12 100644 --- a/core/src/backends/lnd/rest/types.rs +++ b/core/src/backends/lnd/rest/types.rs @@ -161,7 +161,7 @@ impl TryInto for InvoiceResponse { type Error = Error; fn try_into(self) -> Result { - let mut settle_date: Option = None; + let mut settle_date: Option = None; if self.settled { settle_date = Some(parse_number(&self.settle_date).expect("settle_date should be a number")); diff --git a/core/src/types.rs b/core/src/types.rs index 49d0d86..a3042a7 100644 --- a/core/src/types.rs +++ b/core/src/types.rs @@ -87,9 +87,9 @@ pub struct Invoice { pub pre_image: Option, pub payment_hash: String, pub settled: bool, - pub settle_date: Option, - pub creation_date: i64, - pub expiry: i32, + pub settle_date: Option, + pub creation_date: u64, + pub expiry: u64, pub status: InvoiceStatus, } From 0559eb3a958e1592f95eaf02a8162dd1a697598b Mon Sep 17 00:00:00 2001 From: Dolu Date: Fri, 19 Aug 2022 15:36:47 +0200 Subject: [PATCH 05/11] feat(una-js): add pay_invoice() method to js bindings --- bindings/una-js/index.d.ts | 17 +++++++++++++++++ bindings/una-js/src/lib.rs | 16 ++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/bindings/una-js/index.d.ts b/bindings/una-js/index.d.ts index fcb1902..b63caa1 100644 --- a/bindings/una-js/index.d.ts +++ b/bindings/una-js/index.d.ts @@ -9,6 +9,7 @@ export class Node { createInvoice(invoice: CreateInvoiceParams): Promise getInfo(): Promise payInvoice(invoice: PayInvoiceParams): Promise + getInvoice(payementHash: String): Promise } export type Backend = "LndRest" | "LndGrpc" | "ClnGrpc" | "EclairRest" | "InvalidBackend"; @@ -37,6 +38,22 @@ export interface CreateInvoiceResult { payment_request: string; } +export type InvoiceStatus = "Pending" | "Settled" | "Cancelled" | "Accepted"; + +export interface Invoice { + amount: number; + amount_msat: number; + bolt11: string; + creation_date: number; + expiry: number; + memo: string; + payment_hash: string; + pre_image?: string | null; + settle_date?: number | null; + settled: boolean; + status: InvoiceStatus; +} + export type Network = | ("mainnet" | "testnet" | "regtest") | { diff --git a/bindings/una-js/src/lib.rs b/bindings/una-js/src/lib.rs index 4d0ac69..34037d4 100644 --- a/bindings/una-js/src/lib.rs +++ b/bindings/una-js/src/lib.rs @@ -101,6 +101,22 @@ impl JsNode { ) } + #[napi( + ts_args_type = "payementHash: String", + ts_return_type = "Promise" + )] + pub fn get_invoice(&self, env: Env, payement_hash: String) -> Result { + let node = self.0.clone(); + + env.execute_tokio_future( + async move { + let invoice = node.lock().await.get_invoice(payement_hash).await.unwrap(); + Ok(invoice) + }, + |&mut env, invoice| Ok(env.to_js_value(&invoice)), + ) + } + #[napi( ts_args_type = "invoice: PayInvoiceParams", ts_return_type = "Promise" From 9ae1748d6023d0486287224c6fd925aedefbefb7 Mon Sep 17 00:00:00 2001 From: Dolu Date: Fri, 19 Aug 2022 16:05:59 +0200 Subject: [PATCH 06/11] refactor: delete tools file and using utils --- core/src/backends/cln/grpc/node.rs | 6 ++++-- core/src/backends/cln/grpc/pb.rs | 11 +++++++---- core/src/backends/eclair/rest/node.rs | 6 ++---- core/src/backends/eclair/rest/types.rs | 8 +++++--- core/src/backends/lnd/rest/node.rs | 6 +++--- core/src/backends/lnd/rest/types.rs | 8 ++++---- core/src/lib.rs | 1 - core/src/node.rs | 4 ++-- core/src/tools.rs | 25 ------------------------- core/src/utils.rs | 9 ++++++++- 10 files changed, 35 insertions(+), 49 deletions(-) delete mode 100644 core/src/tools.rs diff --git a/core/src/backends/cln/grpc/node.rs b/core/src/backends/cln/grpc/node.rs index f296469..817a7f0 100644 --- a/core/src/backends/cln/grpc/node.rs +++ b/core/src/backends/cln/grpc/node.rs @@ -3,11 +3,13 @@ use tonic::transport::{Certificate, Channel, ClientTlsConfig, Endpoint, Identity use crate::error::Error; use crate::node::NodeMethods; use crate::types::{ - CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, PayInvoiceParams, PayInvoiceResult, + CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeInfo, PayInvoiceParams, PayInvoiceResult, }; use super::config::ClnGrpcConfig; -use super::pb::{node_client::NodeClient, GetinfoRequest, InvoiceRequest, PayRequest, ListinvoicesRequest}; +use super::pb::{ + node_client::NodeClient, GetinfoRequest, InvoiceRequest, ListinvoicesRequest, PayRequest, +}; pub struct ClnGrpc { endpoint: Endpoint, diff --git a/core/src/backends/cln/grpc/pb.rs b/core/src/backends/cln/grpc/pb.rs index 43a406b..e14049c 100644 --- a/core/src/backends/cln/grpc/pb.rs +++ b/core/src/backends/cln/grpc/pb.rs @@ -2,7 +2,11 @@ use std::time::UNIX_EPOCH; -use crate::{error::Error, tools::msat_to_sat, types::*, utils}; +use crate::{ + error::Error, + types::*, + utils::{self, msat_to_sat}, +}; use cuid; tonic::include_proto!("cln"); @@ -73,9 +77,8 @@ impl TryInto for ListinvoicesResponse { type Error = Error; fn try_into(self) -> Result { - println!("{:?}", self); let invoice: &ListinvoicesInvoices = self.invoices.get(0).expect("No invoice found"); - // let bolt11 = (&invoice.bolt11).ok_or_else(|| Error::ApiError(String::from("bolt11 missing")))?.as_ref(); + let bolt11 = match &invoice.bolt11 { Some(bolt11) => bolt11.as_ref(), None => return Err(Error::ApiError(String::from("bolt11 missing"))), @@ -113,7 +116,7 @@ impl TryInto for ListinvoicesResponse { Some(description) => String::from(description), None => return Err(Error::ApiError(String::from("description missing"))), }, - amount: msat_to_sat(amount_msat), + amount: msat_to_sat(*amount_msat), amount_msat: *amount_msat, pre_image: invoice.payment_preimage.as_ref().map(hex::encode), payment_hash: decoded_invoice.payment_hash().to_string(), diff --git a/core/src/backends/eclair/rest/node.rs b/core/src/backends/eclair/rest/node.rs index f4bb447..05449e0 100644 --- a/core/src/backends/eclair/rest/node.rs +++ b/core/src/backends/eclair/rest/node.rs @@ -3,13 +3,13 @@ use std::collections::HashMap; use crate::error::Error; use crate::node::NodeMethods; use crate::types::{ - CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, PayInvoiceParams, PayInvoiceResult, + CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeInfo, PayInvoiceParams, PayInvoiceResult, }; use super::config::EclairRestConfig; use super::types::{ ApiError, ChannelState, CreateInvoiceRequest, CreateInvoiceResponse, GetChannelsResponse, - GetInfoResponse, PayInvoiceRequest, PayInvoiceResponse, InvoiceResponse, + GetInfoResponse, InvoiceResponse, PayInvoiceRequest, PayInvoiceResponse, }; pub struct EclairRest { @@ -125,8 +125,6 @@ impl NodeMethods for EclairRest { async fn get_invoice(&self, payment_hash: String) -> Result { let url = format!("{}/getreceivedinfo", self.config.url); - println!("{}", payment_hash); - let mut params = HashMap::new(); params.insert("paymentHash", &payment_hash); diff --git a/core/src/backends/eclair/rest/types.rs b/core/src/backends/eclair/rest/types.rs index deec566..b221d86 100644 --- a/core/src/backends/eclair/rest/types.rs +++ b/core/src/backends/eclair/rest/types.rs @@ -1,11 +1,13 @@ #![allow(clippy::from_over_into)] -use crate::{tools::msat_to_sat, types::*}; +use crate::{ + types::*, + utils::{self, msat_to_sat}, +}; use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::error::Error; -use crate::{types::*, utils}; #[derive(Debug, Deserialize)] pub struct ApiError { @@ -167,7 +169,7 @@ impl Into for InvoiceResponse { Invoice { bolt11: self.payment_request.serialized, memo: self.payment_request.description, - amount: msat_to_sat(&self.payment_request.amount), + amount: msat_to_sat(self.payment_request.amount), amount_msat: self.payment_request.amount, pre_image: self.payment_preimage, payment_hash: self.payment_request.payment_hash, diff --git a/core/src/backends/lnd/rest/node.rs b/core/src/backends/lnd/rest/node.rs index 5c6d0ea..5318b46 100644 --- a/core/src/backends/lnd/rest/node.rs +++ b/core/src/backends/lnd/rest/node.rs @@ -1,13 +1,13 @@ use crate::error::Error; use crate::node::NodeMethods; use crate::types::{ - CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, PayInvoiceParams, PayInvoiceResult, + CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeInfo, PayInvoiceParams, PayInvoiceResult, }; use super::config::LndRestConfig; use super::types::{ - ApiError, CreateInvoiceRequest, CreateInvoiceResponse, GetInfoResponse, SendPaymentSyncRequest, - SendPaymentSyncResponse, InvoiceResponse + ApiError, CreateInvoiceRequest, CreateInvoiceResponse, GetInfoResponse, InvoiceResponse, + SendPaymentSyncRequest, SendPaymentSyncResponse, }; pub struct LndRest { diff --git a/core/src/backends/lnd/rest/types.rs b/core/src/backends/lnd/rest/types.rs index efd5b12..4f382fb 100644 --- a/core/src/backends/lnd/rest/types.rs +++ b/core/src/backends/lnd/rest/types.rs @@ -2,11 +2,11 @@ use std::collections::HashMap; -use crate::tools::convert_base64_to_hex; +use crate::utils::{b64_to_hex, parse_number}; use crate::{types::*, utils}; pub type Base64String = String; -use crate::{error::Error, tools::parse_number}; +use crate::error::Error; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize)] @@ -180,10 +180,10 @@ impl TryInto for InvoiceResponse { amount: parse_number(&self.value).expect("amt_paid_sat should be a number"), amount_msat: parse_number(&self.value_msat).expect("amt_paid_msat should be a number"), pre_image: Some( - convert_base64_to_hex(&self.r_preimage) + b64_to_hex(&self.r_preimage) .expect("coudln't convert r_preimage from base64 to hex"), ), - payment_hash: convert_base64_to_hex(&self.r_hash) + payment_hash: b64_to_hex(&self.r_hash) .expect("coudln't convert r_hash from base64 to hex"), settled: self.settled, settle_date, diff --git a/core/src/lib.rs b/core/src/lib.rs index 2beb4ca..bce2008 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,6 +1,5 @@ pub mod backends; pub mod error; pub mod node; -pub mod tools; pub mod types; pub mod utils; diff --git a/core/src/node.rs b/core/src/node.rs index bb49fc0..1619327 100644 --- a/core/src/node.rs +++ b/core/src/node.rs @@ -3,8 +3,8 @@ use crate::backends::eclair::rest::node::EclairRest; use crate::backends::lnd::rest::node::LndRest; use crate::error::Error; use crate::types::{ - Backend, CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, PayInvoiceParams, - PayInvoiceResult, + Backend, CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, + PayInvoiceParams, PayInvoiceResult, }; #[async_trait::async_trait] diff --git a/core/src/tools.rs b/core/src/tools.rs deleted file mode 100644 index bfbf6d7..0000000 --- a/core/src/tools.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::str::FromStr; - -use crate::error::Error; - -pub fn sat_to_msat(sat: &u64) -> u64 { - sat * 1000 -} - -pub fn msat_to_sat(msat: &u64) -> u64 { - msat / 1000 -} - -pub fn convert_base64_to_hex(hex: &str) -> Result { - let base64_decoded = base64::decode(hex); - match base64_decoded { - Ok(e) => Ok(hex::encode(e)), - Err(_) => Err(Error::ConversionError(String::from( - "coudln't convert from base64 to hex", - ))), - } -} - -pub fn parse_number(text: &str) -> Result { - text.trim().parse::() -} diff --git a/core/src/utils.rs b/core/src/utils.rs index 4b67c9c..ecdf338 100644 --- a/core/src/utils.rs +++ b/core/src/utils.rs @@ -1,4 +1,7 @@ -use std::ops::{Div, Mul}; +use std::{ + ops::{Div, Mul}, + str::FromStr, +}; use crate::error::Error; @@ -42,3 +45,7 @@ pub fn b64_to_hex(b64: &str) -> Result { let bytes = base64::decode(b64)?; Ok(hex::encode(&bytes)) } + +pub fn parse_number(text: &str) -> Result { + text.trim().parse::() +} From 6a09f6b9ca4890b69c447e645ede72fa22470636 Mon Sep 17 00:00:00 2001 From: Dolu Date: Fri, 19 Aug 2022 16:16:51 +0200 Subject: [PATCH 07/11] refactor(una-js): build js bindings and add getInvoice() in test.mjs --- bindings/una-js/index.d.ts | 2 +- bindings/una-js/test.mjs | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bindings/una-js/index.d.ts b/bindings/una-js/index.d.ts index b63caa1..53df225 100644 --- a/bindings/una-js/index.d.ts +++ b/bindings/una-js/index.d.ts @@ -8,8 +8,8 @@ export class Node { constructor(backend: Backend, config: NodeConfig) createInvoice(invoice: CreateInvoiceParams): Promise getInfo(): Promise - payInvoice(invoice: PayInvoiceParams): Promise getInvoice(payementHash: String): Promise + payInvoice(invoice: PayInvoiceParams): Promise } export type Backend = "LndRest" | "LndGrpc" | "ClnGrpc" | "EclairRest" | "InvalidBackend"; diff --git a/bindings/una-js/test.mjs b/bindings/una-js/test.mjs index fbf8166..8f9de55 100644 --- a/bindings/una-js/test.mjs +++ b/bindings/una-js/test.mjs @@ -5,20 +5,20 @@ const config = { rest: { url: "https://127.0.0.1:8081", macaroon: - "0201036c6e6402f801030a100249a596de122aa0b6cee0d446f166231201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620a66d3da5b13d50b2c506a2191927bce8ef13e8fd192af2c69b5dcefddb97199d", + "0201036c6e6402f801030a1090788030ebddcc36000f194b061613a41201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620b1f79f1a88e156dff7b533a32a23abbe22546a6ef2e81dccd2991a2786007dff", tls_certificate: - "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949434a7a434341637967417749424167495141553567664541653374586471776a6c494b476f6e6a414b42676771686b6a4f50515144416a41784d5238770a485159445651514b45785a73626d5167595856306232646c626d56795958526c5a43426a5a584a304d51347744415944565151444577566862476c6a5a5441650a467730794d6a41344d5455784e544d334e444a61467730794d7a45774d5441784e544d334e444a614d444578487a416442674e5642416f54466d78755a4342680a645852765a3256755a584a686447566b49474e6c636e5178446a414d42674e5642414d544257467361574e6c4d466b77457759484b6f5a497a6a3043415159490a4b6f5a497a6a30444151634451674145796c494245585854544d6f76526f594c515064454c6a38626a7257656744637556564b3044744b4a78774d65773754440a342b6f4c754438334c546b697137446b2b6d6842505a6e782f4f4754474b7a754a4c314465364f4278544342776a414f42674e56485138424166384542414d430a41715177457759445652306c42417777436759494b775942425155484177457744775944565230544151482f42415577417745422f7a416442674e56485134450a4667515563616b6351494e38553048734c735752527544394d304f47306d6b77617759445652305242475177596f4946595778705932574343577876593246730a6147397a644949465957787059325743446e4276624746794c5734784c57467361574e6c67675231626d6c3467677031626d6c346347466a61325630676764690a64575a6a623235756877522f4141414268784141414141414141414141414141414141414141414268775373465141454d416f4743437147534d343942414d430a41306b414d45594349514364767236702f466446464866597871596279594b483635344e6e7372557779344f544648386c31727662514968414f5433546c437a0a77477863642b35416367364e31596472652b76544f2f34525371636c7a5a6532776151520a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a", + "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949434a7a43434163796741774942416749514f66564e7a49356e622b465831757662674844302b7a414b42676771686b6a4f50515144416a41784d5238770a485159445651514b45785a73626d5167595856306232646c626d56795958526c5a43426a5a584a304d51347744415944565151444577566862476c6a5a5441650a467730794d6a41344d4467774e6a4d314d7a6c61467730794d7a45774d444d774e6a4d314d7a6c614d444578487a416442674e5642416f54466d78755a4342680a645852765a3256755a584a686447566b49474e6c636e5178446a414d42674e5642414d544257467361574e6c4d466b77457759484b6f5a497a6a3043415159490a4b6f5a497a6a3044415163445167414578375568773146647543444f7a42456d634f346c374f6a33476b47674e734b75337a36454761782b39713042546655320a4d48336435396b45466a3534517562685739746942797669594d6e736633314f4537733744714f4278544342776a414f42674e56485138424166384542414d430a41715177457759445652306c42417777436759494b775942425155484177457744775944565230544151482f42415577417745422f7a416442674e56485134450a4667515572683771416c497875576e6a56536e4779352f4d4e77563454636f77617759445652305242475177596f4946595778705932574343577876593246730a6147397a644949465957787059325743446e4276624746794c5734304c57467361574e6c67675231626d6c3467677031626d6c346347466a61325630676764690a64575a6a623235756877522f4141414268784141414141414141414141414141414141414141414268775373464141454d416f4743437147534d343942414d430a41306b414d455943495143305536414d4a5a2b2f506c6859567a505736793637746877566b316743525243386d4c416c6e37752b4d514968414d675a6e4944370a7265727147474555386334622f6967697479776a36516737415a6e354c65306f696a6c450a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a", }, }, cln: { grpc: { url: "https://localhost:11002", tls_certificate: - "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0d0a4d494942636a434341526967417749424167494a414d466c546169526559526a4d416f4743437147534d343942414d434d4259784644415342674e5642414d4d0d0a43324e73626942536232393049454e424d434158445463314d4445774d5441774d4441774d466f59447a51774f5459774d5441784d4441774d444177576a41570d0a4d52517745675944565151444441746a62473467556d3976644342445154425a4d424d4742797147534d34394167454743437147534d343941774548413049410d0a424c4344514c387469663542445051643943715976776361756749752b757274616e554359536c6b4b714b3736562f4d66646b307372464c364c6d6e684b32720d0a61434c494b475841334f4f545038426e5a5a3144547a4b6a5454424c4d426b4741315564455151534d42434341324e73626f494a6247396a5957786f62334e300d0a4d42304741315564446751574242526a68486d527145316c775a56656b53735a374f56456d4a6c65736a415042674e5648524d4241663845425441444151482f0d0a4d416f4743437147534d343942414d43413067414d45554349474b772b784259364f656b334b6c675957693050346459525a57632b67537343616835513554620d0a4a575959416945413538316b475337347335694a58475950514351416d777531524b4577426139432b625552526e4569676b633d0d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0d0a", + "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0d0a4d4949426354434341526967417749424167494a415065324934484e463673474d416f4743437147534d343942414d434d4259784644415342674e5642414d4d0d0a43324e73626942536232393049454e424d434158445463314d4445774d5441774d4441774d466f59447a51774f5459774d5441784d4441774d444177576a41570d0a4d52517745675944565151444441746a62473467556d3976644342445154425a4d424d4742797147534d34394167454743437147534d343941774548413049410d0a424e30454c4e62366a3477674d693761695455322b79334c444272754a71546d672f533361772f50756d58785a71674c6a746d6a654e746f6f6a44364474654d0d0a4931444a4877354366765777796a6d72496455587346616a5454424c4d426b4741315564455151534d42434341324e73626f494a6247396a5957786f62334e300d0a4d4230474131556444675157424251477178664e67534f32393055445254786a4a7a6a65774364534e44415042674e5648524d4241663845425441444151482f0d0a4d416f4743437147534d343942414d43413063414d4551434941765a4b39647a6344356a6f7a7347666473493346685032337a364754656773384a695a574b530d0a5479597a416941546f7970426f4c77684d33645443764f3679626248626375422f78532f62665458765336774749453774773d3d0d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0d0a", tls_client_certificate: - "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0d0a4d49494252544342374b41444167454341676b416a707851696b2f6852626b77436759494b6f5a497a6a304541774977466a45554d424947413155454177774c0d0a5932787549464a7662335167513045774942634e4e7a55774d5441784d4441774d444177576867504e4441354e6a41784d4445774d4441774d4442614d426f780d0a4744415742674e5642414d4d44324e736269426e636e426a49454e73615756756444425a4d424d4742797147534d34394167454743437147534d3439417745480d0a413049414247516a7057383170623545486749616878497165424e4d4138762f367070515761354f57366b33646e55484f434f465539686e72755630664d4a490d0a367444487363614a6c64327978666970575277712f2b6d32537a4f6a485441624d426b4741315564455151534d42434341324e73626f494a6247396a5957786f0d0a62334e304d416f4743437147534d343942414d43413067414d4555434951446e703555734f64457335304a6e3839596a5367456572635a4952314e70726831610d0a384e6b657736566d32514967523433303231333450654337617477363046524d3338414974433767672f686c78534c304f577a787833673d0d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0d0a", + "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0d0a4d494942526a4342374b41444167454341676b4132595a375737573739526777436759494b6f5a497a6a304541774977466a45554d424947413155454177774c0d0a5932787549464a7662335167513045774942634e4e7a55774d5441784d4441774d444177576867504e4441354e6a41784d4445774d4441774d4442614d426f780d0a4744415742674e5642414d4d44324e736269426e636e426a49454e73615756756444425a4d424d4742797147534d34394167454743437147534d3439417745480d0a413049414248454b66516b6763733930516c36522f686e31305331482b676b6169455975646d327a6d647665374e494e51464d7179533879546b674c6a474c780d0a613072376e2b394e5a4351334a5041357a424e42646f6b3941586d6a485441624d426b4741315564455151534d42434341324e73626f494a6247396a5957786f0d0a62334e304d416f4743437147534d343942414d4341306b414d4559434951443462584675435832534f34744d2f4a4258376b32514a49494d3369726c533633550d0a46534475736272437777496841496c656435564854327547302b454135335a7a6677354a70477374563659526a52424e445a6d45783479340d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0d0a", tls_client_key: - "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0d0a4d494748416745414d424d4742797147534d34394167454743437147534d3439417745484247307761774942415151676c5a43666461766d53776b4939526c6a0d0a427062636e6f737757355351764348474b7346637744767642794b6852414e434141526b493656764e61572b52423443476f63534b6e67545441504c2f2b71610d0a55466d75546c75704e335a31427a676a685650595a36376c64487a43534f725178374847695a58647373583471566b634b762f70746b737a0d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0d0a", + "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0d0a4d494748416745414d424d4742797147534d34394167454743437147534d343941774548424730776177494241515167756c4e4532566247504a6a4c2b6b322b0d0a6f334249584b533032434657423663356f46363968516270566b796852414e4341415278436e304a49484c5064454a656b66345a39644574522f6f4a476f68470d0a4c6e5a7473356e6233757a53445542544b736b764d6b3549433478693857744b2b352f765457516b4e7954774f6377545158614a505146350d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0d0a", }, }, eclair: { @@ -40,23 +40,23 @@ async function test_node(backend, config) { node .getInfo() - .then((info) => console.log(info)) + .then((info) => console.log('getInfo(): ', info)) .catch((err) => console.log(err.message, err.code)); - node - .createInvoice(invoice) - .then((invoice) => console.log(invoice)) - .catch((err) => console.log(err.message, err.code)); + const createInvoiceResult = await node.createInvoice(invoice).catch((err) => console.log(err.message, err.code)); + console.log('createInvoice(): ', createInvoiceResult); + const getInvoiceResult = await node.getInvoice(createInvoiceResult.payment_hash).catch((err) => console.log(err.message, err.code)); + console.log('getInvoice(): ', getInvoiceResult); node .payInvoice({ payment_request: - "lnbcrt10u1p30ag67p5v45p0xljwcktszppfj8a3wcg460xrwwhpt0xcghx36xcsr3dkz7spp5nctfmzx6k5dyv693t52y0fs95zrk33f37mue5f3geu4wt9cwgltsdpz2phkcctjypykuan0d93k2grxdaezqcn0vgxqyjw5qcqp2rzjq25z6j80gkd8vyr5nptma4lnmhjvyq66eretzvy5a4gty72g8xpejqqqdsqqqqgqqyqqqqlgqqqqqqgq9q9qyysgqd4e22wy6ejggnpg8kfkuxf6vkgxejfe4283zjgsfxmd8w7dsvk7k43lsgakwzyanzfu372w8ke8tsuyzmp28kng4e6xyz8eyh99ncagpa8myt0", + "lnbcrt500u1p30lxcjpp5nqerez0kcz78dakgd5kystfyf3nndzqxcuj3dc52vf42uvhlkemqdqqcqzpgxqyz5vqsp5r6ahdt5em43pt8578z4xvkzm9ah70x97ey0f768xx8r9msvlza6s9qyyssqfrt207yknpnvshqxtuku03r3rt99hvswpk7px0qy95yrym7md2mnfttxyhmxkjdm7agrqggcdf4at6ld9lkwx4q664y4hrrnd5p8argpx5yhdy", }) - .then((result) => console.log(result)) + .then((result) => console.log('payInvoice(): ', result)) .catch((err) => console.log(err.message, err.code)); } // test_node("LndRest", config.lnd.rest); -test_node("EclairRest", config.eclair.rest); -// test_node("ClnGrpc", config.cln.grpc); +// test_node("EclairRest", config.eclair.rest); +test_node("ClnGrpc", config.cln.grpc); From ad9ba0da081a6b626bb697ceb476834869ca5fab Mon Sep 17 00:00:00 2001 From: Dolu Date: Fri, 19 Aug 2022 16:36:12 +0200 Subject: [PATCH 08/11] feat(una-python): add payInvoice() in python bindings --- bindings/una-python/src/lib.rs | 12 +++++++++++- bindings/una-python/test.py | 32 +++++++++++++++++++------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/bindings/una-python/src/lib.rs b/bindings/una-python/src/lib.rs index ee134f0..ab1efe1 100644 --- a/bindings/una-python/src/lib.rs +++ b/bindings/una-python/src/lib.rs @@ -12,7 +12,7 @@ use una_core::{ node::{Node, NodeMethods}, types::{ Backend, CreateInvoiceParams, CreateInvoiceResult, NodeConfig, NodeInfo, PayInvoiceParams, - PayInvoiceResult, + PayInvoiceResult, Invoice, }, }; @@ -105,6 +105,16 @@ impl PyNode { Ok(result) }) } + + pub fn get_invoice<'p>(&self, py: Python<'p>, payment_request: String) -> PyResult<&'p PyAny> { + let node = self.0.clone(); + + pyo3_asyncio::tokio::future_into_py(py, async move { + let result = node.lock().await.get_invoice(payment_request).await.or_py_error()?; + let result = Python::with_gil(|py| pythonize::(py, &result).or_py_error())?; + Ok(result) + }) + } } /// A Python module implemented in Rust. diff --git a/bindings/una-python/test.py b/bindings/una-python/test.py index 867eb84..812b4f6 100644 --- a/bindings/una-python/test.py +++ b/bindings/una-python/test.py @@ -9,30 +9,34 @@ async def lnd_rest(): config_lnd_rest = dict({ "url": "https://127.0.0.1:8081", - "macaroon": "0201036c6e6402f801030a100249a596de122aa0b6cee0d446f166231201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620a66d3da5b13d50b2c506a2191927bce8ef13e8fd192af2c69b5dcefddb97199d", - "tls_certificate": "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949434a7a434341637967417749424167495141553567664541653374586471776a6c494b476f6e6a414b42676771686b6a4f50515144416a41784d5238770a485159445651514b45785a73626d5167595856306232646c626d56795958526c5a43426a5a584a304d51347744415944565151444577566862476c6a5a5441650a467730794d6a41344d5455784e544d334e444a61467730794d7a45774d5441784e544d334e444a614d444578487a416442674e5642416f54466d78755a4342680a645852765a3256755a584a686447566b49474e6c636e5178446a414d42674e5642414d544257467361574e6c4d466b77457759484b6f5a497a6a3043415159490a4b6f5a497a6a30444151634451674145796c494245585854544d6f76526f594c515064454c6a38626a7257656744637556564b3044744b4a78774d65773754440a342b6f4c754438334c546b697137446b2b6d6842505a6e782f4f4754474b7a754a4c314465364f4278544342776a414f42674e56485138424166384542414d430a41715177457759445652306c42417777436759494b775942425155484177457744775944565230544151482f42415577417745422f7a416442674e56485134450a4667515563616b6351494e38553048734c735752527544394d304f47306d6b77617759445652305242475177596f4946595778705932574343577876593246730a6147397a644949465957787059325743446e4276624746794c5734784c57467361574e6c67675231626d6c3467677031626d6c346347466a61325630676764690a64575a6a623235756877522f4141414268784141414141414141414141414141414141414141414268775373465141454d416f4743437147534d343942414d430a41306b414d45594349514364767236702f466446464866597871596279594b483635344e6e7372557779344f544648386c31727662514968414f5433546c437a0a77477863642b35416367364e31596472652b76544f2f34525371636c7a5a6532776151520a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a" + "macaroon": "0201036c6e6402f801030a1090788030ebddcc36000f194b061613a41201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620b1f79f1a88e156dff7b533a32a23abbe22546a6ef2e81dccd2991a2786007dff", + "tls_certificate": "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949434a7a43434163796741774942416749514f66564e7a49356e622b465831757662674844302b7a414b42676771686b6a4f50515144416a41784d5238770a485159445651514b45785a73626d5167595856306232646c626d56795958526c5a43426a5a584a304d51347744415944565151444577566862476c6a5a5441650a467730794d6a41344d4467774e6a4d314d7a6c61467730794d7a45774d444d774e6a4d314d7a6c614d444578487a416442674e5642416f54466d78755a4342680a645852765a3256755a584a686447566b49474e6c636e5178446a414d42674e5642414d544257467361574e6c4d466b77457759484b6f5a497a6a3043415159490a4b6f5a497a6a3044415163445167414578375568773146647543444f7a42456d634f346c374f6a33476b47674e734b75337a36454761782b39713042546655320a4d48336435396b45466a3534517562685739746942797669594d6e736633314f4537733744714f4278544342776a414f42674e56485138424166384542414d430a41715177457759445652306c42417777436759494b775942425155484177457744775944565230544151482f42415577417745422f7a416442674e56485134450a4667515572683771416c497875576e6a56536e4779352f4d4e77563454636f77617759445652305242475177596f4946595778705932574343577876593246730a6147397a644949465957787059325743446e4276624746794c5734304c57467361574e6c67675231626d6c3467677031626d6c346347466a61325630676764690a64575a6a623235756877522f4141414268784141414141414141414141414141414141414141414268775373464141454d416f4743437147534d343942414d430a41306b414d455943495143305536414d4a5a2b2f506c6859567a505736793637746877566b316743525243386d4c416c6e37752b4d514968414d675a6e4944370a7265727147474555386334622f6967697479776a36516737415a6e354c65306f696a6c450a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a" }) lnd_rest = una.Node("LndRest", config_lnd_rest) print(await lnd_rest.get_info()) - print(await lnd_rest.create_invoice(invoice)) + newInvoice = await lnd_rest.create_invoice(invoice) + print(newInvoice) + print(await lnd_rest.get_invoice(newInvoice["payment_hash"])) print(await lnd_rest.pay_invoice(dict({ - "payment_request": "lnbcrt10u1p306998pp53jzmkeummuu0u6rfxpcgx7rk5adturte8mppr64032nek3jmjtvqdq4w3jhxapqvf5kuerfdenhxcqzpgxqrrsssp53nhngg8858l2ch63ks3yc2uetzf4gnctp9xccqxuchztxmahd2as9qyyssqzl6m9rwgwp0r5a6de7fj98k42sn7vvf30n6k2ymcq7wvmk4689ls7zewqq9x5maqa60tshdmhu3gx3dd243vh9h9xralrfrjpfwfn3gq6xml7k" + "payment_request": "lnbcrt500u1p30l86jsp5t8da6ptufsqmaeq82ggj459t0xaagr2zavd3rfjgq67ld2vxq70qpp59an0dyn45jp87ae8jkqxxmt5v3m2nlfvc274l097tehzeh5y06nsdpz2phkcctjypykuan0d93k2grxdaezqcn0vgxqyjw5qcqp2rzjqf6tlmcyzxx6j6td5scpr45cwjd850fyu5y9rqk3vfdygjsv8xxezqqqwgqqqqgqqqqqqqlgqqqqqqgq9q9qyysgqsclv524vlt3mumew6wsw25xszlr6zscn29w0g5ryhg67xvl89jsj9u4ylvqu65p7up07vuuee20hcfg2v5qfqzqk9f8vpwktuct6fwcq78fxyn" }))) async def cln_grpc(): config_cln_grpc = dict({ "url": "https://localhost:11002", - "tls_certificate": "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0d0a4d494942636a434341526967417749424167494a414d466c546169526559526a4d416f4743437147534d343942414d434d4259784644415342674e5642414d4d0d0a43324e73626942536232393049454e424d434158445463314d4445774d5441774d4441774d466f59447a51774f5459774d5441784d4441774d444177576a41570d0a4d52517745675944565151444441746a62473467556d3976644342445154425a4d424d4742797147534d34394167454743437147534d343941774548413049410d0a424c4344514c387469663542445051643943715976776361756749752b757274616e554359536c6b4b714b3736562f4d66646b307372464c364c6d6e684b32720d0a61434c494b475841334f4f545038426e5a5a3144547a4b6a5454424c4d426b4741315564455151534d42434341324e73626f494a6247396a5957786f62334e300d0a4d42304741315564446751574242526a68486d527145316c775a56656b53735a374f56456d4a6c65736a415042674e5648524d4241663845425441444151482f0d0a4d416f4743437147534d343942414d43413067414d45554349474b772b784259364f656b334b6c675957693050346459525a57632b67537343616835513554620d0a4a575959416945413538316b475337347335694a58475950514351416d777531524b4577426139432b625552526e4569676b633d0d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0d0a", - "tls_client_certificate": "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0d0a4d49494252544342374b41444167454341676b416a707851696b2f6852626b77436759494b6f5a497a6a304541774977466a45554d424947413155454177774c0d0a5932787549464a7662335167513045774942634e4e7a55774d5441784d4441774d444177576867504e4441354e6a41784d4445774d4441774d4442614d426f780d0a4744415742674e5642414d4d44324e736269426e636e426a49454e73615756756444425a4d424d4742797147534d34394167454743437147534d3439417745480d0a413049414247516a7057383170623545486749616878497165424e4d4138762f367070515761354f57366b33646e55484f434f465539686e72755630664d4a490d0a367444487363614a6c64327978666970575277712f2b6d32537a4f6a485441624d426b4741315564455151534d42434341324e73626f494a6247396a5957786f0d0a62334e304d416f4743437147534d343942414d43413067414d4555434951446e703555734f64457335304a6e3839596a5367456572635a4952314e70726831610d0a384e6b657736566d32514967523433303231333450654337617477363046524d3338414974433767672f686c78534c304f577a787833673d0d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0d0a", - "tls_client_key": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0d0a4d494748416745414d424d4742797147534d34394167454743437147534d3439417745484247307761774942415151676c5a43666461766d53776b4939526c6a0d0a427062636e6f737757355351764348474b7346637744767642794b6852414e434141526b493656764e61572b52423443476f63534b6e67545441504c2f2b71610d0a55466d75546c75704e335a31427a676a685650595a36376c64487a43534f725178374847695a58647373583471566b634b762f70746b737a0d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0d0a", + "tls_certificate": "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0d0a4d4949426354434341526967417749424167494a415065324934484e463673474d416f4743437147534d343942414d434d4259784644415342674e5642414d4d0d0a43324e73626942536232393049454e424d434158445463314d4445774d5441774d4441774d466f59447a51774f5459774d5441784d4441774d444177576a41570d0a4d52517745675944565151444441746a62473467556d3976644342445154425a4d424d4742797147534d34394167454743437147534d343941774548413049410d0a424e30454c4e62366a3477674d693761695455322b79334c444272754a71546d672f533361772f50756d58785a71674c6a746d6a654e746f6f6a44364474654d0d0a4931444a4877354366765777796a6d72496455587346616a5454424c4d426b4741315564455151534d42434341324e73626f494a6247396a5957786f62334e300d0a4d4230474131556444675157424251477178664e67534f32393055445254786a4a7a6a65774364534e44415042674e5648524d4241663845425441444151482f0d0a4d416f4743437147534d343942414d43413063414d4551434941765a4b39647a6344356a6f7a7347666473493346685032337a364754656773384a695a574b530d0a5479597a416941546f7970426f4c77684d33645443764f3679626248626375422f78532f62665458765336774749453774773d3d0d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0d0a", + "tls_client_certificate": "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0d0a4d494942526a4342374b41444167454341676b4132595a375737573739526777436759494b6f5a497a6a304541774977466a45554d424947413155454177774c0d0a5932787549464a7662335167513045774942634e4e7a55774d5441784d4441774d444177576867504e4441354e6a41784d4445774d4441774d4442614d426f780d0a4744415742674e5642414d4d44324e736269426e636e426a49454e73615756756444425a4d424d4742797147534d34394167454743437147534d3439417745480d0a413049414248454b66516b6763733930516c36522f686e31305331482b676b6169455975646d327a6d647665374e494e51464d7179533879546b674c6a474c780d0a613072376e2b394e5a4351334a5041357a424e42646f6b3941586d6a485441624d426b4741315564455151534d42434341324e73626f494a6247396a5957786f0d0a62334e304d416f4743437147534d343942414d4341306b414d4559434951443462584675435832534f34744d2f4a4258376b32514a49494d3369726c533633550d0a46534475736272437777496841496c656435564854327547302b454135335a7a6677354a70477374563659526a52424e445a6d45783479340d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0d0a", + "tls_client_key": "2d2d2d2d2d424547494e2050524956415445204b45592d2d2d2d2d0d0a4d494748416745414d424d4742797147534d34394167454743437147534d343941774548424730776177494241515167756c4e4532566247504a6a4c2b6b322b0d0a6f334249584b533032434657423663356f46363968516270566b796852414e4341415278436e304a49484c5064454a656b66345a39644574522f6f4a476f68470d0a4c6e5a7473356e6233757a53445542544b736b764d6b3549433478693857744b2b352f765457516b4e7954774f6377545158614a505146350d0a2d2d2d2d2d454e442050524956415445204b45592d2d2d2d2d0d0a", }) cln_grpc = una.Node("ClnGrpc", config_cln_grpc) print(await cln_grpc.get_info()) - print(await cln_grpc.create_invoice(invoice)) + newInvoice = await cln_grpc.create_invoice(invoice) + print(newInvoice) + print(await cln_grpc.get_invoice(newInvoice["payment_hash"])) print(await cln_grpc.pay_invoice(dict({ - "payment_request": "lnbcrt10u1p306998pp53jzmkeummuu0u6rfxpcgx7rk5adturte8mppr64032nek3jmjtvqdq4w3jhxapqvf5kuerfdenhxcqzpgxqrrsssp53nhngg8858l2ch63ks3yc2uetzf4gnctp9xccqxuchztxmahd2as9qyyssqzl6m9rwgwp0r5a6de7fj98k42sn7vvf30n6k2ymcq7wvmk4689ls7zewqq9x5maqa60tshdmhu3gx3dd243vh9h9xralrfrjpfwfn3gq6xml7k" + "payment_request": "lnbcrt500u1p30l8sqpp5w7lv678lvu6y79607slk7ucz9swnsr8hl0946nnpy6j5ey27xazsdqqcqzpgxqyz5vqsp5mtnhgzrvduh2dw6kvnrpa8asx2rdz6wqum9exla2nwfxmuqw2y6q9qyyssqtak8660yd9ne2u52tctnpsgyj97gsqt572sg6lp76dwanv0a43w3y9gavap4kkn4uzcedmjstachapkhuyex9ascy08wvx69v6xkrysq49fg6k" }))) async def eclair_rest(): @@ -44,11 +48,13 @@ async def eclair_rest(): eclair_rest = una.Node("EclairRest", config_eclair_rest) print(await eclair_rest.get_info()) - print(await eclair_rest.create_invoice(invoice)) + newInvoice = await eclair_rest.create_invoice(invoice) + print(newInvoice) + print(await eclair_rest.get_invoice(newInvoice["payment_hash"])) print(await eclair_rest.pay_invoice(dict({ - "payment_request": "lnbcrt10u1p30afdnsp5y05vz3g860fn8fy6fy2u9pr49ttnl6qq85v3mkmzz58cyqxmdpvspp52vy739gahycksqafupk4fw7mu0xjyf2l2e5exr64ycmjedzs3a5sdq4w3jhxapqvf5kuerfdenhxxqyjw5qcqp2rzjq25z6j80gkd8vyr5nptma4lnmhjvyq66eretzvy5a4gty72g8xpejqqqdsqqqqgqqyqqqqlgqqqqqqgq9q9qyysgqp0kpcx20yq5hftwguxfp0aka297g7kqplfl9ntc3rznkwnm9kf94ssq3qygjt2v0jrm95w2s590gc087vr6k3r9fvg9ey58et0dlcdcp978dh6" + "payment_request": "lnbcrt500u1p30l8ucsp5g2yz97eklymf2j5l6s0ukhn00xnkjh77ycjrmya0ve0k7yvfxu7spp5l33rnrfhrmlhe84theeu6kr5mjnntncewnzvdgctqfwnt9v4ky8qdpz2phkcctjypykuan0d93k2grxdaezqcn0vgxqyjw5qcqp2rzjqf6tlmcyzxx6j6td5scpr45cwjd850fyu5y9rqk3vfdygjsv8xxezqqqwgqqqqgqqqqqqqlgqqqqqqgq9q9qyysgqcv0etwph6zxa68rgezshmgy42z2f4e6xzdvjy64ayyeuy4e25u59gxtqsrftj9fq9k5vpcmurtgdq3x3d2chezl0g6cjg8v4g5th46sp23tz4q" }))) # asyncio.run(lnd_rest()) -asyncio.run(cln_grpc()) -# asyncio.run(eclair_rest()) \ No newline at end of file +# asyncio.run(cln_grpc()) +asyncio.run(eclair_rest()) \ No newline at end of file From 2516f71f438ba40de8386625428748ce00fde43d Mon Sep 17 00:00:00 2001 From: Dolu Date: Fri, 19 Aug 2022 18:46:46 +0200 Subject: [PATCH 09/11] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lounès Ksouri --- bindings/una-js/src/lib.rs | 4 ++-- bindings/una-python/src/lib.rs | 4 ++-- core/src/backends/cln/grpc/node.rs | 5 +---- core/src/backends/eclair/rest/node.rs | 2 ++ core/src/backends/eclair/rest/types.rs | 9 +++++---- core/src/backends/lnd/rest/node.rs | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bindings/una-js/src/lib.rs b/bindings/una-js/src/lib.rs index 34037d4..4704215 100644 --- a/bindings/una-js/src/lib.rs +++ b/bindings/una-js/src/lib.rs @@ -102,7 +102,7 @@ impl JsNode { } #[napi( - ts_args_type = "payementHash: String", + ts_args_type = "paymentHash: string", ts_return_type = "Promise" )] pub fn get_invoice(&self, env: Env, payement_hash: String) -> Result { @@ -110,7 +110,7 @@ impl JsNode { env.execute_tokio_future( async move { - let invoice = node.lock().await.get_invoice(payement_hash).await.unwrap(); + let invoice = node.lock().await.get_invoice(payment_hash).await.or_napi_error()?; Ok(invoice) }, |&mut env, invoice| Ok(env.to_js_value(&invoice)), diff --git a/bindings/una-python/src/lib.rs b/bindings/una-python/src/lib.rs index ab1efe1..05caa56 100644 --- a/bindings/una-python/src/lib.rs +++ b/bindings/una-python/src/lib.rs @@ -106,11 +106,11 @@ impl PyNode { }) } - pub fn get_invoice<'p>(&self, py: Python<'p>, payment_request: String) -> PyResult<&'p PyAny> { + pub fn get_invoice<'p>(&self, py: Python<'p>, payment_hash: String) -> PyResult<&'p PyAny> { let node = self.0.clone(); pyo3_asyncio::tokio::future_into_py(py, async move { - let result = node.lock().await.get_invoice(payment_request).await.or_py_error()?; + let result = node.lock().await.get_invoice(payment_hash).await.or_py_error()?; let result = Python::with_gil(|py| pythonize::(py, &result).or_py_error())?; Ok(result) }) diff --git a/core/src/backends/cln/grpc/node.rs b/core/src/backends/cln/grpc/node.rs index 817a7f0..65d707d 100644 --- a/core/src/backends/cln/grpc/node.rs +++ b/core/src/backends/cln/grpc/node.rs @@ -73,10 +73,7 @@ impl NodeMethods for ClnGrpc { async fn get_invoice(&self, payment_hash: String) -> Result { let mut client = self.get_client().await?; - let payment_hash_hex = match hex::decode(payment_hash) { - Ok(hex) => hex, - Err(_) => return Err(Error::ApiError(String::from("Invalid payment_hash"))), - }; + let payment_hash_hex = hex::decode(payment_hash).map_err(|_| Error::ApiError(String::from("Invalid payment_hash")))?; let request = ListinvoicesRequest { payment_hash: Some(payment_hash_hex), diff --git a/core/src/backends/eclair/rest/node.rs b/core/src/backends/eclair/rest/node.rs index 05449e0..2e380ac 100644 --- a/core/src/backends/eclair/rest/node.rs +++ b/core/src/backends/eclair/rest/node.rs @@ -129,7 +129,9 @@ impl NodeMethods for EclairRest { params.insert("paymentHash", &payment_hash); let mut response = self.client.post(&url).form(¶ms).send().await?; + response = Self::on_response(response).await?; + let data: InvoiceResponse = response.json().await?; Ok(data.into()) diff --git a/core/src/backends/eclair/rest/types.rs b/core/src/backends/eclair/rest/types.rs index b221d86..df22b49 100644 --- a/core/src/backends/eclair/rest/types.rs +++ b/core/src/backends/eclair/rest/types.rs @@ -2,7 +2,7 @@ use crate::{ types::*, - utils::{self, msat_to_sat}, + utils, }; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -103,7 +103,7 @@ pub struct Feature { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InvoiceResponse { - pub payment_request: PayementRequestResponse, + pub payment_request: PaymentRequestResponse, pub payment_preimage: Option, pub status: InvoiceStatusResponse, pub created_at: InvoiceDateResponse, @@ -111,7 +111,7 @@ pub struct InvoiceResponse { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct PayementRequestResponse { +pub struct PaymentRequestResponse { pub prefix: String, pub timestamp: i64, pub node_id: String, @@ -141,7 +141,8 @@ pub struct InvoiceDateResponse { #[serde(rename_all = "camelCase")] pub struct InvoiceStatusResponse { // "type" is a reserved keyword. Fix it with an _ - pub _type: InvoiceStatusTypeResponse, + #[serde(rename = "type")] + pub type_field: InvoiceStatusTypeResponse, pub amount: Option, pub received_at: Option, } diff --git a/core/src/backends/lnd/rest/node.rs b/core/src/backends/lnd/rest/node.rs index 5318b46..e9216f1 100644 --- a/core/src/backends/lnd/rest/node.rs +++ b/core/src/backends/lnd/rest/node.rs @@ -98,7 +98,7 @@ impl NodeMethods for LndRest { } async fn get_invoice(&self, payment_hash: String) -> Result { - let url = format!("{0}/v1/invoice/{1}", self.config.url, payment_hash); + let url = format!("{}/v1/invoice/{}", self.config.url, payment_hash); let mut response = self.client.get(&url).send().await?; From a756d2f39af0e8f83a22d5e910df133c9c8ec6cb Mon Sep 17 00:00:00 2001 From: Dolu Date: Fri, 19 Aug 2022 20:27:34 +0200 Subject: [PATCH 10/11] refactor: rename _type to type_field using serde --- core/src/backends/eclair/rest/types.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/backends/eclair/rest/types.rs b/core/src/backends/eclair/rest/types.rs index df22b49..52fa2b6 100644 --- a/core/src/backends/eclair/rest/types.rs +++ b/core/src/backends/eclair/rest/types.rs @@ -140,7 +140,6 @@ pub struct InvoiceDateResponse { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct InvoiceStatusResponse { - // "type" is a reserved keyword. Fix it with an _ #[serde(rename = "type")] pub type_field: InvoiceStatusTypeResponse, pub amount: Option, @@ -150,7 +149,7 @@ pub struct InvoiceStatusResponse { impl Into for InvoiceResponse { fn into(self) -> Invoice { let mut settle_date: Option = None; - let settled = match self.status._type { + let settled = match self.status.type_field { InvoiceStatusTypeResponse::Received => match self.status.received_at { Some(e) => { settle_date = Some(e.unix); @@ -161,7 +160,7 @@ impl Into for InvoiceResponse { _ => false, }; - let status = match self.status._type { + let status = match self.status.type_field { InvoiceStatusTypeResponse::Received => crate::types::InvoiceStatus::Settled, InvoiceStatusTypeResponse::Pending => crate::types::InvoiceStatus::Pending, InvoiceStatusTypeResponse::Expired => crate::types::InvoiceStatus::Cancelled, @@ -170,7 +169,7 @@ impl Into for InvoiceResponse { Invoice { bolt11: self.payment_request.serialized, memo: self.payment_request.description, - amount: msat_to_sat(self.payment_request.amount), + amount: utils::msat_to_sat(self.payment_request.amount), amount_msat: self.payment_request.amount, pre_image: self.payment_preimage, payment_hash: self.payment_request.payment_hash, From ae4cb4df8a8ad849ceb5bd5dd3863b15d748a975 Mon Sep 17 00:00:00 2001 From: Dolu Date: Mon, 22 Aug 2022 10:51:49 +0200 Subject: [PATCH 11/11] refactor: improve parse_number() error --- bindings/una-js/src/lib.rs | 9 +++++++-- bindings/una-python/src/lib.rs | 11 ++++++++--- core/src/backends/cln/grpc/node.rs | 3 ++- core/src/backends/eclair/rest/node.rs | 4 ++-- core/src/backends/eclair/rest/types.rs | 5 +---- core/src/backends/lnd/rest/types.rs | 23 +++++++++-------------- core/src/error.rs | 2 ++ core/src/utils.rs | 6 ++++-- 8 files changed, 35 insertions(+), 28 deletions(-) diff --git a/bindings/una-js/src/lib.rs b/bindings/una-js/src/lib.rs index 4704215..3d7e866 100644 --- a/bindings/una-js/src/lib.rs +++ b/bindings/una-js/src/lib.rs @@ -105,12 +105,17 @@ impl JsNode { ts_args_type = "paymentHash: string", ts_return_type = "Promise" )] - pub fn get_invoice(&self, env: Env, payement_hash: String) -> Result { + pub fn get_invoice(&self, env: Env, payment_hash: String) -> Result { let node = self.0.clone(); env.execute_tokio_future( async move { - let invoice = node.lock().await.get_invoice(payment_hash).await.or_napi_error()?; + let invoice = node + .lock() + .await + .get_invoice(payment_hash) + .await + .or_napi_error()?; Ok(invoice) }, |&mut env, invoice| Ok(env.to_js_value(&invoice)), diff --git a/bindings/una-python/src/lib.rs b/bindings/una-python/src/lib.rs index 05caa56..95cafc3 100644 --- a/bindings/una-python/src/lib.rs +++ b/bindings/una-python/src/lib.rs @@ -11,8 +11,8 @@ use una_core::{ }, node::{Node, NodeMethods}, types::{ - Backend, CreateInvoiceParams, CreateInvoiceResult, NodeConfig, NodeInfo, PayInvoiceParams, - PayInvoiceResult, Invoice, + Backend, CreateInvoiceParams, CreateInvoiceResult, Invoice, NodeConfig, NodeInfo, + PayInvoiceParams, PayInvoiceResult, }, }; @@ -110,7 +110,12 @@ impl PyNode { let node = self.0.clone(); pyo3_asyncio::tokio::future_into_py(py, async move { - let result = node.lock().await.get_invoice(payment_hash).await.or_py_error()?; + let result = node + .lock() + .await + .get_invoice(payment_hash) + .await + .or_py_error()?; let result = Python::with_gil(|py| pythonize::(py, &result).or_py_error())?; Ok(result) }) diff --git a/core/src/backends/cln/grpc/node.rs b/core/src/backends/cln/grpc/node.rs index 65d707d..2d2d80f 100644 --- a/core/src/backends/cln/grpc/node.rs +++ b/core/src/backends/cln/grpc/node.rs @@ -73,7 +73,8 @@ impl NodeMethods for ClnGrpc { async fn get_invoice(&self, payment_hash: String) -> Result { let mut client = self.get_client().await?; - let payment_hash_hex = hex::decode(payment_hash).map_err(|_| Error::ApiError(String::from("Invalid payment_hash")))?; + let payment_hash_hex = hex::decode(payment_hash) + .map_err(|_| Error::ApiError(String::from("Invalid payment_hash")))?; let request = ListinvoicesRequest { payment_hash: Some(payment_hash_hex), diff --git a/core/src/backends/eclair/rest/node.rs b/core/src/backends/eclair/rest/node.rs index 2e380ac..48f0fcd 100644 --- a/core/src/backends/eclair/rest/node.rs +++ b/core/src/backends/eclair/rest/node.rs @@ -129,9 +129,9 @@ impl NodeMethods for EclairRest { params.insert("paymentHash", &payment_hash); let mut response = self.client.post(&url).form(¶ms).send().await?; - + response = Self::on_response(response).await?; - + let data: InvoiceResponse = response.json().await?; Ok(data.into()) diff --git a/core/src/backends/eclair/rest/types.rs b/core/src/backends/eclair/rest/types.rs index 52fa2b6..25a4f72 100644 --- a/core/src/backends/eclair/rest/types.rs +++ b/core/src/backends/eclair/rest/types.rs @@ -1,9 +1,6 @@ #![allow(clippy::from_over_into)] -use crate::{ - types::*, - utils, -}; +use crate::{types::*, utils}; use serde::{Deserialize, Serialize}; use serde_json::Value; diff --git a/core/src/backends/lnd/rest/types.rs b/core/src/backends/lnd/rest/types.rs index 4f382fb..4f626be 100644 --- a/core/src/backends/lnd/rest/types.rs +++ b/core/src/backends/lnd/rest/types.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; -use crate::utils::{b64_to_hex, parse_number}; use crate::{types::*, utils}; pub type Base64String = String; @@ -163,8 +162,9 @@ impl TryInto for InvoiceResponse { fn try_into(self) -> Result { let mut settle_date: Option = None; if self.settled { - settle_date = - Some(parse_number(&self.settle_date).expect("settle_date should be a number")); + settle_date = Some( + utils::parse_number(&self.settle_date).expect("settle_date should be a number"), + ); } let status = match self.state { @@ -177,19 +177,14 @@ impl TryInto for InvoiceResponse { let invoice = Invoice { bolt11: self.payment_request, memo: self.memo, - amount: parse_number(&self.value).expect("amt_paid_sat should be a number"), - amount_msat: parse_number(&self.value_msat).expect("amt_paid_msat should be a number"), - pre_image: Some( - b64_to_hex(&self.r_preimage) - .expect("coudln't convert r_preimage from base64 to hex"), - ), - payment_hash: b64_to_hex(&self.r_hash) - .expect("coudln't convert r_hash from base64 to hex"), + amount: utils::parse_number(&self.value)?, + amount_msat: utils::parse_number(&self.value_msat)?, + pre_image: Some(utils::b64_to_hex(&self.r_preimage)?), + payment_hash: utils::b64_to_hex(&self.r_hash)?, settled: self.settled, settle_date, - creation_date: parse_number(&self.creation_date) - .expect("creation_date should be a number"), - expiry: parse_number(&self.expiry).expect("expiry should be a number"), + creation_date: utils::parse_number(&self.creation_date)?, + expiry: utils::parse_number(&self.expiry)?, status, }; diff --git a/core/src/error.rs b/core/src/error.rs index e85b0ac..a707a31 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -33,6 +33,7 @@ pub enum Error { ApiError(String), UnknownError(String), ConversionError(String), + ParseNumberError(String), } impl Display for Error { @@ -47,6 +48,7 @@ impl Display for Error { Error::ApiError(err) => err.clone(), Error::ConversionError(err) => err.clone(), Error::UnknownError(err) => err.clone(), + Error::ParseNumberError(err) => err.clone(), }; write!(f, "{}", str) diff --git a/core/src/utils.rs b/core/src/utils.rs index ecdf338..4d04bf8 100644 --- a/core/src/utils.rs +++ b/core/src/utils.rs @@ -46,6 +46,8 @@ pub fn b64_to_hex(b64: &str) -> Result { Ok(hex::encode(&bytes)) } -pub fn parse_number(text: &str) -> Result { - text.trim().parse::() +pub fn parse_number(text: &str) -> Result { + text.trim() + .parse::() + .map_err(|_| Error::ParseNumberError(String::from("Could not parse number"))) }