@@ -21,6 +21,7 @@ use lightning::util::config::{ChannelHandshakeConfig, ChannelHandshakeLimits, Us
2121use lightning:: util:: persist:: KVStore ;
2222use lightning:: util:: ser:: { Writeable , Writer } ;
2323use lightning_invoice:: payment:: payment_parameters_from_invoice;
24+ use lightning_invoice:: payment:: payment_parameters_from_zero_amount_invoice;
2425use lightning_invoice:: { utils, Bolt11Invoice , Currency } ;
2526use lightning_persister:: fs_store:: FilesystemStore ;
2627use std:: env;
@@ -162,20 +163,35 @@ pub(crate) fn poll_for_user_input(
162163 continue ;
163164 }
164165
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+
165177 if let Ok ( offer) = Offer :: from_str ( invoice_str. unwrap ( ) ) {
166178 let offer_hash = Sha256 :: hash ( invoice_str. unwrap ( ) . as_bytes ( ) ) ;
167179 let payment_id = PaymentId ( * offer_hash. as_ref ( ) ) ;
168180
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+ }
177193
178- loop {
194+ while user_provided_amt . is_none ( ) {
179195 print ! ( "Paying offer for {} msat. Continue (Y/N)? >" , amt_msat) ;
180196 io:: stdout ( ) . flush ( ) . unwrap ( ) ;
181197
@@ -211,8 +227,9 @@ pub(crate) fn poll_for_user_input(
211227 . unwrap ( ) ;
212228
213229 let retry = Retry :: Timeout ( Duration :: from_secs ( 10 ) ) ;
230+ let amt = Some ( amt_msat) ;
214231 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 ) ;
216233 if pay. is_err ( ) {
217234 println ! ( "ERROR: Failed to pay: {:?}" , pay) ;
218235 }
@@ -221,6 +238,7 @@ pub(crate) fn poll_for_user_input(
221238 Ok ( invoice) => send_payment (
222239 & channel_manager,
223240 & invoice,
241+ user_provided_amt,
224242 & mut outbound_payments. lock ( ) . unwrap ( ) ,
225243 Arc :: clone ( & fs_store) ,
226244 ) ,
@@ -563,7 +581,7 @@ fn help() {
563581 println ! ( " disconnectpeer <peer_pubkey>" ) ;
564582 println ! ( " listpeers" ) ;
565583 println ! ( "\n Payments:" ) ;
566- println ! ( " sendpayment <invoice|offer>" ) ;
584+ println ! ( " sendpayment <invoice|offer> [<amount_msat>] " ) ;
567585 println ! ( " keysend <dest_pubkey> <amt_msats>" ) ;
568586 println ! ( " listpayments" ) ;
569587 println ! ( "\n Invoices:" ) ;
@@ -770,20 +788,41 @@ fn open_channel(
770788}
771789
772790fn send_payment (
773- channel_manager : & ChannelManager , invoice : & Bolt11Invoice ,
791+ channel_manager : & ChannelManager , invoice : & Bolt11Invoice , required_amount_msat : Option < u64 > ,
774792 outbound_payments : & mut OutboundPaymentInfoStorage , fs_store : Arc < FilesystemStore > ,
775793) {
776794 let payment_id = PaymentId ( ( * invoice. payment_hash ( ) ) . to_byte_array ( ) ) ;
777795 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+ } ;
787826 outbound_payments. payments . insert (
788827 payment_id,
789828 PaymentInfo {
0 commit comments