@@ -174,10 +174,10 @@ def setup_node_with_tier_store(tmp_dir, esplora_endpoint, listening_addresses) -
174174 builder .set_chain_source_esplora (esplora_endpoint , None )
175175 builder .set_network (DEFAULT_TEST_NETWORK )
176176 builder .set_listening_addresses (listening_addresses )
177- builder .set_backup_store (FfiDynStore . from_store ( backup ) )
178- builder .set_ephemeral_store (FfiDynStore . from_store ( ephemeral ) )
177+ builder .set_backup_store (backup )
178+ builder .set_ephemeral_store (ephemeral )
179179
180- return builder .build_with_store (node_entropy , FfiDynStore . from_store ( primary ) ), (primary , backup , ephemeral )
180+ return builder .build_with_store (node_entropy , primary ), (primary , backup , ephemeral )
181181
182182def do_channel_full_cycle (setup_1 : NodeSetup , setup_2 : NodeSetup , esplora_endpoint ):
183183 # Fund both nodes
@@ -269,6 +269,32 @@ def expect_event(node, expected_event_type):
269269 node .event_handled ()
270270 return event
271271
272+ def wait_until (predicate , timeout = 10.0 , interval = 0.1 , message = "condition not met before timeout" ):
273+ deadline = time .time () + timeout
274+ while time .time () < deadline :
275+ if predicate ():
276+ return
277+ time .sleep (interval )
278+ raise AssertionError (message )
279+
280+
281+ def stores_match (store_a , store_b ):
282+ if set (store_a .storage .keys ()) != set (store_b .storage .keys ()):
283+ return False
284+
285+ for namespace_key in store_a .storage :
286+ keys_a = store_a .storage [namespace_key ]
287+ keys_b = store_b .storage [namespace_key ]
288+
289+ if set (keys_a .keys ()) != set (keys_b .keys ()):
290+ return False
291+
292+ for key in keys_a :
293+ if keys_a [key ] != keys_b [key ]:
294+ return False
295+
296+ return True
297+
272298class TestLdkNode (unittest .TestCase ):
273299 def setUp (self ):
274300 bitcoin_cli ("createwallet ldk_node_test" )
@@ -287,40 +313,67 @@ def test_channel_full_cycle(self):
287313 setup_2 .cleanup ()
288314
289315 def test_tier_store (self ):
290- # Set event loop for async Python callbacks from Rust
316+ # Set an event loop for async Python callbacks invoked from Rust.
291317 # (https://mozilla.github.io/uniffi-rs/0.27/futures.html#python-uniffi_set_event_loop)
292318 loop = asyncio .new_event_loop ()
293-
319+
294320 def run_loop ():
295321 asyncio .set_event_loop (loop )
296322 loop .run_forever ()
297-
323+
298324 loop_thread = threading .Thread (target = run_loop , daemon = True )
299325 loop_thread .start ()
300326 ldk_node .uniffi_set_event_loop (loop )
301-
327+
302328 esplora_endpoint = get_esplora_endpoint ()
303- setup_1 , setup_2 = setup_two_nodes (esplora_endpoint , port_1 = 2325 , port_2 = 2326 , use_tier_store = True )
304-
305- do_channel_full_cycle (setup_1 , setup_2 , esplora_endpoint )
306-
307- primary , backup , ephemeral = setup_1 .stores
308-
309- # Wait for async backup
310- time .sleep (2 )
311-
312- self .assertGreater (len (primary .storage ), 0 , "Primary should have data" )
313- self .assertGreater (len (backup .storage ), 0 , "Backup should have data" )
314- self .assertEqual (list (primary .storage .keys ()), list (backup .storage .keys ()),
315- "Backup should mirror primary" )
316-
317- self .assertGreater (len (ephemeral .storage ), 0 , "Ephemeral should have data" )
318- ephemeral_keys = [key for namespace in ephemeral .storage .values () for key in namespace .keys ()]
319- has_scorer_or_graph = any (key in ['scorer' , 'network_graph' ] for key in ephemeral_keys )
320- self .assertTrue (has_scorer_or_graph , "Ephemeral should contain scorer or network_graph data" )
321-
322- setup_1 .cleanup ()
323- setup_2 .cleanup ()
329+ setup_1 , setup_2 = setup_two_nodes (
330+ esplora_endpoint ,
331+ port_1 = 2325 ,
332+ port_2 = 2326 ,
333+ use_tier_store = True ,
334+ )
335+
336+ try :
337+ do_channel_full_cycle (setup_1 , setup_2 , esplora_endpoint )
338+
339+ primary , backup , ephemeral = setup_1 .stores
340+
341+ wait_until (
342+ lambda : len (primary .storage ) > 0 ,
343+ message = "primary store did not receive any data" ,
344+ )
345+ wait_until (
346+ lambda : len (backup .storage ) > 0 ,
347+ message = "backup store did not receive any data" ,
348+ )
349+ wait_until (
350+ lambda : stores_match (primary , backup ),
351+ timeout = 15.0 ,
352+ message = "backup store did not converge to primary contents" ,
353+ )
354+
355+ self .assertTrue (stores_match (primary , backup ), "backup should mirror primary contents" )
356+
357+ self .assertGreater (len (ephemeral .storage ), 0 , "ephemeral should have data" )
358+
359+ ephemeral_keys = [
360+ key
361+ for namespace in ephemeral .storage .values ()
362+ for key in namespace .keys ()
363+ ]
364+ has_scorer_or_graph = any (
365+ key in ["scorer" , "network_graph" ] for key in ephemeral_keys
366+ )
367+ self .assertTrue (
368+ has_scorer_or_graph ,
369+ "ephemeral should contain scorer or network_graph data" ,
370+ )
371+ finally :
372+ setup_1 .cleanup ()
373+ setup_2 .cleanup ()
374+ loop .call_soon_threadsafe (loop .stop )
375+ loop_thread .join (timeout = 5 )
376+ loop .close ()
324377
325378if __name__ == '__main__' :
326379 unittest .main ()
0 commit comments