@@ -33,12 +33,10 @@ pub use lightning::util::string::UntrustedString;
3333
3434pub use lightning_types:: payment:: { PaymentHash , PaymentPreimage , PaymentSecret } ;
3535
36- pub use lightning_invoice:: { Bolt11Invoice , Description } ;
36+ pub use lightning_invoice:: { Description , SignedRawBolt11Invoice } ;
3737
3838pub use lightning_liquidity:: lsps1:: msgs:: ChannelInfo as ChannelOrderInfo ;
39- pub use lightning_liquidity:: lsps1:: msgs:: {
40- Bolt11PaymentInfo , OrderId , OrderParameters , PaymentState ,
41- } ;
39+ pub use lightning_liquidity:: lsps1:: msgs:: { OrderId , OrderParameters , PaymentState } ;
4240
4341pub use bitcoin:: { Address , BlockHash , FeeRate , Network , OutPoint , Txid } ;
4442
@@ -60,10 +58,12 @@ use bitcoin::hashes::Hash;
6058use bitcoin:: secp256k1:: PublicKey ;
6159use lightning:: ln:: channelmanager:: PaymentId ;
6260use lightning:: util:: ser:: Writeable ;
63- use lightning_invoice:: SignedRawBolt11Invoice ;
61+ use lightning_invoice:: { Bolt11Invoice as LdkBolt11Invoice , Bolt11InvoiceDescriptionRef } ;
6462
6563use std:: convert:: TryInto ;
6664use std:: str:: FromStr ;
65+ use std:: sync:: Arc ;
66+ use std:: time:: { Duration , UNIX_EPOCH } ;
6767
6868impl UniffiCustomTypeConverter for PublicKey {
6969 type Builtin = String ;
@@ -113,24 +113,6 @@ impl UniffiCustomTypeConverter for Address {
113113 }
114114}
115115
116- impl UniffiCustomTypeConverter for Bolt11Invoice {
117- type Builtin = String ;
118-
119- fn into_custom ( val : Self :: Builtin ) -> uniffi:: Result < Self > {
120- if let Ok ( signed) = val. parse :: < SignedRawBolt11Invoice > ( ) {
121- if let Ok ( invoice) = Bolt11Invoice :: from_signed ( signed) {
122- return Ok ( invoice) ;
123- }
124- }
125-
126- Err ( Error :: InvalidInvoice . into ( ) )
127- }
128-
129- fn from_custom ( obj : Self ) -> Self :: Builtin {
130- obj. to_string ( )
131- }
132- }
133-
134116impl UniffiCustomTypeConverter for Offer {
135117 type Builtin = String ;
136118
@@ -405,6 +387,211 @@ impl From<lightning_invoice::Bolt11InvoiceDescription> for Bolt11InvoiceDescript
405387 }
406388}
407389
390+ impl < ' a > From < Bolt11InvoiceDescriptionRef < ' a > > for Bolt11InvoiceDescription {
391+ fn from ( value : Bolt11InvoiceDescriptionRef < ' a > ) -> Self {
392+ match value {
393+ lightning_invoice:: Bolt11InvoiceDescriptionRef :: Direct ( description) => {
394+ Bolt11InvoiceDescription :: Direct { description : description. to_string ( ) }
395+ } ,
396+ lightning_invoice:: Bolt11InvoiceDescriptionRef :: Hash ( hash) => {
397+ Bolt11InvoiceDescription :: Hash { hash : hex_utils:: to_string ( hash. 0 . as_ref ( ) ) }
398+ } ,
399+ }
400+ }
401+ }
402+
403+ #[ derive( Debug , Clone , Hash , Eq , PartialEq , Ord , PartialOrd ) ]
404+ pub enum Currency {
405+ Bitcoin ,
406+ BitcoinTestnet ,
407+ Regtest ,
408+ Simnet ,
409+ Signet ,
410+ }
411+
412+ impl From < lightning_invoice:: Currency > for Currency {
413+ fn from ( currency : lightning_invoice:: Currency ) -> Self {
414+ match currency {
415+ lightning_invoice:: Currency :: Bitcoin => Currency :: Bitcoin ,
416+ lightning_invoice:: Currency :: BitcoinTestnet => Currency :: BitcoinTestnet ,
417+ lightning_invoice:: Currency :: Regtest => Currency :: Regtest ,
418+ lightning_invoice:: Currency :: Simnet => Currency :: Simnet ,
419+ lightning_invoice:: Currency :: Signet => Currency :: Signet ,
420+ }
421+ }
422+ }
423+
424+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
425+ pub struct RouteHintHop {
426+ pub src_node_id : PublicKey ,
427+ pub short_channel_id : u64 ,
428+ pub cltv_expiry_delta : u16 ,
429+ pub htlc_minimum_msat : Option < u64 > ,
430+ pub htlc_maximum_msat : Option < u64 > ,
431+ pub fees : RoutingFees ,
432+ }
433+
434+ impl From < lightning:: routing:: router:: RouteHintHop > for RouteHintHop {
435+ fn from ( hop : lightning:: routing:: router:: RouteHintHop ) -> Self {
436+ Self {
437+ src_node_id : hop. src_node_id ,
438+ short_channel_id : hop. short_channel_id ,
439+ cltv_expiry_delta : hop. cltv_expiry_delta ,
440+ htlc_minimum_msat : hop. htlc_minimum_msat ,
441+ htlc_maximum_msat : hop. htlc_maximum_msat ,
442+ fees : hop. fees ,
443+ }
444+ }
445+ }
446+
447+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
448+ pub struct Bolt11Invoice {
449+ pub inner : LdkBolt11Invoice ,
450+ }
451+
452+ impl Bolt11Invoice {
453+ pub fn from_str ( invoice_str : & str ) -> Result < Self , Error > {
454+ invoice_str. parse ( )
455+ }
456+
457+ pub fn signable_hash ( & self ) -> Vec < u8 > {
458+ self . inner . signable_hash ( ) . to_vec ( )
459+ }
460+
461+ pub fn expiry_time_seconds ( & self ) -> u64 {
462+ self . inner . expiry_time ( ) . as_secs ( )
463+ }
464+
465+ pub fn min_final_cltv_expiry_delta ( & self ) -> u64 {
466+ self . inner . min_final_cltv_expiry_delta ( )
467+ }
468+
469+ pub fn amount_milli_satoshis ( & self ) -> Option < u64 > {
470+ self . inner . amount_milli_satoshis ( )
471+ }
472+
473+ pub fn is_expired ( & self ) -> bool {
474+ self . inner . is_expired ( )
475+ }
476+
477+ pub fn seconds_since_epoch ( & self ) -> u64 {
478+ self . inner . duration_since_epoch ( ) . as_secs ( )
479+ }
480+
481+ pub fn would_expire ( & self , at_time_seconds : u64 ) -> bool {
482+ self . inner . would_expire ( Duration :: from_secs ( at_time_seconds) )
483+ }
484+
485+ pub fn seconds_until_expiry ( & self ) -> u64 {
486+ self . inner . duration_until_expiry ( ) . as_secs ( )
487+ }
488+
489+ pub fn payment_hash ( & self ) -> PaymentHash {
490+ PaymentHash ( self . inner . payment_hash ( ) . to_byte_array ( ) )
491+ }
492+
493+ pub fn timestamp_seconds ( & self ) -> u64 {
494+ self . inner . timestamp ( ) . duration_since ( UNIX_EPOCH ) . unwrap ( ) . as_secs ( )
495+ }
496+
497+ pub fn currency ( & self ) -> Currency {
498+ self . inner . currency ( ) . into ( )
499+ }
500+
501+ pub fn payment_secret ( & self ) -> PaymentSecret {
502+ PaymentSecret ( self . inner . payment_secret ( ) . 0 )
503+ }
504+
505+ pub fn description ( & self ) -> Bolt11InvoiceDescription {
506+ self . inner . description ( ) . into ( )
507+ }
508+
509+ pub fn fallback_addresses ( & self ) -> Vec < Address > {
510+ self . inner . fallback_addresses ( )
511+ }
512+
513+ pub fn route_hints ( & self ) -> Vec < Vec < RouteHintHop > > {
514+ self . inner
515+ . route_hints ( )
516+ . iter ( )
517+ . map ( |route| {
518+ route. 0 . iter ( )
519+ . map ( |hop| RouteHintHop :: from ( hop. clone ( ) ) )
520+ . collect ( )
521+ } )
522+ . collect ( )
523+ }
524+
525+ pub fn features ( & self ) -> Option < Vec < u8 > > {
526+ self . inner . features ( ) . map ( |f| f. encode ( ) )
527+ }
528+
529+ pub fn recover_payee_pub_key ( & self ) -> PublicKey {
530+ self . inner . recover_payee_pub_key ( )
531+ }
532+
533+ pub fn network ( & self ) -> Network {
534+ self . inner . network ( )
535+ }
536+
537+ pub fn into_inner ( self ) -> lightning_invoice:: Bolt11Invoice {
538+ self . inner
539+ }
540+ }
541+
542+ impl std:: str:: FromStr for Bolt11Invoice {
543+ type Err = Error ;
544+
545+ fn from_str ( invoice_str : & str ) -> Result < Self , Self :: Err > {
546+ match invoice_str. parse :: < SignedRawBolt11Invoice > ( ) {
547+ Ok ( signed) => match LdkBolt11Invoice :: from_signed ( signed) {
548+ Ok ( invoice) => Ok ( Bolt11Invoice { inner : invoice } ) ,
549+ Err ( _) => Err ( Error :: InvalidInvoice ) ,
550+ } ,
551+ Err ( _) => Err ( Error :: InvalidInvoice ) ,
552+ }
553+ }
554+ }
555+
556+ impl std:: fmt:: Display for Bolt11Invoice {
557+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
558+ write ! ( f, "{}" , self . inner)
559+ }
560+ }
561+
562+ impl From < LdkBolt11Invoice > for Bolt11Invoice {
563+ fn from ( invoice : LdkBolt11Invoice ) -> Self {
564+ Bolt11Invoice { inner : invoice }
565+ }
566+ }
567+
568+ impl From < Bolt11Invoice > for LdkBolt11Invoice {
569+ fn from ( wrapper : Bolt11Invoice ) -> Self {
570+ wrapper. into_inner ( )
571+ }
572+ }
573+
574+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
575+ pub struct Bolt11PaymentInfo {
576+ pub state : PaymentState ,
577+ pub expires_at : chrono:: DateTime < chrono:: Utc > ,
578+ pub fee_total_sat : u64 ,
579+ pub order_total_sat : u64 ,
580+ pub invoice : Arc < Bolt11Invoice > ,
581+ }
582+
583+ impl From < lightning_liquidity:: lsps1:: msgs:: Bolt11PaymentInfo > for Bolt11PaymentInfo {
584+ fn from ( info : lightning_liquidity:: lsps1:: msgs:: Bolt11PaymentInfo ) -> Self {
585+ Self {
586+ state : info. state ,
587+ expires_at : info. expires_at ,
588+ fee_total_sat : info. fee_total_sat ,
589+ order_total_sat : info. order_total_sat ,
590+ invoice : Arc :: new ( info. invoice . into ( ) ) ,
591+ }
592+ }
593+ }
594+
408595impl UniffiCustomTypeConverter for OrderId {
409596 type Builtin = String ;
410597
@@ -441,4 +628,67 @@ mod tests {
441628 let reconverted_description: Bolt11InvoiceDescription = converted_description. into ( ) ;
442629 assert_eq ! ( description, reconverted_description) ;
443630 }
631+
632+ #[ test]
633+ fn test_bolt11_invoice ( ) {
634+ let invoice_string = "lnbc1pn8g249pp5f6ytj32ty90jhvw69enf30hwfgdhyymjewywcmfjevflg6s4z86qdqqcqzzgxqyz5vqrzjqwnvuc0u4txn35cafc7w94gxvq5p3cu9dd95f7hlrh0fvs46wpvhdfjjzh2j9f7ye5qqqqryqqqqthqqpysp5mm832athgcal3m7h35sc29j63lmgzvwc5smfjh2es65elc2ns7dq9qrsgqu2xcje2gsnjp0wn97aknyd3h58an7sjj6nhcrm40846jxphv47958c6th76whmec8ttr2wmg6sxwchvxmsc00kqrzqcga6lvsf9jtqgqy5yexa" ;
635+ let ldk_invoice: LdkBolt11Invoice = invoice_string. parse ( ) . unwrap ( ) ;
636+
637+ let wrapped_invoice = Bolt11Invoice :: from ( ldk_invoice. clone ( ) ) ;
638+
639+ assert_eq ! (
640+ ldk_invoice. payment_hash( ) . to_byte_array( ) . to_vec( ) ,
641+ wrapped_invoice. payment_hash( ) . 0 . to_vec( )
642+ ) ;
643+ assert_eq ! ( ldk_invoice. amount_milli_satoshis( ) , wrapped_invoice. amount_milli_satoshis( ) ) ;
644+ assert_eq ! ( ldk_invoice. expiry_time( ) . as_secs( ) , wrapped_invoice. expiry_time_seconds( ) ) ;
645+ assert_eq ! (
646+ ldk_invoice. min_final_cltv_expiry_delta( ) ,
647+ wrapped_invoice. min_final_cltv_expiry_delta( )
648+ ) ;
649+ assert_eq ! (
650+ ldk_invoice. duration_since_epoch( ) . as_secs( ) ,
651+ wrapped_invoice. seconds_since_epoch( )
652+ ) ;
653+ assert_eq ! (
654+ ldk_invoice. duration_until_expiry( ) . as_secs( ) ,
655+ wrapped_invoice. seconds_until_expiry( )
656+ ) ;
657+
658+ let future_time = Duration :: from_secs ( wrapped_invoice. seconds_since_epoch ( ) + 10000 ) ;
659+ assert ! ( !ldk_invoice. would_expire( future_time) ) ;
660+ assert ! ( !wrapped_invoice. would_expire( future_time. as_secs( ) ) ) ;
661+
662+ let invoice_str = wrapped_invoice. to_string ( ) ;
663+ let parsed_invoice: Bolt11Invoice = invoice_str. parse ( ) . unwrap ( ) ;
664+ assert_eq ! (
665+ wrapped_invoice. inner. payment_hash( ) . to_byte_array( ) . to_vec( ) ,
666+ parsed_invoice. inner. payment_hash( ) . to_byte_array( ) . to_vec( )
667+ ) ;
668+ }
669+
670+ #[ test]
671+ fn test_bolt11_invoice_roundtrip ( ) {
672+ let invoice_string = "lnbc1pn8g249pp5f6ytj32ty90jhvw69enf30hwfgdhyymjewywcmfjevflg6s4z86qdqqcqzzgxqyz5vqrzjqwnvuc0u4txn35cafc7w94gxvq5p3cu9dd95f7hlrh0fvs46wpvhdfjjzh2j9f7ye5qqqqryqqqqthqqpysp5mm832athgcal3m7h35sc29j63lmgzvwc5smfjh2es65elc2ns7dq9qrsgqu2xcje2gsnjp0wn97aknyd3h58an7sjj6nhcrm40846jxphv47958c6th76whmec8ttr2wmg6sxwchvxmsc00kqrzqcga6lvsf9jtqgqy5yexa" ;
673+ let original_invoice: LdkBolt11Invoice = invoice_string. parse ( ) . unwrap ( ) ;
674+ let wrapped_invoice = Bolt11Invoice { inner : original_invoice. clone ( ) } ;
675+ let unwrapped_invoice: LdkBolt11Invoice = wrapped_invoice. clone ( ) . into ( ) ;
676+
677+ assert_eq ! ( original_invoice. payment_hash( ) , unwrapped_invoice. payment_hash( ) ) ;
678+ assert_eq ! ( original_invoice. payment_secret( ) , unwrapped_invoice. payment_secret( ) ) ;
679+ assert_eq ! (
680+ original_invoice. amount_milli_satoshis( ) ,
681+ unwrapped_invoice. amount_milli_satoshis( )
682+ ) ;
683+ assert_eq ! ( original_invoice. timestamp( ) , unwrapped_invoice. timestamp( ) ) ;
684+ assert_eq ! ( original_invoice. expiry_time( ) , unwrapped_invoice. expiry_time( ) ) ;
685+ assert_eq ! (
686+ original_invoice. min_final_cltv_expiry_delta( ) ,
687+ unwrapped_invoice. min_final_cltv_expiry_delta( )
688+ ) ;
689+
690+ let original_hints = original_invoice. route_hints ( ) ;
691+ let unwrapped_hints = unwrapped_invoice. route_hints ( ) ;
692+ assert_eq ! ( original_hints. len( ) , unwrapped_hints. len( ) ) ;
693+ }
444694}
0 commit comments