@@ -13,6 +13,7 @@ use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
13
13
use lightning:: ln:: channelmanager:: { ChannelDetails , ChannelManager , PaymentId , PaymentSendFailure , MIN_FINAL_CLTV_EXPIRY } ;
14
14
#[ cfg( feature = "std" ) ]
15
15
use lightning:: ln:: channelmanager:: { PhantomRouteHints , MIN_CLTV_EXPIRY_DELTA } ;
16
+ use lightning:: ln:: inbound_payment:: { create, create_from_hash, ExpandedKey } ;
16
17
use lightning:: ln:: msgs:: LightningError ;
17
18
use lightning:: routing:: scoring:: Score ;
18
19
use lightning:: routing:: network_graph:: { NetworkGraph , RoutingFees } ;
@@ -38,29 +39,30 @@ use sync::Mutex;
38
39
/// may be too long for QR code scanning. To fix this, `PhantomRouteHints::channels` may be pared
39
40
/// down
40
41
///
41
- /// `payment_hash` and `payment_secret` can come from [`ChannelManager::create_inbound_payment`] or
42
- /// [`ChannelManager::create_inbound_payment_for_hash`]. These values can be retrieved from any
43
- /// participating node. Alternatively, [`inbound_payment::create`] or
44
- /// [`inbound_payment::create_from_hash`] may be used to retrieve these values without a
45
- /// `ChannelManager`.
42
+ /// `payment_hash` can be specified if you have a specific need for a custom payment hash (see the difference
43
+ /// between [`ChannelManager::create_inbound_payment`] and [`ChannelManager::create_inbound_payment_for_hash`]).
44
+ /// This value can be retrieved from any participating node. If `None` is provided for `payment_hash`, then one will be created.
45
+ ///
46
+ /// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
47
+ /// in excess of the current time.
46
48
///
47
49
/// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
48
50
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
49
51
/// requirement).
50
52
///
51
53
/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
52
54
/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
53
- /// [`inbound_payment::create `]: lightning::ln::inbound_payment::create
54
- /// [`inbound_payment::create_from_hash `]: lightning::ln::inbound_payment::create_from_hash
55
+ /// [`ChannelManager::create_inbound_payment `]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment
56
+ /// [`ChannelManager::create_inbound_payment_for_hash `]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
55
57
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
56
58
pub fn create_phantom_invoice < Signer : Sign , K : Deref > (
57
- amt_msat : Option < u64 > , description : String , payment_hash : PaymentHash , payment_secret : PaymentSecret ,
59
+ amt_msat : Option < u64 > , payment_hash : Option < PaymentHash > , description : String , invoice_expiry_delta_secs : u32 ,
58
60
phantom_route_hints : Vec < PhantomRouteHints > , keys_manager : K , network : Currency ,
59
61
) -> Result < Invoice , SignOrCreationError < ( ) > > where K :: Target : KeysInterface {
60
62
let description = Description :: new ( description) . map_err ( SignOrCreationError :: CreationError ) ?;
61
63
let description = InvoiceDescription :: Direct ( & description, ) ;
62
64
_create_phantom_invoice :: < Signer , K > (
63
- amt_msat, description , payment_hash , payment_secret , phantom_route_hints, keys_manager, network,
65
+ amt_msat, payment_hash , description , invoice_expiry_delta_secs , phantom_route_hints, keys_manager, network,
64
66
)
65
67
}
66
68
@@ -80,42 +82,41 @@ pub fn create_phantom_invoice<Signer: Sign, K: Deref>(
80
82
///
81
83
/// `description_hash` is a SHA-256 hash of the description text
82
84
///
83
- /// `payment_hash` and `payment_secret` can come from [`ChannelManager::create_inbound_payment`] or
84
- /// [`ChannelManager::create_inbound_payment_for_hash`]. These values can be retrieved from any
85
- /// participating node. Alternatively, [`inbound_payment::create`] or
86
- /// [`inbound_payment::create_from_hash`] may be used to retrieve these values without a
87
- /// `ChannelManager`.
85
+ /// `payment_hash` can be specified if you have a specific need for a custom payment hash (see the difference
86
+ /// between [`ChannelManager::create_inbound_payment`] and [`ChannelManager::create_inbound_payment_for_hash`]).
87
+ /// This value can be retrieved from any participating node. If `None` is provided for `payment_hash`, then one will be created.
88
+ ///
89
+ /// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
90
+ /// in excess of the current time.
88
91
///
89
92
/// Note that the provided `keys_manager`'s `KeysInterface` implementation must support phantom
90
93
/// invoices in its `sign_invoice` implementation ([`PhantomKeysManager`] satisfies this
91
94
/// requirement).
92
95
///
93
96
/// [`PhantomKeysManager`]: lightning::chain::keysinterface::PhantomKeysManager
94
97
/// [`ChannelManager::get_phantom_route_hints`]: lightning::ln::channelmanager::ChannelManager::get_phantom_route_hints
95
- /// [`inbound_payment::create `]: lightning::ln::inbound_payment::create
96
- /// [`inbound_payment::create_from_hash `]: lightning::ln::inbound_payment::create_from_hash
98
+ /// [`ChannelManager::create_inbound_payment `]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment
99
+ /// [`ChannelManager::create_inbound_payment_for_hash `]: lightning::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
97
100
/// [`PhantomRouteHints::channels`]: lightning::ln::channelmanager::PhantomRouteHints::channels
98
101
pub fn create_phantom_invoice_with_description_hash < Signer : Sign , K : Deref > (
99
- amt_msat : Option < u64 > , description_hash : Sha256 , payment_hash : PaymentHash ,
100
- payment_secret : PaymentSecret , phantom_route_hints : Vec < PhantomRouteHints > ,
101
- keys_manager : K , network : Currency ,
102
+ amt_msat : Option < u64 > , payment_hash : Option < PaymentHash > , invoice_expiry_delta_secs : u32 ,
103
+ description_hash : Sha256 , phantom_route_hints : Vec < PhantomRouteHints > , keys_manager : K , network : Currency ,
102
104
) -> Result < Invoice , SignOrCreationError < ( ) > > where K :: Target : KeysInterface
103
105
{
104
-
105
106
_create_phantom_invoice :: < Signer , K > (
106
- amt_msat,
107
- InvoiceDescription :: Hash ( & description_hash) ,
108
- payment_hash, payment_secret, phantom_route_hints, keys_manager, network,
107
+ amt_msat, payment_hash, InvoiceDescription :: Hash ( & description_hash) ,
108
+ invoice_expiry_delta_secs, phantom_route_hints, keys_manager, network,
109
109
)
110
110
}
111
111
112
112
#[ cfg( feature = "std" ) ]
113
113
fn _create_phantom_invoice < Signer : Sign , K : Deref > (
114
- amt_msat : Option < u64 > , description : InvoiceDescription , payment_hash : PaymentHash ,
115
- payment_secret : PaymentSecret , phantom_route_hints : Vec < PhantomRouteHints > ,
116
- keys_manager : K , network : Currency ,
114
+ amt_msat : Option < u64 > , payment_hash : Option < PaymentHash > , description : InvoiceDescription ,
115
+ invoice_expiry_delta_secs : u32 , phantom_route_hints : Vec < PhantomRouteHints > , keys_manager : K , network : Currency ,
117
116
) -> Result < Invoice , SignOrCreationError < ( ) > > where K :: Target : KeysInterface
118
117
{
118
+ use std:: time:: { SystemTime , UNIX_EPOCH } ;
119
+
119
120
if phantom_route_hints. len ( ) == 0 {
120
121
return Err ( SignOrCreationError :: CreationError (
121
122
CreationError :: MissingRouteHints ,
@@ -128,6 +129,35 @@ fn _create_phantom_invoice<Signer: Sign, K: Deref>(
128
129
InvoiceDescription :: Hash ( hash) => InvoiceBuilder :: new ( network) . description_hash ( hash. 0 ) ,
129
130
} ;
130
131
132
+ // If we ever see performance here being too slow then we should probably take this ExpandedKey as a parameter instead.
133
+ let keys = ExpandedKey :: new ( & keys_manager. get_inbound_payment_key_material ( ) ) ;
134
+ let ( payment_hash, payment_secret) = if let Some ( payment_hash) = payment_hash {
135
+ let payment_secret = create_from_hash (
136
+ & keys,
137
+ amt_msat,
138
+ payment_hash,
139
+ invoice_expiry_delta_secs,
140
+ SystemTime :: now ( )
141
+ . duration_since ( UNIX_EPOCH )
142
+ . expect ( "Time must be > 1970" )
143
+ . as_secs ( ) ,
144
+ )
145
+ . map_err ( |_| SignOrCreationError :: CreationError ( CreationError :: InvalidAmount ) ) ?;
146
+ ( payment_hash, payment_secret)
147
+ } else {
148
+ create (
149
+ & keys,
150
+ amt_msat,
151
+ invoice_expiry_delta_secs,
152
+ & keys_manager,
153
+ SystemTime :: now ( )
154
+ . duration_since ( UNIX_EPOCH )
155
+ . expect ( "Time must be > 1970" )
156
+ . as_secs ( ) ,
157
+ )
158
+ . map_err ( |_| SignOrCreationError :: CreationError ( CreationError :: InvalidAmount ) ) ?
159
+ } ;
160
+
131
161
let mut invoice = invoice
132
162
. current_timestamp ( )
133
163
. payment_hash ( Hash :: from_slice ( & payment_hash. 0 ) . unwrap ( ) )
@@ -755,23 +785,25 @@ mod test {
755
785
nodes[ 2 ] . node . handle_channel_update ( & nodes[ 0 ] . node . get_our_node_id ( ) , & chan_0_2. 0 ) ;
756
786
757
787
let payment_amt = 10_000 ;
758
- let ( payment_preimage, payment_hash, payment_secret) = {
759
- if user_generated_pmt_hash {
760
- let payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
761
- let payment_hash = PaymentHash ( Sha256 :: hash ( & payment_preimage. 0 [ ..] ) . into_inner ( ) ) ;
762
- let payment_secret = nodes[ 1 ] . node . create_inbound_payment_for_hash ( payment_hash, Some ( payment_amt) , 3600 ) . unwrap ( ) ;
763
- ( payment_preimage, payment_hash, payment_secret)
764
- } else {
765
- let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt) , 3600 ) . unwrap ( ) ;
766
- let payment_preimage = nodes[ 1 ] . node . get_payment_preimage ( payment_hash, payment_secret) . unwrap ( ) ;
767
- ( payment_preimage, payment_hash, payment_secret)
768
- }
769
- } ;
770
788
let route_hints = vec ! [
771
789
nodes[ 1 ] . node. get_phantom_route_hints( ) ,
772
790
nodes[ 2 ] . node. get_phantom_route_hints( ) ,
773
791
] ;
774
- let invoice = :: utils:: create_phantom_invoice :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( Some ( payment_amt) , "test" . to_string ( ) , payment_hash, payment_secret, route_hints, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
792
+
793
+ let user_payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
794
+ let payment_hash = if user_generated_pmt_hash {
795
+ Some ( PaymentHash ( Sha256 :: hash ( & user_payment_preimage. 0 [ ..] ) . into_inner ( ) ) )
796
+ } else {
797
+ None
798
+ } ;
799
+
800
+ let invoice = :: utils:: create_phantom_invoice :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( Some ( payment_amt) , payment_hash, "test" . to_string ( ) , 3600 , route_hints, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
801
+ let ( payment_hash, payment_secret) = ( PaymentHash ( invoice. payment_hash ( ) . into_inner ( ) ) , * invoice. payment_secret ( ) ) ;
802
+ let payment_preimage = if user_generated_pmt_hash {
803
+ user_payment_preimage
804
+ } else {
805
+ nodes[ 1 ] . node . get_payment_preimage ( payment_hash, payment_secret) . unwrap ( )
806
+ } ;
775
807
776
808
assert_eq ! ( invoice. min_final_cltv_expiry( ) , MIN_FINAL_CLTV_EXPIRY as u64 ) ;
777
809
assert_eq ! ( invoice. description( ) , InvoiceDescription :: Direct ( & Description ( "test" . to_string( ) ) ) ) ;
@@ -856,14 +888,13 @@ mod test {
856
888
let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
857
889
858
890
let payment_amt = 20_000 ;
859
- let ( payment_hash, payment_secret) = nodes[ 1 ] . node . create_inbound_payment ( Some ( payment_amt) , 3600 ) . unwrap ( ) ;
860
891
let route_hints = vec ! [
861
892
nodes[ 1 ] . node. get_phantom_route_hints( ) ,
862
893
nodes[ 2 ] . node. get_phantom_route_hints( ) ,
863
894
] ;
864
895
865
896
let description_hash = crate :: Sha256 ( Hash :: hash ( "Description hash phantom invoice" . as_bytes ( ) ) ) ;
866
- let invoice = :: utils:: create_phantom_invoice_with_description_hash :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( Some ( payment_amt) , description_hash , payment_hash , payment_secret , route_hints, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
897
+ let invoice = :: utils:: create_phantom_invoice_with_description_hash :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( Some ( payment_amt) , None , 3600 , description_hash , route_hints, & nodes[ 1 ] . keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
867
898
868
899
assert_eq ! ( invoice. amount_pico_btc( ) , Some ( 200_000 ) ) ;
869
900
assert_eq ! ( invoice. min_final_cltv_expiry( ) , MIN_FINAL_CLTV_EXPIRY as u64 ) ;
@@ -1166,15 +1197,14 @@ mod test {
1166
1197
mut chan_ids_to_match : HashSet < u64 > ,
1167
1198
nodes_contains_public_channels : bool
1168
1199
) {
1169
- let ( payment_hash, payment_secret) = invoice_node. node . create_inbound_payment ( invoice_amt, 3600 ) . unwrap ( ) ;
1170
1200
let phantom_route_hints = network_multi_nodes. iter ( )
1171
1201
. map ( |node| node. node . get_phantom_route_hints ( ) )
1172
1202
. collect :: < Vec < PhantomRouteHints > > ( ) ;
1173
1203
let phantom_scids = phantom_route_hints. iter ( )
1174
1204
. map ( |route_hint| route_hint. phantom_scid )
1175
1205
. collect :: < HashSet < u64 > > ( ) ;
1176
1206
1177
- let invoice = :: utils:: create_phantom_invoice :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( invoice_amt, "test" . to_string ( ) , payment_hash , payment_secret , phantom_route_hints, & invoice_node. keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
1207
+ let invoice = :: utils:: create_phantom_invoice :: < EnforcingSigner , & test_utils:: TestKeysInterface > ( invoice_amt, None , "test" . to_string ( ) , 3600 , phantom_route_hints, & invoice_node. keys_manager , Currency :: BitcoinTestnet ) . unwrap ( ) ;
1178
1208
1179
1209
let invoice_hints = invoice. private_routes ( ) ;
1180
1210
0 commit comments