Skip to content

Commit c15b8ce

Browse files
committed
Add Amount type for CLI amount arguments
Replace raw u64 amount fields with an Amount type that requires a denomination suffix (e.g. 50sat, 50000msat). This removes ambiguity about whether positional arguments are in sats or msats.
1 parent 824da25 commit c15b8ce

2 files changed

Lines changed: 214 additions & 50 deletions

File tree

ldk-server-cli/src/main.rs

Lines changed: 67 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ use ldk_server_client::ldk_server_protos::types::{
4040
};
4141
use serde::Serialize;
4242
use serde_json::{json, Value};
43-
use types::{CliListForwardedPaymentsResponse, CliListPaymentsResponse, CliPaginatedResponse};
43+
use types::{
44+
Amount, CliListForwardedPaymentsResponse, CliListPaymentsResponse, CliPaginatedResponse,
45+
};
4446

4547
mod config;
4648
mod types;
@@ -97,10 +99,8 @@ enum Commands {
9799
OnchainSend {
98100
#[arg(help = "The address to send coins to")]
99101
address: String,
100-
#[arg(
101-
help = "The amount in satoshis to send. Will respect any on-chain reserve needed for anchor channels"
102-
)]
103-
amount_sats: Option<u64>,
102+
#[arg(help = "The amount to send, e.g. 50sat or 50000msat")]
103+
amount: Option<Amount>,
104104
#[arg(
105105
long,
106106
help = "Send full balance to the address. Warning: will not retain on-chain reserves for anchor channels"
@@ -115,9 +115,9 @@ enum Commands {
115115
#[command(about = "Create a BOLT11 invoice to receive a payment")]
116116
Bolt11Receive {
117117
#[arg(
118-
help = "Amount in millisatoshis to request. If unset, a variable-amount invoice is returned"
118+
help = "Amount to request, e.g. 50sat or 50000msat. If unset, a variable-amount invoice is returned"
119119
)]
120-
amount_msat: Option<u64>,
120+
amount: Option<Amount>,
121121
#[arg(short, long, help = "Description to attach along with the invoice")]
122122
description: Option<String>,
123123
#[arg(
@@ -132,13 +132,15 @@ enum Commands {
132132
Bolt11Send {
133133
#[arg(help = "A BOLT11 invoice for a payment within the Lightning Network")]
134134
invoice: String,
135-
#[arg(help = "Amount in millisatoshis. Required when paying a zero-amount invoice")]
136-
amount_msat: Option<u64>,
135+
#[arg(
136+
help = "Amount to send, e.g. 50sat or 50000msat. Required when paying a zero-amount invoice"
137+
)]
138+
amount: Option<Amount>,
137139
#[arg(
138140
long,
139-
help = "Maximum total fees in millisatoshis that may accrue during route finding. Defaults to 1% of payment + 50 sats"
141+
help = "Maximum total routing fee, e.g. 50sat or 50000msat. Defaults to 1% of payment + 50 sats"
140142
)]
141-
max_total_routing_fee_msat: Option<u64>,
143+
max_total_routing_fee: Option<Amount>,
142144
#[arg(long, help = "Maximum total CLTV delta we accept for the route (default: 1008)")]
143145
max_total_cltv_expiry_delta: Option<u32>,
144146
#[arg(
@@ -155,9 +157,9 @@ enum Commands {
155157
#[command(about = "Return a BOLT12 offer for receiving payments")]
156158
Bolt12Receive {
157159
#[arg(
158-
help = "Amount in millisatoshis to request. If unset, a variable-amount offer is returned"
160+
help = "Amount to request, e.g. 50sat or 50000msat. If unset, a variable-amount offer is returned"
159161
)]
160-
amount_msat: Option<u64>,
162+
amount: Option<Amount>,
161163
#[arg(help = "Description to attach along with the offer")]
162164
description: String,
163165
#[arg(long, help = "Offer expiry time in seconds")]
@@ -169,8 +171,10 @@ enum Commands {
169171
Bolt12Send {
170172
#[arg(help = "A BOLT12 offer for a payment within the Lightning Network")]
171173
offer: String,
172-
#[arg(help = "Amount in millisatoshis. Required when paying a zero-amount offer")]
173-
amount_msat: Option<u64>,
174+
#[arg(
175+
help = "Amount to send, e.g. 50sat or 50000msat. Required when paying a zero-amount offer"
176+
)]
177+
amount: Option<Amount>,
174178
#[arg(short, long, help = "Number of items requested")]
175179
quantity: Option<u64>,
176180
#[arg(
@@ -181,9 +185,9 @@ enum Commands {
181185
payer_note: Option<String>,
182186
#[arg(
183187
long,
184-
help = "Maximum total fees, in millisatoshi, that may accrue during route finding, Defaults to 1% of the payment amount + 50 sats"
188+
help = "Maximum total routing fee, e.g. 50sat or 50000msat. Defaults to 1% of the payment amount + 50 sats"
185189
)]
186-
max_total_routing_fee_msat: Option<u64>,
190+
max_total_routing_fee: Option<Amount>,
187191
#[arg(long, help = "Maximum total CLTV delta we accept for the route (default: 1008)")]
188192
max_total_cltv_expiry_delta: Option<u32>,
189193
#[arg(
@@ -201,13 +205,13 @@ enum Commands {
201205
SpontaneousSend {
202206
#[arg(help = "The hex-encoded public key of the node to send the payment to")]
203207
node_id: String,
204-
#[arg(help = "The amount in millisatoshis to send")]
205-
amount_msat: u64,
208+
#[arg(help = "The amount to send, e.g. 50sat or 50000msat")]
209+
amount: Amount,
206210
#[arg(
207211
long,
208-
help = "Maximum total fees in millisatoshis that may accrue during route finding. Defaults to 1% of payment + 50 sats"
212+
help = "Maximum total routing fee, e.g. 50sat or 50000msat. Defaults to 1% of payment + 50 sats"
209213
)]
210-
max_total_routing_fee_msat: Option<u64>,
214+
max_total_routing_fee: Option<Amount>,
211215
#[arg(long, help = "Maximum total CLTV delta we accept for the route (default: 1008)")]
212216
max_total_cltv_expiry_delta: Option<u32>,
213217
#[arg(
@@ -245,13 +249,10 @@ enum Commands {
245249
help = "Address to connect to remote peer (IPv4:port, IPv6:port, OnionV3:port, or hostname:port)"
246250
)]
247251
address: String,
248-
#[arg(help = "The amount of satoshis to commit to the channel")]
249-
channel_amount_sats: u64,
250-
#[arg(
251-
long,
252-
help = "Amount of satoshis to push to the remote side as part of the initial commitment state"
253-
)]
254-
push_to_counterparty_msat: Option<u64>,
252+
#[arg(help = "The amount to commit to the channel, e.g. 100sat or 100000msat")]
253+
channel_amount: Amount,
254+
#[arg(long, help = "Amount to push to the remote side, e.g. 50sat or 50000msat")]
255+
push_to_counterparty: Option<Amount>,
255256
#[arg(long, help = "Whether the channel should be public")]
256257
announce_channel: bool,
257258
// Channel config options
@@ -279,17 +280,17 @@ enum Commands {
279280
user_channel_id: String,
280281
#[arg(help = "The hex-encoded public key of the channel's counterparty node")]
281282
counterparty_node_id: String,
282-
#[arg(help = "The amount of sats to splice into the channel")]
283-
splice_amount_sats: u64,
283+
#[arg(help = "The amount to splice into the channel, e.g. 50sat or 50000msat")]
284+
splice_amount: Amount,
284285
},
285286
#[command(about = "Decrease the channel balance by the given amount")]
286287
SpliceOut {
287288
#[arg(help = "The local user_channel_id of this channel")]
288289
user_channel_id: String,
289290
#[arg(help = "The hex-encoded public key of the channel's counterparty node")]
290291
counterparty_node_id: String,
291-
#[arg(help = "The amount of sats to splice out of the channel")]
292-
splice_amount_sats: u64,
292+
#[arg(help = "The amount to splice out of the channel, e.g. 50sat or 50000msat")]
293+
splice_amount: Amount,
293294
#[arg(
294295
short,
295296
long,
@@ -464,7 +465,8 @@ async fn main() {
464465
client.onchain_receive(OnchainReceiveRequest {}).await,
465466
);
466467
},
467-
Commands::OnchainSend { address, amount_sats, send_all, fee_rate_sat_per_vb } => {
468+
Commands::OnchainSend { address, amount, send_all, fee_rate_sat_per_vb } => {
469+
let amount_sats = amount.map(|a| a.to_sat().unwrap_or_else(|e| handle_error_msg(&e)));
468470
handle_response_result::<_, OnchainSendResponse>(
469471
client
470472
.onchain_send(OnchainSendRequest {
@@ -476,7 +478,8 @@ async fn main() {
476478
.await,
477479
);
478480
},
479-
Commands::Bolt11Receive { description, description_hash, expiry_secs, amount_msat } => {
481+
Commands::Bolt11Receive { description, description_hash, expiry_secs, amount } => {
482+
let amount_msat = amount.map(|a| a.to_msat());
480483
let invoice_description = match (description, description_hash) {
481484
(Some(desc), None) => Some(Bolt11InvoiceDescription {
482485
kind: Some(bolt11_invoice_description::Kind::Direct(desc)),
@@ -503,12 +506,14 @@ async fn main() {
503506
},
504507
Commands::Bolt11Send {
505508
invoice,
506-
amount_msat,
507-
max_total_routing_fee_msat,
509+
amount,
510+
max_total_routing_fee,
508511
max_total_cltv_expiry_delta,
509512
max_path_count,
510513
max_channel_saturation_power_of_half,
511514
} => {
515+
let amount_msat = amount.map(|a| a.to_msat());
516+
let max_total_routing_fee_msat = max_total_routing_fee.map(|a| a.to_msat());
512517
let route_parameters = RouteParametersConfig {
513518
max_total_routing_fee_msat,
514519
max_total_cltv_expiry_delta: max_total_cltv_expiry_delta
@@ -527,7 +532,8 @@ async fn main() {
527532
.await,
528533
);
529534
},
530-
Commands::Bolt12Receive { description, amount_msat, expiry_secs, quantity } => {
535+
Commands::Bolt12Receive { description, amount, expiry_secs, quantity } => {
536+
let amount_msat = amount.map(|a| a.to_msat());
531537
handle_response_result::<_, Bolt12ReceiveResponse>(
532538
client
533539
.bolt12_receive(Bolt12ReceiveRequest {
@@ -541,14 +547,16 @@ async fn main() {
541547
},
542548
Commands::Bolt12Send {
543549
offer,
544-
amount_msat,
550+
amount,
545551
quantity,
546552
payer_note,
547-
max_total_routing_fee_msat,
553+
max_total_routing_fee,
548554
max_total_cltv_expiry_delta,
549555
max_path_count,
550556
max_channel_saturation_power_of_half,
551557
} => {
558+
let amount_msat = amount.map(|a| a.to_msat());
559+
let max_total_routing_fee_msat = max_total_routing_fee.map(|a| a.to_msat());
552560
let route_parameters = RouteParametersConfig {
553561
max_total_routing_fee_msat,
554562
max_total_cltv_expiry_delta: max_total_cltv_expiry_delta
@@ -572,12 +580,14 @@ async fn main() {
572580
},
573581
Commands::SpontaneousSend {
574582
node_id,
575-
amount_msat,
576-
max_total_routing_fee_msat,
583+
amount,
584+
max_total_routing_fee,
577585
max_total_cltv_expiry_delta,
578586
max_path_count,
579587
max_channel_saturation_power_of_half,
580588
} => {
589+
let amount_msat = amount.to_msat();
590+
let max_total_routing_fee_msat = max_total_routing_fee.map(|a| a.to_msat());
581591
let route_parameters = RouteParametersConfig {
582592
max_total_routing_fee_msat,
583593
max_total_cltv_expiry_delta: max_total_cltv_expiry_delta
@@ -622,13 +632,16 @@ async fn main() {
622632
Commands::OpenChannel {
623633
node_pubkey,
624634
address,
625-
channel_amount_sats,
626-
push_to_counterparty_msat,
635+
channel_amount,
636+
push_to_counterparty,
627637
announce_channel,
628638
forwarding_fee_proportional_millionths,
629639
forwarding_fee_base_msat,
630640
cltv_expiry_delta,
631641
} => {
642+
let channel_amount_sats =
643+
channel_amount.to_sat().unwrap_or_else(|e| handle_error_msg(&e));
644+
let push_to_counterparty_msat = push_to_counterparty.map(|a| a.to_msat());
632645
let channel_config = build_open_channel_config(
633646
forwarding_fee_proportional_millionths,
634647
forwarding_fee_base_msat,
@@ -648,7 +661,9 @@ async fn main() {
648661
.await,
649662
);
650663
},
651-
Commands::SpliceIn { user_channel_id, counterparty_node_id, splice_amount_sats } => {
664+
Commands::SpliceIn { user_channel_id, counterparty_node_id, splice_amount } => {
665+
let splice_amount_sats =
666+
splice_amount.to_sat().unwrap_or_else(|e| handle_error_msg(&e));
652667
handle_response_result::<_, SpliceInResponse>(
653668
client
654669
.splice_in(SpliceInRequest {
@@ -659,12 +674,9 @@ async fn main() {
659674
.await,
660675
);
661676
},
662-
Commands::SpliceOut {
663-
user_channel_id,
664-
counterparty_node_id,
665-
address,
666-
splice_amount_sats,
667-
} => {
677+
Commands::SpliceOut { user_channel_id, counterparty_node_id, address, splice_amount } => {
678+
let splice_amount_sats =
679+
splice_amount.to_sat().unwrap_or_else(|e| handle_error_msg(&e));
668680
handle_response_result::<_, SpliceOutResponse>(
669681
client
670682
.splice_out(SpliceOutRequest {
@@ -875,6 +887,11 @@ fn parse_page_token(token_str: &str) -> Result<PageToken, LdkServerError> {
875887
Ok(PageToken { token: parts[0].to_string(), index })
876888
}
877889

890+
fn handle_error_msg(msg: &str) -> ! {
891+
eprintln!("Error: {msg}");
892+
std::process::exit(1);
893+
}
894+
878895
fn handle_error(e: LdkServerError) -> ! {
879896
let error_type = match e.error_code {
880897
InvalidRequestError => "Invalid Request",

0 commit comments

Comments
 (0)