55import os
66import re
77import requests
8+ import socket
89
910from ldk_node import *
1011
@@ -118,8 +119,68 @@ def expect_event(node, expected_event_type):
118119 assert isinstance (event , expected_event_type )
119120 print ("EVENT:" , event )
120121 node .event_handled ()
121- return event
122-
122+ return event
123+
124+ def find_two_free_ports ():
125+ with socket .socket () as s1 , socket .socket () as s2 :
126+ s1 .bind (("127.0.0.1" , 0 ))
127+ s2 .bind (("127.0.0.1" ,0 ))
128+ port_1 = s1 .getsockname ()[1 ]
129+ port_2 = s2 .getsockname ()[1 ]
130+ return port_1 , port_2
131+
132+ def setup_two_nodes (esplora_endpoint ):
133+ port_1 , port_2 = find_two_free_ports ()
134+ tmp_dir_1 = tempfile .TemporaryDirectory ("_ldk_node_1" )
135+ listening_addresses_1 = [f"127.0.0.1:{ port_1 } " ]
136+ node_1 = setup_node (tmp_dir_1 .name , esplora_endpoint , listening_addresses_1 )
137+ node_1 .start ()
138+ node_id_1 = node_1 .node_id ()
139+
140+ tmp_dir_2 = tempfile .TemporaryDirectory ("_ldk_node_2" )
141+ listening_addresses_2 = [f"127.0.0.1:{ port_2 } " ]
142+ node_2 = setup_node (tmp_dir_2 .name , esplora_endpoint , listening_addresses_2 )
143+ node_2 .start ()
144+ node_id_2 = node_2 .node_id ()
145+
146+ return node_1 , node_2 , tmp_dir_1 , tmp_dir_2 , node_id_1 , node_id_2 , listening_addresses_2
147+
148+ def fund_nodes (node_1 , node_2 , esplora_endpoint , amount_sats = 100000 ):
149+ address_1 = node_1 .onchain_payment ().new_address ()
150+ txid_1 = send_to_address (address_1 , amount_sats )
151+ address_2 = node_2 .onchain_payment ().new_address ()
152+ txid_2 = send_to_address (address_2 , amount_sats )
153+
154+ wait_for_tx (esplora_endpoint , txid_1 )
155+ wait_for_tx (esplora_endpoint , txid_2 )
156+ mine_and_wait (esplora_endpoint , 6 )
157+
158+ node_1 .sync_wallets ()
159+ node_2 .sync_wallets ()
160+
161+ def open_channel_and_wait_ready (node_1 , node_2 , node_id_2 , listening_address_2 , esplora_endpoint , channel_amount_sats = 50000 ):
162+ node_1 .open_channel (node_id_2 , listening_address_2 , channel_amount_sats , None , None )
163+
164+ channel_pending_event_1 = expect_event (node_1 , Event .CHANNEL_PENDING )
165+ expect_event (node_2 , Event .CHANNEL_PENDING )
166+
167+ funding_txid = channel_pending_event_1 .funding_txo .txid
168+ wait_for_tx (esplora_endpoint , funding_txid )
169+ mine_and_wait (esplora_endpoint , 6 )
170+
171+ node_1 .sync_wallets ()
172+ node_2 .sync_wallets ()
173+
174+ channel_ready_event_1 = expect_event (node_1 , Event .CHANNEL_READY )
175+ channel_ready_event_2 = expect_event (node_2 , Event .CHANNEL_READY )
176+ return channel_ready_event_1 , channel_ready_event_2 , funding_txid
177+
178+ def stop_and_cleanup (node_1 , node_2 , tmp_dir_1 , tmp_dir_2 ):
179+ node_1 .stop ()
180+ node_2 .stop ()
181+ time .sleep (1 )
182+ tmp_dir_1 .cleanup ()
183+ tmp_dir_2 .cleanup ()
123184
124185def assert_feature_helpers_return_bool (test_case , features ):
125186 feature_methods = [
@@ -153,45 +214,57 @@ def setUp(self):
153214 esplora_endpoint = get_esplora_endpoint ()
154215 mine_and_wait (esplora_endpoint , 1 )
155216
156- def test_channel_full_cycle (self ):
217+ def test_spontaneous_payment (self ):
218+ """Spontaneous payment test in python: keysend after channel ready."""
157219 esplora_endpoint = get_esplora_endpoint ()
158220
159- ## Setup Node 1
160- tmp_dir_1 = tempfile . TemporaryDirectory ( "_ldk_node_1" )
161- print ( "TMP DIR 1:" , tmp_dir_1 . name )
221+ node_1 , node_2 , tmp_dir_1 , tmp_dir_2 , node_id_1 , node_id_2 , listening_addresses_2 = setup_two_nodes ( esplora_endpoint )
222+ fund_nodes ( node_1 , node_2 , esplora_endpoint )
223+ open_channel_and_wait_ready ( node_1 , node_2 , node_id_2 , listening_addresses_2 [ 0 ], esplora_endpoint )
162224
163- listening_addresses_1 = ["127.0.0.1:2323" ]
164- node_1 = setup_node (tmp_dir_1 .name , esplora_endpoint , listening_addresses_1 )
165- node_1 .start ()
166- node_id_1 = node_1 .node_id ()
167- print ("Node ID 1:" , node_id_1 )
225+ keysend_amount_msat = 2_500_000
226+ custom_tlvs = [CustomTlvRecord (type_num = 13377331 , value = bytes ([1 , 2 , 3 ]))]
227+ keysend_payment_id = node_1 .spontaneous_payment ().send_with_custom_tlvs (
228+ keysend_amount_msat , node_id_2 , None , custom_tlvs
229+ )
230+
231+ expect_event (node_1 , Event .PAYMENT_SUCCESSFUL )
232+ received_event = expect_event (node_2 , Event .PAYMENT_RECEIVED )
233+
234+ self .assertEqual (received_event .amount_msat , keysend_amount_msat )
235+ self .assertEqual (received_event .custom_records , custom_tlvs )
168236
169- # Setup Node 2
170- tmp_dir_2 = tempfile .TemporaryDirectory ("_ldk_node_2" )
171- print ("TMP DIR 2:" , tmp_dir_2 .name )
237+ sender_payment = node_1 .payment (keysend_payment_id )
238+ receiver_payment = node_2 .payment (keysend_payment_id )
239+
240+ self .assertIsNotNone (sender_payment )
241+ self .assertIsNotNone (receiver_payment )
242+ self .assertEqual (sender_payment .status , PaymentStatus .SUCCEEDED )
243+ self .assertEqual (sender_payment .direction , PaymentDirection .OUTBOUND )
244+ self .assertEqual (sender_payment .amount_msat , keysend_amount_msat )
245+ self .assertTrue (sender_payment .kind .is_spontaneous ())
246+
247+ self .assertEqual (receiver_payment .status , PaymentStatus .SUCCEEDED )
248+ self .assertEqual (receiver_payment .direction , PaymentDirection .INBOUND )
249+ self .assertEqual (receiver_payment .amount_msat , keysend_amount_msat )
250+ self .assertTrue (receiver_payment .kind .is_spontaneous ())
251+
252+ stop_and_cleanup (node_1 , node_2 , tmp_dir_1 , tmp_dir_2 )
253+
254+ def test_channel_full_cycle (self ):
255+ esplora_endpoint = get_esplora_endpoint ()
172256
173- listening_addresses_2 = ["127.0.0.1:2324" ]
174- node_2 = setup_node (tmp_dir_2 .name , esplora_endpoint , listening_addresses_2 )
175- node_2 .start ()
176- node_id_2 = node_2 .node_id ()
257+ ## Setup two nodes
258+ node_1 , node_2 , tmp_dir_1 , tmp_dir_2 , node_id_1 , node_id_2 , listening_addresses_2 = setup_two_nodes (esplora_endpoint )
259+ print ("Node ID 1:" , node_id_1 )
177260 print ("Node ID 2:" , node_id_2 )
178261
179262 # Check node-announcement features exposed through NodeStatus.
180263 for node in [node_1 , node_2 ]:
181264 node_features_exposed (self , node .status ().node_features )
182265
183- address_1 = node_1 .onchain_payment ().new_address ()
184- txid_1 = send_to_address (address_1 , 100000 )
185- address_2 = node_2 .onchain_payment ().new_address ()
186- txid_2 = send_to_address (address_2 , 100000 )
187-
188- wait_for_tx (esplora_endpoint , txid_1 )
189- wait_for_tx (esplora_endpoint , txid_2 )
190-
191- mine_and_wait (esplora_endpoint , 6 )
266+ fund_nodes (node_1 , node_2 , esplora_endpoint )
192267
193- node_1 .sync_wallets ()
194- node_2 .sync_wallets ()
195268
196269 spendable_balance_1 = node_1 .list_balances ().spendable_onchain_balance_sats
197270 spendable_balance_2 = node_2 .list_balances ().spendable_onchain_balance_sats
@@ -210,22 +283,9 @@ def test_channel_full_cycle(self):
210283 print ("TOTAL 2:" , total_balance_2 )
211284 self .assertEqual (total_balance_2 , 100000 )
212285
213- node_1 .open_channel (node_id_2 , listening_addresses_2 [0 ], 50000 , None , None )
214-
215-
216- channel_pending_event_1 = expect_event (node_1 , Event .CHANNEL_PENDING )
217- channel_pending_event_2 = expect_event (node_2 , Event .CHANNEL_PENDING )
218- funding_txid = channel_pending_event_1 .funding_txo .txid
219- wait_for_tx (esplora_endpoint , funding_txid )
220- mine_and_wait (esplora_endpoint , 6 )
221-
222- node_1 .sync_wallets ()
223- node_2 .sync_wallets ()
224-
225- channel_ready_event_1 = expect_event (node_1 , Event .CHANNEL_READY )
286+ channel_ready_event_1 , channel_ready_event_2 , funding_txid = open_channel_and_wait_ready (node_1 , node_2 , node_id_2 , listening_addresses_2 [0 ], esplora_endpoint )
226287 print ("funding_txo:" , funding_txid )
227288
228- channel_ready_event_2 = expect_event (node_2 , Event .CHANNEL_READY )
229289
230290 # Check negotiated init features exposed through ChannelDetails.
231291 for channel in [node_1 .list_channels ()[0 ], node_2 .list_channels ()[0 ]]:
@@ -259,13 +319,7 @@ def test_channel_full_cycle(self):
259319 self .assertEqual (spendable_balance_after_close_2 , 102500 )
260320
261321 # Stop nodes
262- node_1 .stop ()
263- node_2 .stop ()
264-
265- # Cleanup
266- time .sleep (1 ) # Wait a sec so our logs can finish writing
267- tmp_dir_1 .cleanup ()
268- tmp_dir_2 .cleanup ()
322+ stop_and_cleanup (node_1 , node_2 , tmp_dir_1 , tmp_dir_2 )
269323
270324if __name__ == '__main__' :
271325 unittest .main ()
0 commit comments