@@ -17,14 +17,15 @@ use bitcoin::hashes::Hash;
1717use bitcoin:: { Address , Amount , ScriptBuf , Txid } ;
1818use common:: logging:: { init_log_logger, validate_log_entry, MultiNodeLogger , TestLogWriter } ;
1919use common:: {
20- bump_fee_and_broadcast, distribute_funds_unconfirmed, do_channel_full_cycle,
21- expect_channel_pending_event, expect_channel_ready_event, expect_channel_ready_events,
22- expect_event, expect_payment_claimable_event, expect_payment_received_event,
23- expect_payment_successful_event, expect_splice_pending_event, generate_blocks_and_wait,
24- generate_listening_addresses, open_channel, open_channel_push_amt, open_channel_with_all,
25- premine_and_distribute_funds, premine_blocks, prepare_rbf, random_chain_source, random_config,
26- setup_bitcoind_and_electrsd, setup_builder, setup_node, setup_two_nodes, splice_in_with_all,
27- wait_for_tx, TestChainSource , TestStoreType , TestSyncStore ,
20+ bump_fee_and_broadcast, create_tier_stores, distribute_funds_unconfirmed,
21+ do_channel_full_cycle, expect_channel_pending_event, expect_channel_ready_event,
22+ expect_channel_ready_events, expect_event, expect_payment_claimable_event,
23+ expect_payment_received_event, expect_payment_successful_event, expect_splice_pending_event,
24+ generate_blocks_and_wait, generate_listening_addresses, open_channel, open_channel_push_amt,
25+ open_channel_with_all, premine_and_distribute_funds, premine_blocks, prepare_rbf,
26+ random_chain_source, random_config, random_storage_path, setup_bitcoind_and_electrsd,
27+ setup_builder, setup_node, setup_two_nodes, setup_two_nodes_with_store, splice_in_with_all,
28+ test_kv_list, test_kv_read, wait_for_tx, TestChainSource , TestStoreType , TestSyncStore ,
2829} ;
2930use electrsd:: corepc_node:: Node as BitcoinD ;
3031use electrsd:: ElectrsD ;
@@ -39,6 +40,11 @@ use ldk_node::{Builder, Event, NodeError};
3940use lightning:: ln:: channelmanager:: PaymentId ;
4041use lightning:: routing:: gossip:: { NodeAlias , NodeId } ;
4142use lightning:: routing:: router:: RouteParametersConfig ;
43+ use lightning:: util:: persist:: {
44+ CHANNEL_MANAGER_PERSISTENCE_KEY , CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE ,
45+ CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE , NETWORK_GRAPH_PERSISTENCE_KEY ,
46+ NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE , NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE ,
47+ } ;
4248use lightning_invoice:: { Bolt11InvoiceDescription , Description } ;
4349use lightning_types:: payment:: { PaymentHash , PaymentPreimage } ;
4450use log:: LevelFilter ;
@@ -52,6 +58,106 @@ async fn channel_full_cycle() {
5258 . await ;
5359}
5460
61+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
62+ async fn channel_full_cycle_tier_store ( ) {
63+ let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
64+ let chain_source = random_chain_source ( & bitcoind, & electrsd) ;
65+ let ( primary_a, backup_a, ephemeral_a) = create_tier_stores ( random_storage_path ( ) ) ;
66+ let ( primary_b, backup_b, ephemeral_b) = create_tier_stores ( random_storage_path ( ) ) ;
67+
68+ let ( node_a, node_b) = setup_two_nodes_with_store (
69+ & chain_source,
70+ false ,
71+ true ,
72+ false ,
73+ TestStoreType :: TierStore {
74+ primary : primary_a. clone ( ) ,
75+ backup : Some ( backup_a. clone ( ) ) ,
76+ ephemeral : Some ( ephemeral_a. clone ( ) ) ,
77+ } ,
78+ TestStoreType :: TierStore {
79+ primary : primary_b,
80+ backup : Some ( backup_b) ,
81+ ephemeral : Some ( ephemeral_b) ,
82+ } ,
83+ ) ;
84+ do_channel_full_cycle ( node_a, node_b, & bitcoind. client , & electrsd. client , false , true , false )
85+ . await ;
86+
87+ // Verify primary and backup both contain the same channel manager data.
88+ let primary_channel_manager = test_kv_read (
89+ & ( primary_a. clone ( ) ) ,
90+ CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE ,
91+ CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE ,
92+ CHANNEL_MANAGER_PERSISTENCE_KEY ,
93+ )
94+ . expect ( "Primary should have channel manager data" ) ;
95+ let backup_channel_manager = test_kv_read (
96+ & ( backup_a. clone ( ) ) ,
97+ CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE ,
98+ CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE ,
99+ CHANNEL_MANAGER_PERSISTENCE_KEY ,
100+ )
101+ . expect ( "Backup should have channel manager data" ) ;
102+ assert_eq ! (
103+ primary_channel_manager, backup_channel_manager,
104+ "Primary and backup should store identical channel manager data"
105+ ) ;
106+
107+ // Verify payment data is persisted in both primary and backup stores.
108+ let primary_payments =
109+ test_kv_list ( & ( primary_a. clone ( ) ) , "payments" , "" ) . expect ( "Primary should list payments" ) ;
110+ assert ! (
111+ !primary_payments. is_empty( ) ,
112+ "Primary should have payment entries after the full cycle"
113+ ) ;
114+
115+ let backup_payments =
116+ test_kv_list ( & ( backup_a. clone ( ) ) , "payments" , "" ) . expect ( "Backup should list payments" ) ;
117+ assert ! ( !backup_payments. is_empty( ) , "Backup should have payment entries after the full cycle" ) ;
118+
119+ // Verify ephemeral store does not contain primary-backed critical data.
120+ let ephemeral_channel_manager = test_kv_read (
121+ & ( ephemeral_a. clone ( ) ) ,
122+ CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE ,
123+ CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE ,
124+ CHANNEL_MANAGER_PERSISTENCE_KEY ,
125+ ) ;
126+ assert ! ( ephemeral_channel_manager. is_err( ) , "Ephemeral should not have channel manager data" ) ;
127+
128+ let ephemeral_payments = test_kv_list ( & ( ephemeral_a. clone ( ) ) , "payments" , "" ) ;
129+ assert ! (
130+ ephemeral_payments. is_err( ) || ephemeral_payments. unwrap( ) . is_empty( ) ,
131+ "Ephemeral should not have payment data"
132+ ) ;
133+
134+ // Verify ephemeral-routed data is stored in the ephemeral store.
135+ let ephemeral_network_graph = test_kv_read (
136+ & ( ephemeral_a. clone ( ) ) ,
137+ NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE ,
138+ NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE ,
139+ NETWORK_GRAPH_PERSISTENCE_KEY ,
140+ ) ;
141+ assert ! ( ephemeral_network_graph. is_ok( ) , "Ephemeral should have network graph data" ) ;
142+
143+ // Verify ephemeral-routed data is not mirrored to primary or backup stores.
144+ let primary_network_graph = test_kv_read (
145+ & ( primary_a. clone ( ) ) ,
146+ NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE ,
147+ NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE ,
148+ NETWORK_GRAPH_PERSISTENCE_KEY ,
149+ ) ;
150+ assert ! ( primary_network_graph. is_err( ) , "Primary should not have ephemeral network graph data" ) ;
151+
152+ let backup_network_graph = test_kv_read (
153+ & ( backup_a. clone ( ) ) ,
154+ NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE ,
155+ NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE ,
156+ NETWORK_GRAPH_PERSISTENCE_KEY ,
157+ ) ;
158+ assert ! ( backup_network_graph. is_err( ) , "Backup should not have ephemeral network graph data" ) ;
159+ }
160+
55161#[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
56162async fn channel_full_cycle_force_close ( ) {
57163 let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
@@ -237,8 +343,20 @@ async fn start_stop_reinit() {
237343 setup_builder ! ( builder, config. node_config) ;
238344 builder. set_chain_source_esplora ( esplora_url. clone ( ) , Some ( sync_config) ) ;
239345
240- let node =
241- builder. build_with_store ( config. node_entropy . into ( ) , test_sync_store. clone ( ) ) . unwrap ( ) ;
346+ let node;
347+ #[ cfg( feature = "uniffi" ) ]
348+ {
349+ use ldk_node:: { DynStoreWrapper , FfiDynStoreTrait } ;
350+
351+ let test_sync_store: Arc < dyn FfiDynStoreTrait > =
352+ Arc :: new ( DynStoreWrapper ( test_sync_store. clone ( ) ) ) ;
353+
354+ node = builder. build_with_store ( config. node_entropy . into ( ) , test_sync_store) . unwrap ( ) ;
355+ }
356+ #[ cfg( not( feature = "uniffi" ) ) ]
357+ {
358+ node = builder. build_with_store ( config. node_entropy , test_sync_store. clone ( ) ) . unwrap ( ) ;
359+ }
242360 node. start ( ) . unwrap ( ) ;
243361
244362 let expected_node_id = node. node_id ( ) ;
@@ -276,8 +394,21 @@ async fn start_stop_reinit() {
276394 setup_builder ! ( builder, config. node_config) ;
277395 builder. set_chain_source_esplora ( esplora_url. clone ( ) , Some ( sync_config) ) ;
278396
279- let reinitialized_node =
280- builder. build_with_store ( config. node_entropy . into ( ) , test_sync_store) . unwrap ( ) ;
397+ let reinitialized_node;
398+ #[ cfg( feature = "uniffi" ) ]
399+ {
400+ use ldk_node:: { DynStoreWrapper , FfiDynStoreTrait } ;
401+
402+ let test_sync_store: Arc < dyn FfiDynStoreTrait > = Arc :: new ( DynStoreWrapper ( test_sync_store) ) ;
403+
404+ reinitialized_node =
405+ builder. build_with_store ( config. node_entropy . into ( ) , test_sync_store) . unwrap ( ) ;
406+ }
407+ #[ cfg( not( feature = "uniffi" ) ) ]
408+ {
409+ reinitialized_node =
410+ builder. build_with_store ( config. node_entropy , test_sync_store) . unwrap ( ) ;
411+ }
281412 reinitialized_node. start ( ) . unwrap ( ) ;
282413 assert_eq ! ( reinitialized_node. node_id( ) , expected_node_id) ;
283414
0 commit comments