@@ -21,6 +21,7 @@ use lightning::util::config::{ChannelHandshakeConfig, ChannelHandshakeLimits, Us
21
21
use lightning:: util:: persist:: KVStore ;
22
22
use lightning:: util:: ser:: { Writeable , Writer } ;
23
23
use lightning_invoice:: payment:: payment_parameters_from_invoice;
24
+ use lightning_invoice:: payment:: payment_parameters_from_zero_amount_invoice;
24
25
use lightning_invoice:: { utils, Bolt11Invoice , Currency } ;
25
26
use lightning_persister:: fs_store:: FilesystemStore ;
26
27
use std:: env;
@@ -162,20 +163,35 @@ pub(crate) fn poll_for_user_input(
162
163
continue ;
163
164
}
164
165
166
+ let mut user_provided_amt: Option < u64 > = None ;
167
+ if let Some ( amt_msat_str) = words. next ( ) {
168
+ match amt_msat_str. parse ( ) {
169
+ Ok ( amt) => user_provided_amt = Some ( amt) ,
170
+ Err ( e) => {
171
+ println ! ( "ERROR: couldn't parse amount_msat: {}" , e) ;
172
+ continue ;
173
+ }
174
+ } ;
175
+ }
176
+
165
177
if let Ok ( offer) = Offer :: from_str ( invoice_str. unwrap ( ) ) {
166
178
let offer_hash = Sha256 :: hash ( invoice_str. unwrap ( ) . as_bytes ( ) ) ;
167
179
let payment_id = PaymentId ( * offer_hash. as_ref ( ) ) ;
168
180
169
- let amt_msat =
170
- match offer. amount ( ) {
171
- Some ( offer:: Amount :: Bitcoin { amount_msats } ) => * amount_msats,
172
- amt => {
173
- println ! ( "ERROR: Cannot process non-Bitcoin-denominated offer value {:?}" , amt) ;
174
- continue ;
175
- }
176
- } ;
181
+ let amt_msat = match ( offer. amount ( ) , user_provided_amt) {
182
+ ( Some ( offer:: Amount :: Bitcoin { amount_msats } ) , _) => * amount_msats,
183
+ ( _, Some ( amt) ) => amt,
184
+ ( amt, _) => {
185
+ println ! ( "ERROR: Cannot process non-Bitcoin-denominated offer value {:?}" , amt) ;
186
+ continue ;
187
+ }
188
+ } ;
189
+ if user_provided_amt. is_some ( ) && user_provided_amt != Some ( amt_msat) {
190
+ println ! ( "Amount didn't match offer of {}msat" , amt_msat) ;
191
+ continue ;
192
+ }
177
193
178
- loop {
194
+ while user_provided_amt . is_none ( ) {
179
195
print ! ( "Paying offer for {} msat. Continue (Y/N)? >" , amt_msat) ;
180
196
io:: stdout ( ) . flush ( ) . unwrap ( ) ;
181
197
@@ -211,8 +227,9 @@ pub(crate) fn poll_for_user_input(
211
227
. unwrap ( ) ;
212
228
213
229
let retry = Retry :: Timeout ( Duration :: from_secs ( 10 ) ) ;
230
+ let amt = Some ( amt_msat) ;
214
231
let pay = channel_manager
215
- . pay_for_offer ( & offer, None , None , None , payment_id, retry, None ) ;
232
+ . pay_for_offer ( & offer, None , amt , None , payment_id, retry, None ) ;
216
233
if pay. is_err ( ) {
217
234
println ! ( "ERROR: Failed to pay: {:?}" , pay) ;
218
235
}
@@ -221,6 +238,7 @@ pub(crate) fn poll_for_user_input(
221
238
Ok ( invoice) => send_payment (
222
239
& channel_manager,
223
240
& invoice,
241
+ user_provided_amt,
224
242
& mut outbound_payments. lock ( ) . unwrap ( ) ,
225
243
Arc :: clone ( & fs_store) ,
226
244
) ,
@@ -563,7 +581,7 @@ fn help() {
563
581
println ! ( " disconnectpeer <peer_pubkey>" ) ;
564
582
println ! ( " listpeers" ) ;
565
583
println ! ( "\n Payments:" ) ;
566
- println ! ( " sendpayment <invoice|offer>" ) ;
584
+ println ! ( " sendpayment <invoice|offer> [<amount_msat>] " ) ;
567
585
println ! ( " keysend <dest_pubkey> <amt_msats>" ) ;
568
586
println ! ( " listpayments" ) ;
569
587
println ! ( "\n Invoices:" ) ;
@@ -770,20 +788,41 @@ fn open_channel(
770
788
}
771
789
772
790
fn send_payment (
773
- channel_manager : & ChannelManager , invoice : & Bolt11Invoice ,
791
+ channel_manager : & ChannelManager , invoice : & Bolt11Invoice , required_amount_msat : Option < u64 > ,
774
792
outbound_payments : & mut OutboundPaymentInfoStorage , fs_store : Arc < FilesystemStore > ,
775
793
) {
776
794
let payment_id = PaymentId ( ( * invoice. payment_hash ( ) ) . to_byte_array ( ) ) ;
777
795
let payment_secret = Some ( * invoice. payment_secret ( ) ) ;
778
- let ( payment_hash, recipient_onion, route_params) =
779
- match payment_parameters_from_invoice ( invoice) {
780
- Ok ( res) => res,
781
- Err ( e) => {
782
- println ! ( "Failed to parse invoice" ) ;
783
- print ! ( "> " ) ;
784
- return ;
785
- }
786
- } ;
796
+ let zero_amt_invoice =
797
+ invoice. amount_milli_satoshis ( ) . is_none ( ) || invoice. amount_milli_satoshis ( ) == Some ( 0 ) ;
798
+ let pay_params_opt = if zero_amt_invoice {
799
+ if let Some ( amt_msat) = required_amount_msat {
800
+ payment_parameters_from_zero_amount_invoice ( invoice, amt_msat)
801
+ } else {
802
+ println ! ( "Need an amount for the given 0-value invoice" ) ;
803
+ print ! ( "> " ) ;
804
+ return ;
805
+ }
806
+ } else {
807
+ if required_amount_msat. is_some ( ) && invoice. amount_milli_satoshis ( ) != required_amount_msat
808
+ {
809
+ println ! (
810
+ "Amount didn't match invoice value of {}msat" ,
811
+ invoice. amount_milli_satoshis( ) . unwrap_or( 0 )
812
+ ) ;
813
+ print ! ( "> " ) ;
814
+ return ;
815
+ }
816
+ payment_parameters_from_invoice ( invoice)
817
+ } ;
818
+ let ( payment_hash, recipient_onion, route_params) = match pay_params_opt {
819
+ Ok ( res) => res,
820
+ Err ( e) => {
821
+ println ! ( "Failed to parse invoice" ) ;
822
+ print ! ( "> " ) ;
823
+ return ;
824
+ }
825
+ } ;
787
826
outbound_payments. payments . insert (
788
827
payment_id,
789
828
PaymentInfo {
0 commit comments