1use std::collections::{BTreeMap, BTreeSet};
2use std::fmt;
3use std::str::FromStr;
4use std::time::{Duration, SystemTime};
5
6use bitcoin::address::NetworkUnchecked;
7use bitcoin::hashes::sha256;
8use bitcoin::secp256k1::PublicKey;
9use bitcoin::{Address, Network, OutPoint};
10use clap::Subcommand;
11use envs::{
12 FM_LDK_ALIAS_ENV, FM_LND_MACAROON_ENV, FM_LND_PAYMENT_TIMEOUT_SECS_ENV, FM_LND_RPC_ADDR_ENV,
13 FM_LND_TIME_PREF_ENV, FM_LND_TLS_CERT_ENV, FM_PORT_LDK,
14};
15use fedimint_core::config::{FederationId, JsonClientConfig};
16use fedimint_core::encoding::{Decodable, Encodable};
17use fedimint_core::invite_code::InviteCode;
18use fedimint_core::util::{SafeUrl, get_average, get_median};
19use fedimint_core::{Amount, BitcoinAmountOrAll, secp256k1};
20use fedimint_eventlog::{EventKind, EventLogId, PersistedLogEntry, StructuredPaymentEvents};
21use fedimint_lnv2_common::gateway_api::PaymentFee;
22use fedimint_wallet_client::PegOutFees;
23use lightning::ln::msgs::SocketAddress;
24use lightning_invoice::Bolt11Invoice;
25use serde::de::Error as _;
26use serde::{Deserialize, Deserializer, Serialize, Serializer};
27
28pub mod envs;
29pub mod federation_status;
30
31pub use federation_status::*;
32
33pub const V1_API_ENDPOINT: &str = "v1";
34
35pub const ADDRESS_ENDPOINT: &str = "/address";
36pub const ADDRESS_RECHECK_ENDPOINT: &str = "/address_recheck";
37pub const BACKUP_ENDPOINT: &str = "/backup";
38pub const CONFIGURATION_ENDPOINT: &str = "/config";
39pub const CONNECT_FED_ENDPOINT: &str = "/connect_fed";
40pub const CREATE_BOLT11_INVOICE_FOR_OPERATOR_ENDPOINT: &str = "/create_bolt11_invoice_for_operator";
41pub const CREATE_BOLT12_OFFER_FOR_OPERATOR_ENDPOINT: &str = "/create_bolt12_offer_for_operator";
42pub const FEDERATION_STATUS_ENDPOINT: &str = "/federation_status";
43pub const GATEWAY_INFO_ENDPOINT: &str = "/info";
44pub const INVITE_CODES_ENDPOINT: &str = "/invite_codes";
45pub const GET_BALANCES_ENDPOINT: &str = "/balances";
46pub const GET_INVOICE_ENDPOINT: &str = "/get_invoice";
47pub const GET_LN_ONCHAIN_ADDRESS_ENDPOINT: &str = "/get_ln_onchain_address";
48pub const LEAVE_FED_ENDPOINT: &str = "/leave_fed";
49pub const LIST_CHANNELS_ENDPOINT: &str = "/list_channels";
50pub const LIST_TRANSACTIONS_ENDPOINT: &str = "/list_transactions";
51pub const MNEMONIC_ENDPOINT: &str = "/mnemonic";
52pub const CONNECT_PEER_ENDPOINT: &str = "/connect_peer";
53pub const OPEN_CHANNEL_ENDPOINT: &str = "/open_channel";
54pub const OPEN_CHANNEL_WITH_PUSH_ENDPOINT: &str = "/open_channel_with_push";
55pub const CLOSE_CHANNELS_WITH_PEER_ENDPOINT: &str = "/close_channels_with_peer";
56pub const PAY_INVOICE_FOR_OPERATOR_ENDPOINT: &str = "/pay_invoice_for_operator";
57pub const PAY_OFFER_FOR_OPERATOR_ENDPOINT: &str = "/pay_offer_for_operator";
58pub const PAYMENT_LOG_ENDPOINT: &str = "/payment_log";
59pub const PAYMENT_SUMMARY_ENDPOINT: &str = "/payment_summary";
60pub const PEGIN_FROM_ONCHAIN_ENDPOINT: &str = "/pegin_from_onchain";
61pub const RECEIVE_ECASH_ENDPOINT: &str = "/receive_ecash";
62pub const SET_CHANNEL_FEES_ENDPOINT: &str = "/set_channel_fees";
63pub const SET_FEES_ENDPOINT: &str = "/set_fees";
64pub const SET_PAYMENT_POLICY_ENDPOINT: &str = "/set_payment_policy";
65pub const STOP_ENDPOINT: &str = "/stop";
66pub const SEND_ONCHAIN_ENDPOINT: &str = "/send_onchain";
67pub const SPEND_ECASH_ENDPOINT: &str = "/spend_ecash";
68pub const WITHDRAW_ENDPOINT: &str = "/withdraw";
69pub const WITHDRAW_TO_ONCHAIN_ENDPOINT: &str = "/withdraw_to_onchain";
70
71pub const DEFAULT_LIGHTNING_PORT: u16 = 9735;
72
73#[derive(Debug, Serialize, Deserialize, Clone)]
74pub struct ConnectFedPayload {
75 pub invite_code: String,
76 pub use_tor: Option<bool>,
77 pub recover: Option<bool>,
78}
79
80#[derive(Debug, Serialize, Deserialize, Clone)]
81pub struct LeaveFedPayload {
82 pub federation_id: FederationId,
83}
84
85#[derive(Debug, Serialize, Deserialize)]
86pub struct InfoPayload;
87
88#[derive(Debug, Serialize, Deserialize)]
89pub struct BackupPayload {
90 pub federation_id: FederationId,
91}
92
93#[derive(Debug, Serialize, Deserialize, Clone)]
94pub struct ConfigPayload {
95 pub federation_id: Option<FederationId>,
96}
97
98#[derive(Debug, Serialize, Deserialize, Clone)]
99pub struct DepositAddressPayload {
100 pub federation_id: FederationId,
101}
102
103#[derive(Debug, Serialize, Deserialize, Clone)]
104pub struct PeginFromOnchainPayload {
105 pub federation_id: FederationId,
106 pub amount: BitcoinAmountOrAll,
107 pub fee_rate_sats_per_vbyte: u64,
108}
109
110#[derive(Debug, Serialize, Deserialize, Clone)]
111pub struct DepositAddressRecheckPayload {
112 pub address: Address<NetworkUnchecked>,
113 pub federation_id: FederationId,
114}
115
116#[derive(Debug, Serialize, Deserialize, Clone)]
117pub struct WithdrawPayload {
118 pub federation_id: FederationId,
119 pub amount: BitcoinAmountOrAll,
120 pub address: Address<NetworkUnchecked>,
121 #[serde(default)]
124 pub quoted_fees: Option<PegOutFees>,
125}
126
127#[derive(Debug, Serialize, Deserialize, Clone)]
128pub struct WithdrawToOnchainPayload {
129 pub federation_id: FederationId,
130 pub amount: BitcoinAmountOrAll,
131}
132
133#[derive(Debug, Serialize, Deserialize, Clone)]
134pub struct WithdrawResponse {
135 pub txid: bitcoin::Txid,
136 pub fees: PegOutFees,
137}
138
139#[derive(Debug, Serialize, Deserialize, Clone)]
140pub struct WithdrawPreviewPayload {
141 pub federation_id: FederationId,
142 pub amount: BitcoinAmountOrAll,
143 pub address: Address<NetworkUnchecked>,
144}
145
146#[derive(Debug, Serialize, Deserialize, Clone)]
147pub struct WithdrawPreviewResponse {
148 pub withdraw_amount: Amount,
149 pub address: String,
150 pub peg_out_fees: PegOutFees,
151 pub total_cost: Amount,
152 #[serde(default)]
156 pub mint_fees: Option<Amount>,
157 #[serde(default)]
160 pub flat_peg_out_fee: bool,
161}
162
163#[allow(deprecated)]
167#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
168pub enum ConnectorType {
169 Tcp,
170 Tor,
171}
172
173#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
174pub struct FederationConfig {
175 pub invite_code: InviteCode,
176 #[serde(alias = "mint_channel_id")]
179 pub federation_index: u64,
180 pub lightning_fee: PaymentFee,
181 pub transaction_fee: PaymentFee,
182 #[serde(default)]
189 pub payment_policies: BTreeSet<PaymentPolicy>,
190 #[allow(deprecated)] pub _connector: ConnectorType,
192}
193
194impl FederationConfig {
195 pub fn receive_enabled(&self) -> bool {
198 !self
199 .payment_policies
200 .contains(&PaymentPolicy::ReceivesDisabled)
201 }
202}
203
204#[derive(
207 Debug,
208 Clone,
209 Copy,
210 Eq,
211 PartialEq,
212 Ord,
213 PartialOrd,
214 Hash,
215 Encodable,
216 Decodable,
217 Serialize,
218 Deserialize,
219)]
220pub enum PaymentPolicy {
221 ReceivesDisabled,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
228pub struct FederationInfo {
229 pub federation_id: FederationId,
230 pub federation_name: Option<String>,
231 pub balance_msat: Amount,
232 pub config: FederationConfig,
233 pub last_backup_time: Option<SystemTime>,
234}
235
236#[derive(Debug, Serialize, Deserialize, PartialEq)]
237pub struct GatewayInfo {
238 pub version_hash: String,
239 pub federations: Vec<FederationInfo>,
240 #[serde(alias = "channels")]
243 pub federation_fake_scids: Option<BTreeMap<u64, FederationId>>,
244 pub gateway_state: String,
245 pub lightning_info: LightningInfo,
246 pub lightning_mode: LightningMode,
247 pub registrations: BTreeMap<RegisteredProtocol, (SafeUrl, secp256k1::PublicKey)>,
248}
249
250#[derive(Debug, Serialize, Deserialize, PartialEq)]
251pub struct GatewayFedConfig {
252 pub federations: BTreeMap<FederationId, JsonClientConfig>,
253}
254
255#[derive(Debug, Serialize, Deserialize, Clone)]
256pub struct SetFeesPayload {
257 pub federation_id: Option<FederationId>,
258 pub lightning_base: Option<Amount>,
259 pub lightning_parts_per_million: Option<u64>,
260 pub transaction_base: Option<Amount>,
261 pub transaction_parts_per_million: Option<u64>,
262}
263
264#[derive(Debug, Serialize, Deserialize, Clone)]
268pub struct SetPaymentPolicyPayload {
269 pub federation_id: Option<FederationId>,
271 pub receive_enabled: Option<bool>,
275}
276
277#[derive(Debug, Serialize, Deserialize, Clone)]
278pub struct CreateInvoiceForOperatorPayload {
279 pub amount_msats: u64,
280 pub expiry_secs: Option<u32>,
281 pub description: Option<String>,
282}
283
284#[derive(Debug, Serialize, Deserialize, Clone)]
285pub struct PayInvoiceForOperatorPayload {
286 pub invoice: Bolt11Invoice,
287}
288
289#[derive(Debug, Serialize, Deserialize, Clone)]
290pub struct SpendEcashPayload {
291 pub federation_id: FederationId,
293 pub amount: Amount,
295}
296
297#[derive(Debug, Serialize, Deserialize, Clone)]
298pub struct SpendEcashResponse {
299 pub notes: String,
301}
302
303#[derive(Debug, Serialize, Deserialize, Clone)]
304pub struct ReceiveEcashPayload {
305 pub notes: String,
307}
308
309#[derive(Debug, Serialize, Deserialize, Clone)]
310pub struct ReceiveEcashResponse {
311 pub amount: Amount,
312}
313
314#[derive(serde::Serialize, serde::Deserialize, Clone)]
315pub struct GatewayBalances {
316 pub onchain_balance_sats: u64,
317 pub lightning_balance_msats: u64,
318 pub ecash_balances: Vec<FederationBalanceInfo>,
319 pub inbound_lightning_liquidity_msats: u64,
320}
321
322#[derive(serde::Serialize, serde::Deserialize, Clone)]
323pub struct FederationBalanceInfo {
324 pub federation_id: FederationId,
325 pub ecash_balance_msats: Amount,
326}
327
328#[derive(Debug, Serialize, Deserialize, Clone)]
329pub struct MnemonicResponse {
330 pub mnemonic: Vec<String>,
331
332 pub legacy_federations: Vec<FederationId>,
336}
337
338#[derive(Debug, Serialize, Deserialize, Clone)]
339pub struct PaymentLogPayload {
340 pub end_position: Option<EventLogId>,
343
344 pub pagination_size: usize,
346
347 pub federation_id: FederationId,
348
349 pub event_kinds: Vec<EventKind>,
357}
358
359#[derive(Debug, Serialize, Deserialize, Clone)]
360pub struct PaymentLogResponse(pub Vec<PersistedLogEntry>);
361
362#[derive(Debug, Serialize, Deserialize, Clone)]
363pub struct PaymentSummaryResponse {
364 pub outgoing: PaymentStats,
365 pub incoming: PaymentStats,
366}
367
368#[derive(Debug, Serialize, Deserialize, Clone)]
369pub struct PaymentStats {
370 pub average_latency: Option<Duration>,
371 pub median_latency: Option<Duration>,
372 pub total_fees: Amount,
373 pub total_success: usize,
374 pub total_failure: usize,
375}
376
377impl PaymentStats {
378 pub fn compute(events: &StructuredPaymentEvents) -> Self {
380 PaymentStats {
381 average_latency: get_average(&events.latencies_usecs).map(Duration::from_micros),
382 median_latency: get_median(&events.latencies_usecs).map(Duration::from_micros),
383 total_fees: Amount::from_msats(events.fees.iter().map(|a| a.msats).sum()),
384 total_success: events.latencies_usecs.len(),
385 total_failure: events.latencies_failure.len(),
386 }
387 }
388}
389
390#[derive(Debug, Serialize, Deserialize, Clone)]
391pub struct PaymentSummaryPayload {
392 pub start_millis: u64,
393 pub end_millis: u64,
394}
395
396#[derive(Serialize, Deserialize, Debug, Clone)]
397pub struct ChannelInfo {
398 pub remote_pubkey: secp256k1::PublicKey,
399 pub channel_size_sats: u64,
400 pub outbound_liquidity_sats: u64,
401 pub inbound_liquidity_sats: u64,
402 pub is_active: bool,
403 pub funding_outpoint: Option<OutPoint>,
404 pub remote_node_alias: Option<String>,
405 #[serde(default)]
406 pub remote_address: Option<String>,
407 #[serde(default)]
410 pub base_fee_msat: Option<u64>,
411 #[serde(default)]
415 pub parts_per_million: Option<u64>,
416}
417
418#[derive(Debug, Serialize, Deserialize, Clone)]
419pub struct OpenChannelRequest {
420 pub pubkey: secp256k1::PublicKey,
421 pub host: String,
422 pub channel_size_sats: u64,
423 pub push_amount_sats: u64,
424 #[serde(default, deserialize_with = "empty_string_as_none")]
428 pub fee_rate_sats_per_vbyte: Option<u64>,
429 #[serde(default, deserialize_with = "empty_string_as_none")]
432 pub base_fee_msat: Option<u64>,
433 #[serde(default, deserialize_with = "empty_string_as_none")]
436 pub parts_per_million: Option<u64>,
437}
438
439#[derive(Debug, Serialize, Deserialize, Clone)]
440pub struct ConnectPeerRequest {
441 pub node_address: NodeAddress,
442}
443
444#[derive(Debug, Clone, PartialEq, Eq)]
445pub struct NodeAddress {
446 pub pubkey: secp256k1::PublicKey,
447 pub address: SocketAddress,
448}
449
450impl NodeAddress {
451 pub fn host_with_port(&self) -> String {
452 self.address.to_string()
453 }
454}
455
456impl FromStr for NodeAddress {
457 type Err = String;
458
459 fn from_str(input: &str) -> Result<Self, Self::Err> {
460 let (pubkey, address) = input
461 .trim()
462 .split_once('@')
463 .ok_or_else(|| "Expected node address in pubkey@host[:port] format".to_string())?;
464 let pubkey = pubkey
465 .parse()
466 .map_err(|err| format!("Invalid peer public key: {err}"))?;
467 let address = address.trim();
468 if address.is_empty() {
469 return Err("Peer host must not be empty".to_string());
470 }
471 let address = if has_explicit_port(address) {
472 address.to_string()
473 } else {
474 format!("{address}:{DEFAULT_LIGHTNING_PORT}")
475 };
476 let address = SocketAddress::from_str(&address)
477 .map_err(|err| format!("Invalid peer address: {err}"))?;
478
479 Ok(NodeAddress { pubkey, address })
480 }
481}
482
483impl fmt::Display for NodeAddress {
484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485 let address = self.address.to_string();
486 if address.ends_with(&format!(":{DEFAULT_LIGHTNING_PORT}")) {
487 write!(
488 f,
489 "{}@{}",
490 self.pubkey,
491 &address[..address.len() - format!(":{DEFAULT_LIGHTNING_PORT}").len()]
492 )
493 } else {
494 write!(f, "{}@{}", self.pubkey, address)
495 }
496 }
497}
498
499fn has_explicit_port(address: &str) -> bool {
500 if address.starts_with('[') {
501 return address.contains("]:");
502 }
503
504 address.contains(':')
505}
506
507impl Serialize for NodeAddress {
508 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
509 where
510 S: Serializer,
511 {
512 serializer.serialize_str(&self.to_string())
513 }
514}
515
516impl<'de> Deserialize<'de> for NodeAddress {
517 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
518 where
519 D: Deserializer<'de>,
520 {
521 String::deserialize(deserializer)?
522 .parse()
523 .map_err(D::Error::custom)
524 }
525}
526
527#[derive(Debug, Serialize, Deserialize, Clone)]
528pub struct SetChannelFeesRequest {
529 pub funding_outpoint: OutPoint,
532 pub base_fee_msat: u64,
534 pub parts_per_million: u64,
536}
537
538fn empty_string_as_none<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
542where
543 D: serde::Deserializer<'de>,
544 T: std::str::FromStr + Deserialize<'de>,
545 T::Err: std::fmt::Display,
546{
547 #[derive(Deserialize)]
548 #[serde(untagged)]
549 enum StringOrT<T> {
550 String(String),
551 T(T),
552 }
553
554 match Option::<StringOrT<T>>::deserialize(deserializer)? {
555 None => Ok(None),
556 Some(StringOrT::T(value)) => Ok(Some(value)),
557 Some(StringOrT::String(s)) if s.trim().is_empty() => Ok(None),
558 Some(StringOrT::String(s)) => s
559 .trim()
560 .parse::<T>()
561 .map(Some)
562 .map_err(serde::de::Error::custom),
563 }
564}
565
566#[derive(Debug, Serialize, Deserialize, Clone)]
567pub struct SendOnchainRequest {
568 pub address: Address<NetworkUnchecked>,
569 pub amount: BitcoinAmountOrAll,
570 pub fee_rate_sats_per_vbyte: u64,
571}
572
573impl fmt::Display for SendOnchainRequest {
574 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575 write!(
576 f,
577 "SendOnchainRequest {{ address: {}, amount: {}, fee_rate_sats_per_vbyte: {} }}",
578 self.address.assume_checked_ref(),
579 self.amount,
580 self.fee_rate_sats_per_vbyte
581 )
582 }
583}
584
585#[derive(Debug, Serialize, Deserialize, Clone)]
586pub struct CloseChannelsWithPeerRequest {
587 pub pubkey: secp256k1::PublicKey,
588 #[serde(default)]
589 pub force: bool,
590 pub sats_per_vbyte: Option<u64>,
591}
592
593impl fmt::Display for CloseChannelsWithPeerRequest {
594 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
595 write!(
596 f,
597 "CloseChannelsWithPeerRequest {{ pubkey: {}, force: {}, sats_per_vbyte: {} }}",
598 self.pubkey,
599 self.force,
600 match self.sats_per_vbyte {
601 Some(sats) => sats.to_string(),
602 None => "None".to_string(),
603 }
604 )
605 }
606}
607
608#[derive(Debug, Serialize, Deserialize, Clone)]
609pub struct CloseChannelsWithPeerResponse {
610 pub num_channels_closed: u32,
611}
612
613#[derive(Debug, Serialize, Deserialize, Clone)]
614pub struct GetInvoiceRequest {
615 pub payment_hash: sha256::Hash,
616}
617
618#[derive(Debug, Serialize, Deserialize, Clone)]
619pub struct GetInvoiceResponse {
620 pub preimage: Option<String>,
621 pub payment_hash: Option<sha256::Hash>,
622 pub amount: Amount,
623 pub created_at: SystemTime,
624 pub status: PaymentStatus,
625}
626
627#[derive(Debug, Serialize, Deserialize, Clone)]
628pub struct ListTransactionsPayload {
629 pub start_secs: u64,
630 pub end_secs: u64,
631}
632
633#[derive(Debug, Serialize, Deserialize, Clone)]
634pub struct ListTransactionsResponse {
635 pub transactions: Vec<PaymentDetails>,
636}
637
638#[derive(Debug, Serialize, Deserialize, Clone)]
639pub struct PaymentDetails {
640 pub payment_hash: Option<sha256::Hash>,
641 pub preimage: Option<String>,
642 pub payment_kind: PaymentKind,
643 pub amount: Amount,
644 pub direction: PaymentDirection,
645 pub status: PaymentStatus,
646 pub timestamp_secs: u64,
647}
648
649#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
650pub enum PaymentKind {
651 Bolt11,
652 Bolt12Offer,
653 Bolt12Refund,
654 Onchain,
655}
656
657#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
658pub enum PaymentDirection {
659 Outbound,
660 Inbound,
661}
662
663#[derive(Debug, Serialize, Deserialize, Clone)]
664pub struct CreateOfferPayload {
665 pub amount: Option<Amount>,
666 pub description: Option<String>,
667 pub expiry_secs: Option<u32>,
668 pub quantity: Option<u64>,
669}
670
671#[derive(Debug, Serialize, Deserialize, Clone)]
672pub struct CreateOfferResponse {
673 pub offer: String,
674}
675
676#[derive(Debug, Serialize, Deserialize, Clone)]
677pub struct PayOfferPayload {
678 pub offer: String,
679 pub amount: Option<Amount>,
680 pub quantity: Option<u64>,
681 pub payer_note: Option<String>,
682}
683
684#[derive(Debug, Serialize, Deserialize, Clone)]
685pub struct PayOfferResponse {
686 pub preimage: String,
687}
688
689#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
690pub enum PaymentStatus {
691 Pending,
692 Succeeded,
693 Failed,
694}
695
696pub const LND_DEFAULT_TIME_PREF: f64 = 0.5;
701pub const LND_TIME_PREF_MIN: f64 = -1.0;
702pub const LND_TIME_PREF_MAX: f64 = 1.0;
703
704fn parse_lnd_time_pref(s: &str) -> Result<f64, String> {
705 let parsed: f64 = s.parse().map_err(|e| format!("invalid f64: {e}"))?;
706 if !parsed.is_finite() || !(LND_TIME_PREF_MIN..=LND_TIME_PREF_MAX).contains(&parsed) {
707 return Err(format!(
708 "must be a finite value in [{LND_TIME_PREF_MIN}, {LND_TIME_PREF_MAX}]"
709 ));
710 }
711 Ok(parsed)
712}
713
714pub const LND_DEFAULT_PAYMENT_TIMEOUT_SECS: i32 = 180;
718pub const LND_PAYMENT_TIMEOUT_SECS_MIN: i32 = 1;
719pub const LND_PAYMENT_TIMEOUT_SECS_MAX: i32 = 600;
720
721fn parse_lnd_payment_timeout_secs(s: &str) -> Result<i32, String> {
722 let parsed: i32 = s.parse().map_err(|e| format!("invalid i32: {e}"))?;
723 if !(LND_PAYMENT_TIMEOUT_SECS_MIN..=LND_PAYMENT_TIMEOUT_SECS_MAX).contains(&parsed) {
724 return Err(format!(
725 "must be an integer in [{LND_PAYMENT_TIMEOUT_SECS_MIN}, {LND_PAYMENT_TIMEOUT_SECS_MAX}]"
726 ));
727 }
728 Ok(parsed)
729}
730
731#[derive(Debug, Clone, Subcommand, Serialize, Deserialize, PartialEq)]
732pub enum LightningMode {
733 #[clap(name = "lnd")]
734 Lnd {
735 #[arg(long = "lnd-rpc-host", env = FM_LND_RPC_ADDR_ENV)]
737 lnd_rpc_addr: String,
738
739 #[arg(long = "lnd-tls-cert", env = FM_LND_TLS_CERT_ENV)]
741 lnd_tls_cert: String,
742
743 #[arg(long = "lnd-macaroon", env = FM_LND_MACAROON_ENV)]
745 lnd_macaroon: String,
746
747 #[arg(
750 long = "lnd-time-pref",
751 env = FM_LND_TIME_PREF_ENV,
752 default_value_t = LND_DEFAULT_TIME_PREF,
753 value_parser = parse_lnd_time_pref,
754 )]
755 lnd_time_pref: f64,
756
757 #[arg(
761 long = "lnd-payment-timeout",
762 env = FM_LND_PAYMENT_TIMEOUT_SECS_ENV,
763 default_value_t = LND_DEFAULT_PAYMENT_TIMEOUT_SECS,
764 value_parser = parse_lnd_payment_timeout_secs,
765 )]
766 lnd_payment_timeout_secs: i32,
767 },
768 #[clap(name = "ldk")]
769 Ldk {
770 #[arg(long = "ldk-lightning-port", env = FM_PORT_LDK)]
772 lightning_port: u16,
773
774 #[arg(long = "ldk-alias", env = FM_LDK_ALIAS_ENV)]
776 alias: String,
777 },
778}
779
780#[derive(Clone)]
781pub enum ChainSource {
782 Bitcoind {
783 username: String,
784 password: String,
785 server_url: SafeUrl,
786 },
787 Esplora {
788 server_url: SafeUrl,
789 },
790}
791
792impl fmt::Display for ChainSource {
793 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
794 match self {
795 ChainSource::Bitcoind {
796 username: _,
797 password: _,
798 server_url,
799 } => {
800 write!(f, "Bitcoind source with URL: {server_url}")
801 }
802 ChainSource::Esplora { server_url } => {
803 write!(f, "Esplora source with URL: {server_url}")
804 }
805 }
806 }
807}
808
809#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
810#[serde(rename_all = "snake_case")]
811pub enum LightningInfo {
812 Connected {
813 public_key: PublicKey,
814 alias: String,
815 network: Network,
816 block_height: u64,
817 synced_to_chain: bool,
818 },
819 NotConnected,
820}
821
822#[derive(
823 Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Encodable, Decodable, Serialize, Deserialize,
824)]
825#[serde(rename_all = "snake_case")]
826pub enum RegisteredProtocol {
827 Http,
828 Iroh,
829}
830
831#[derive(Debug, Serialize, Deserialize, Clone)]
832pub struct SetMnemonicPayload {
833 pub words: Option<String>,
834}
835
836#[cfg(test)]
837mod tests;