@@ -36,12 +36,16 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
3636 pub async fn new (
3737 config : ClientConfig ,
3838 network : N ,
39- storage : S ,
39+ mut storage : S ,
4040 wallet : Arc < RwLock < W > > ,
4141 ) -> Result < Self > {
4242 // Validate configuration
4343 config. validate ( ) . map_err ( SpvError :: Config ) ?;
4444
45+ // Initialize genesis block or checkpoint before creating managers,
46+ // so they can read the tip from storage during construction.
47+ Self :: initialize_genesis_block ( & config, & mut storage) . await ?;
48+
4549 let masternode_engine = {
4650 if config. enable_masternodes {
4751 Some ( Arc :: new ( RwLock :: new ( MasternodeListEngine :: default_for_network (
@@ -68,49 +72,60 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
6872 } ;
6973 let checkpoint_manager = Arc :: new ( CheckpointManager :: new ( checkpoints) ) ;
7074 managers. block_headers =
71- Some ( BlockHeadersManager :: new ( storage. block_headers ( ) , checkpoint_manager) ) ;
75+ Some ( BlockHeadersManager :: new ( storage. block_headers ( ) , checkpoint_manager) . await ? ) ;
7276
7377 if config. enable_filters {
74- managers. filter_headers =
75- Some ( FilterHeadersManager :: new ( storage. block_headers ( ) , storage. filter_headers ( ) ) ) ;
76- managers. filters = Some ( FiltersManager :: new (
77- wallet. clone ( ) ,
78- storage. block_headers ( ) ,
79- storage. filter_headers ( ) ,
80- storage. filters ( ) ,
81- ) ) ;
82- managers. blocks =
83- Some ( BlocksManager :: new ( wallet. clone ( ) , storage. block_headers ( ) , storage. blocks ( ) ) ) ;
78+ managers. filter_headers = Some (
79+ FilterHeadersManager :: new ( storage. block_headers ( ) , storage. filter_headers ( ) )
80+ . await ?,
81+ ) ;
82+ managers. filters = Some (
83+ FiltersManager :: new (
84+ wallet. clone ( ) ,
85+ storage. block_headers ( ) ,
86+ storage. filter_headers ( ) ,
87+ storage. filters ( ) ,
88+ )
89+ . await ,
90+ ) ;
91+ managers. blocks = Some (
92+ BlocksManager :: new ( wallet. clone ( ) , storage. block_headers ( ) , storage. blocks ( ) ) . await ,
93+ ) ;
8494 }
8595
8696 // Build masternode manager if enabled
8797 if config. enable_masternodes {
8898 let masternode_list_engine = masternode_engine
8999 . clone ( )
90100 . expect ( "Masternode list engine must exist if masternodes are enabled" ) ;
91- managers. masternode = Some ( MasternodesManager :: new (
92- storage. block_headers ( ) ,
93- masternode_list_engine. clone ( ) ,
94- config. network ,
95- ) ) ;
96- managers. chainlock = Some ( ChainLockManager :: new (
97- storage. block_headers ( ) ,
98- storage. metadata ( ) ,
99- masternode_list_engine. clone ( ) ,
100- ) ) ;
101+ managers. masternode = Some (
102+ MasternodesManager :: new (
103+ storage. block_headers ( ) ,
104+ masternode_list_engine. clone ( ) ,
105+ config. network ,
106+ )
107+ . await ,
108+ ) ;
109+ managers. chainlock = Some (
110+ ChainLockManager :: new (
111+ storage. block_headers ( ) ,
112+ storage. metadata ( ) ,
113+ masternode_list_engine. clone ( ) ,
114+ )
115+ . await ,
116+ ) ;
101117 managers. instantsend = Some ( InstantSendManager :: new ( masternode_list_engine. clone ( ) ) ) ;
102118 }
103119
104- // Create sync coordinator (managers are passed to start() later)
105- let sync_coordinator = SyncCoordinator :: new ( managers) ;
120+ let sync_coordinator = SyncCoordinator :: new ( managers) . await ;
106121
107122 // Create mempool state
108123 let mempool_state = Arc :: new ( RwLock :: new ( MempoolState :: default ( ) ) ) ;
109124
110125 // Wrap storage in Arc<Mutex>
111126 let storage = Arc :: new ( Mutex :: new ( storage) ) ;
112127
113- Ok ( Self {
128+ let client = Self {
114129 config : Arc :: new ( RwLock :: new ( config) ) ,
115130 network : Arc :: new ( Mutex :: new ( network) ) ,
116131 storage,
@@ -120,56 +135,51 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
120135 running : Arc :: new ( RwLock :: new ( false ) ) ,
121136 mempool_state,
122137 mempool_filter : Arc :: new ( RwLock :: new ( None ) ) ,
123- } )
124- }
125-
126- /// Start the SPV client.
127- pub ( super ) async fn start ( & self ) -> Result < ( ) > {
128- {
129- let running = self . running . read ( ) . await ;
130- if * running {
131- return Err ( SpvError :: Config ( "Client already running" . to_string ( ) ) ) ;
132- }
133- }
138+ } ;
134139
135140 // Load wallet data from storage
136- self . load_wallet_data ( ) . await ?;
137-
138- let config = self . config . read ( ) . await ;
141+ client. load_wallet_data ( ) . await ?;
139142
140143 // Initialize mempool filter if mempool tracking is enabled
141- if config. enable_mempool_tracking {
142- // TODO: Get monitored addresses from wallet
143- let filter = Arc :: new ( MempoolFilter :: new (
144- config. mempool_strategy ,
145- config. max_mempool_transactions ,
146- self . mempool_state . clone ( ) ,
147- HashSet :: new ( ) , // Will be populated from wallet's monitored addresses
148- config. network ,
149- ) ) ;
150-
151- * self . mempool_filter . write ( ) . await = Some ( filter) ;
152-
153- // Load mempool state from storage if persistence is enabled
154- if config. persist_mempool {
155- if let Some ( state) = self
156- . storage
157- . lock ( )
158- . await
159- . load_mempool_state ( )
160- . await
161- . map_err ( SpvError :: Storage ) ?
162- {
163- * self . mempool_state . write ( ) . await = state;
144+ {
145+ let config = client. config . read ( ) . await ;
146+ if config. enable_mempool_tracking {
147+ let filter = Arc :: new ( MempoolFilter :: new (
148+ config. mempool_strategy ,
149+ config. max_mempool_transactions ,
150+ client. mempool_state . clone ( ) ,
151+ HashSet :: new ( ) , // TODO: populate from wallet's monitored addresses
152+ config. network ,
153+ ) ) ;
154+ * client. mempool_filter . write ( ) . await = Some ( filter) ;
155+
156+ // Load mempool state from storage if persistence is enabled
157+ if config. persist_mempool {
158+ if let Some ( state) = client
159+ . storage
160+ . lock ( )
161+ . await
162+ . load_mempool_state ( )
163+ . await
164+ . map_err ( SpvError :: Storage ) ?
165+ {
166+ * client. mempool_state . write ( ) . await = state;
167+ }
164168 }
165169 }
166170 }
167171
168- // Drop config before calling methods that also read it
169- drop ( config ) ;
172+ Ok ( client )
173+ }
170174
171- // Initialize genesis block if not already present
172- self . initialize_genesis_block ( ) . await ?;
175+ /// Start the SPV client: spawn sync tasks and connect to the network.
176+ pub ( super ) async fn start ( & self ) -> Result < ( ) > {
177+ {
178+ let running = self . running . read ( ) . await ;
179+ if * running {
180+ return Err ( SpvError :: Config ( "Client already running" . to_string ( ) ) ) ;
181+ }
182+ }
173183
174184 // Start the storage background worker for periodic persistence
175185 self . storage . lock ( ) . await . start ( ) . await ;
@@ -233,15 +243,12 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
233243 self . stop ( ) . await
234244 }
235245
236- /// Initialize genesis block or checkpoint.
237- pub ( super ) async fn initialize_genesis_block ( & self ) -> Result < ( ) > {
238- let config = self . config . read ( ) . await ;
239-
246+ /// Initialize genesis block or checkpoint in storage .
247+ ///
248+ /// Called before creating managers so they can read the tip during construction.
249+ async fn initialize_genesis_block ( config : & ClientConfig , storage : & mut S ) -> Result < ( ) > {
240250 // Check if we already have any headers in storage
241- let current_tip = {
242- let storage = self . storage . lock ( ) . await ;
243- storage. get_tip_height ( ) . await
244- } ;
251+ let current_tip = storage. get_tip_height ( ) . await ;
245252
246253 if current_tip. is_some ( ) {
247254 // We already have headers, genesis block should be at height 0
@@ -301,12 +308,9 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
301308 calculated_hash
302309 ) ;
303310 } else {
304- {
305- let mut storage = self . storage . lock ( ) . await ;
306- storage
307- . store_headers_at_height ( & [ checkpoint_header] , checkpoint. height )
308- . await ?;
309- }
311+ storage
312+ . store_headers_at_height ( & [ checkpoint_header] , checkpoint. height )
313+ . await ?;
310314
311315 tracing:: info!(
312316 "✅ Initialized from checkpoint at height {}, skipping {} headers" ,
@@ -346,17 +350,10 @@ impl<W: WalletInterface, N: NetworkManager, S: StorageManager> DashSpvClient<W,
346350 tracing:: debug!( "Using genesis block header with hash: {}" , calculated_hash) ;
347351
348352 // Store the genesis header at height 0
349- let genesis_headers = vec ! [ genesis_header] ;
350- {
351- let mut storage = self . storage . lock ( ) . await ;
352- storage. store_headers ( & genesis_headers) . await . map_err ( SpvError :: Storage ) ?;
353- }
353+ storage. store_headers ( & [ genesis_header] ) . await . map_err ( SpvError :: Storage ) ?;
354354
355355 // Verify it was stored correctly
356- let stored_height = {
357- let storage = self . storage . lock ( ) . await ;
358- storage. get_tip_height ( ) . await
359- } ;
356+ let stored_height = storage. get_tip_height ( ) . await ;
360357 tracing:: info!(
361358 "✅ Genesis block initialized at height 0, storage reports tip height: {:?}" ,
362359 stored_height
0 commit comments