Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: gateway can pay invoices as itself #5868

Merged
merged 1 commit into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat: expose ILnRpcClient::pay() in gateway CLI
  • Loading branch information
tvolk131 committed Aug 26, 2024
commit c88021b4d136d83bc1da79bb76e99eaeac52f6ed
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions gateway/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ clap_complete = "4.5.23"
fedimint-core = { workspace = true }
fedimint-logging = { workspace = true }
fedimint-mint-client = { workspace = true }
lightning-invoice = { workspace = true }
ln-gateway = { version = "=0.5.0-alpha", default-features = false, package = "fedimint-ln-gateway", path = "../ln-gateway" }
reqwest = { workspace = true }
serde = { workspace = true }
Expand Down
26 changes: 26 additions & 0 deletions gateway/cli/src/lightning_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::time::Duration;

use clap::Subcommand;
use fedimint_core::util::{backoff_util, retry};
use fedimint_core::Amount;
use lightning_invoice::Bolt11Invoice;
use ln_gateway::rpc::rpc_client::GatewayRpcClient;
use ln_gateway::rpc::{
CloseChannelsWithPeerPayload, GetLnOnchainAddressPayload, OpenChannelPayload,
Expand All @@ -28,6 +30,16 @@ pub enum LightningCommands {
#[clap(long)]
description: Option<String>,
},
/// Pay a lightning invoice as the gateway (i.e. no e-cash exchange).
PayInvoice {
invoice: Bolt11Invoice,

#[clap(long, default_value_t = 144)]
max_delay: u64,

#[clap(long)]
max_fee_msats: Amount,
},
/// Get a Bitcoin address from the gateway's lightning node's onchain
/// wallet.
GetLnOnchainAddress,
Expand Down Expand Up @@ -93,6 +105,20 @@ impl LightningCommands {
.await?;
println!("{response}");
}
Self::PayInvoice {
tvolk131 marked this conversation as resolved.
Show resolved Hide resolved
invoice,
max_delay,
max_fee_msats,
} => {
let response = create_client()
.pay_invoice(ln_gateway::rpc::PayInvoicePayload {
invoice,
max_delay,
max_fee: max_fee_msats,
})
.await?;
println!("{response}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why we"re not using print_response?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for types that implement std::fmt::Display, print_response() will wrap the value in quotations where as using println!() simply prints the value.

}
Self::GetLnOnchainAddress => {
let response = create_client()
.get_ln_onchain_address(GetLnOnchainAddressPayload {})
Expand Down
31 changes: 25 additions & 6 deletions gateway/ln-gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ use fedimint_core::task::{sleep, TaskGroup, TaskHandle, TaskShutdownToken};
use fedimint_core::time::duration_since_epoch;
use fedimint_core::util::{SafeUrl, Spanned};
use fedimint_core::{fedimint_build_code_version_env, Amount, BitcoinAmountOrAll, BitcoinHash};
use fedimint_ln_client::pay::PayInvoicePayload;
use fedimint_ln_common::config::{GatewayFee, LightningClientConfig};
use fedimint_ln_common::contracts::Preimage;
use fedimint_ln_common::LightningCommonInit;
Expand All @@ -95,11 +94,12 @@ use lightning_invoice::{Bolt11Invoice, RoutingFees};
use rand::Rng;
use rpc::{
CloseChannelsWithPeerPayload, CreateInvoiceForSelfPayload, FederationInfo, GatewayFedConfig,
GatewayInfo, LeaveFedPayload, OpenChannelPayload, ReceiveEcashPayload, ReceiveEcashResponse,
SetConfigurationPayload, SpendEcashPayload, SpendEcashResponse, V1_API_ENDPOINT,
GatewayInfo, LeaveFedPayload, OpenChannelPayload, PayInvoicePayload, ReceiveEcashPayload,
ReceiveEcashResponse, SetConfigurationPayload, SpendEcashPayload, SpendEcashResponse,
V1_API_ENDPOINT,
};
use state_machine::pay::OutgoingPaymentError;
use state_machine::GatewayClientModule;
use state_machine::{GatewayClientModule, GatewayExtPayStates};
use thiserror::Error;
use tokio::sync::RwLock;
use tracing::{debug, error, info, info_span, warn, Instrument};
Expand All @@ -115,7 +115,6 @@ use crate::rpc::{
BackupPayload, BalancePayload, ConnectFedPayload, DepositAddressPayload, FederationBalanceInfo,
GatewayBalances, RestorePayload, WithdrawPayload,
};
use crate::state_machine::GatewayExtPayStates;
use crate::types::PrettyInterceptHtlcRequest;

/// How long a gateway announcement stays valid
Expand Down Expand Up @@ -936,9 +935,29 @@ impl Gateway {
.map_err(|e| GatewayError::UnexpectedState(e.to_string()))
}

/// Requests the gateway to pay an outgoing LN invoice using its own funds.
/// Returns the payment hash's preimage on success.
async fn handle_pay_invoice_self_msg(&self, payload: PayInvoicePayload) -> Result<Preimage> {
if let GatewayState::Running { lightning_context } = self.get_state().await {
let res = lightning_context
.lnrpc
.pay(payload.invoice, payload.max_delay, payload.max_fee)
.await?;
Ok(Preimage(
res.preimage.try_into().expect("preimage is 32 bytes"),
))
} else {
warn!("Gateway is not connected to lightning node, cannot pay invoice");
Err(GatewayError::Disconnected)
}
}

/// Requests the gateway to pay an outgoing LN invoice on behalf of a
/// Fedimint client. Returns the payment hash's preimage on success.
async fn handle_pay_invoice_msg(&self, payload: PayInvoicePayload) -> Result<Preimage> {
async fn handle_pay_invoice_msg(
&self,
payload: fedimint_ln_client::pay::PayInvoicePayload,
) -> Result<Preimage> {
if let GatewayState::Running { .. } = self.get_state().await {
debug!("Handling pay invoice message: {payload:?}");
let client = self.select_client(payload.federation_id).await?;
Expand Down
9 changes: 8 additions & 1 deletion gateway/ln-gateway/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use fedimint_core::core::OperationId;
use fedimint_core::{secp256k1, Amount, BitcoinAmountOrAll};
use fedimint_ln_common::config::parse_routing_fees;
use fedimint_mint_client::OOBNotes;
use lightning_invoice::RoutingFees;
use lightning_invoice::{Bolt11Invoice, RoutingFees};
use serde::{Deserialize, Serialize};

use crate::lightning::LightningMode;
Expand Down Expand Up @@ -158,6 +158,13 @@ pub struct CreateInvoiceForSelfPayload {
pub description: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PayInvoicePayload {
pub invoice: Bolt11Invoice,
pub max_delay: u64,
pub max_fee: Amount,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GetLnOnchainAddressPayload;

Expand Down
16 changes: 13 additions & 3 deletions gateway/ln-gateway/src/rpc/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use fedimint_ln_common::gateway_endpoint_constants::{
RECEIVE_ECASH_ENDPOINT, RESTORE_ENDPOINT, SET_CONFIGURATION_ENDPOINT, SPEND_ECASH_ENDPOINT,
WITHDRAW_ENDPOINT,
};
use fedimint_lnv2_common::endpoint_constants::CREATE_BOLT11_INVOICE_FOR_SELF_ENDPOINT;
use fedimint_lnv2_common::endpoint_constants::{
CREATE_BOLT11_INVOICE_FOR_SELF_ENDPOINT, PAY_INVOICE_SELF_ENDPOINT,
};
use lightning_invoice::Bolt11Invoice;
use reqwest::{Method, StatusCode};
use serde::de::DeserializeOwned;
Expand All @@ -21,8 +23,8 @@ use super::{
BackupPayload, BalancePayload, CloseChannelsWithPeerPayload, ConfigPayload, ConnectFedPayload,
CreateInvoiceForSelfPayload, DepositAddressPayload, FederationInfo, GatewayBalances,
GatewayFedConfig, GatewayInfo, GetLnOnchainAddressPayload, LeaveFedPayload, OpenChannelPayload,
ReceiveEcashPayload, ReceiveEcashResponse, RestorePayload, SetConfigurationPayload,
SpendEcashPayload, SpendEcashResponse, WithdrawPayload,
PayInvoicePayload, ReceiveEcashPayload, ReceiveEcashResponse, RestorePayload,
SetConfigurationPayload, SpendEcashPayload, SpendEcashResponse, WithdrawPayload,
};
use crate::lightning::ChannelInfo;
use crate::CloseChannelsWithPeerResponse;
Expand Down Expand Up @@ -162,6 +164,14 @@ impl GatewayRpcClient {
self.call_post(url, payload).await
}

pub async fn pay_invoice(&self, payload: PayInvoicePayload) -> GatewayRpcResult<String> {
let url = self
.base_url
.join(PAY_INVOICE_SELF_ENDPOINT)
.expect("invalid base url");
self.call_post(url, payload).await
}

pub async fn get_ln_onchain_address(
&self,
payload: GetLnOnchainAddressPayload,
Expand Down
19 changes: 14 additions & 5 deletions gateway/ln-gateway/src/rpc/rpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use bitcoin_hashes::{sha256, Hash};
use fedimint_core::config::FederationId;
use fedimint_core::encoding::Encodable;
use fedimint_core::task::TaskGroup;
use fedimint_ln_client::pay::PayInvoicePayload;
use fedimint_ln_common::gateway_endpoint_constants::{
ADDRESS_ENDPOINT, BACKUP_ENDPOINT, BALANCE_ENDPOINT, CLOSE_CHANNELS_WITH_PEER_ENDPOINT,
CONFIGURATION_ENDPOINT, CONNECT_FED_ENDPOINT, GATEWAY_INFO_ENDPOINT,
Expand All @@ -21,8 +20,8 @@ use fedimint_ln_common::gateway_endpoint_constants::{
};
use fedimint_lnv2_client::{CreateBolt11InvoicePayload, SendPaymentPayload};
use fedimint_lnv2_common::endpoint_constants::{
CREATE_BOLT11_INVOICE_ENDPOINT, CREATE_BOLT11_INVOICE_FOR_SELF_ENDPOINT, ROUTING_INFO_ENDPOINT,
SEND_PAYMENT_ENDPOINT,
CREATE_BOLT11_INVOICE_ENDPOINT, CREATE_BOLT11_INVOICE_FOR_SELF_ENDPOINT,
PAY_INVOICE_SELF_ENDPOINT, ROUTING_INFO_ENDPOINT, SEND_PAYMENT_ENDPOINT,
};
use hex::ToHex;
use serde_json::{json, Value};
Expand All @@ -33,7 +32,7 @@ use tracing::{error, info, instrument};
use super::{
BackupPayload, BalancePayload, CloseChannelsWithPeerPayload, ConnectFedPayload,
CreateInvoiceForSelfPayload, DepositAddressPayload, GetLnOnchainAddressPayload, InfoPayload,
LeaveFedPayload, OpenChannelPayload, ReceiveEcashPayload, RestorePayload,
LeaveFedPayload, OpenChannelPayload, PayInvoicePayload, ReceiveEcashPayload, RestorePayload,
SetConfigurationPayload, SpendEcashPayload, WithdrawPayload, V1_API_ENDPOINT,
};
use crate::rpc::ConfigPayload;
Expand Down Expand Up @@ -175,6 +174,7 @@ fn v1_routes(gateway: Arc<Gateway>) -> Router {
.route(LEAVE_FED_ENDPOINT, post(leave_fed))
.route(BACKUP_ENDPOINT, post(backup))
.route(RESTORE_ENDPOINT, post(restore))
.route(PAY_INVOICE_SELF_ENDPOINT, post(pay_invoice_self))
.route(
GET_LN_ONCHAIN_ADDRESS_ENDPOINT,
post(get_ln_onchain_address),
Expand Down Expand Up @@ -292,9 +292,18 @@ async fn create_invoice_for_self(
}

#[instrument(skip_all, err, fields(?payload))]
async fn pay_invoice(
async fn pay_invoice_self(
Extension(gateway): Extension<Arc<Gateway>>,
Json(payload): Json<PayInvoicePayload>,
) -> Result<impl IntoResponse, GatewayError> {
let preimage = gateway.handle_pay_invoice_self_msg(payload).await?;
Ok(Json(json!(preimage.0.encode_hex::<String>())))
}

#[instrument(skip_all, err, fields(?payload))]
async fn pay_invoice(
Extension(gateway): Extension<Arc<Gateway>>,
Json(payload): Json<fedimint_ln_client::pay::PayInvoicePayload>,
) -> Result<impl IntoResponse, GatewayError> {
let preimage = gateway.handle_pay_invoice_msg(payload).await?;
Ok(Json(json!(preimage.0.encode_hex::<String>())))
Expand Down
1 change: 1 addition & 0 deletions modules/fedimint-lnv2-common/src/endpoint_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub const CREATE_BOLT11_INVOICE_ENDPOINT: &str = "/create_bolt11_invoice";
pub const CREATE_BOLT11_INVOICE_FOR_SELF_ENDPOINT: &str = "/create_bolt11_invoice_for_self";
pub const GATEWAYS_ENDPOINT: &str = "gateways";
pub const OUTGOING_CONTRACT_EXPIRATION_ENDPOINT: &str = "outgoing_contract_expiration";
pub const PAY_INVOICE_SELF_ENDPOINT: &str = "/pay_invoice_self";
pub const REMOVE_GATEWAY_ENDPOINT: &str = "remove_gateway";
pub const ROUTING_INFO_ENDPOINT: &str = "/routing_info";
pub const SEND_PAYMENT_ENDPOINT: &str = "/send_payment";
Loading