1+ //! Wallet consistency validation and recovery functionality.
2+
3+ use std:: sync:: Arc ;
4+ use tokio:: sync:: RwLock ;
5+ use std:: collections:: HashSet ;
6+
7+ use crate :: error:: { Result , SpvError } ;
8+ use crate :: types:: WatchItem ;
9+ use crate :: wallet:: Wallet ;
10+ use crate :: storage:: StorageManager ;
11+
12+ /// Report of wallet consistency validation.
13+ #[ derive( Debug , Clone ) ]
14+ pub struct ConsistencyReport {
15+ /// UTXO mismatches between wallet and storage.
16+ pub utxo_mismatches : Vec < String > ,
17+ /// Address mismatches between watch items and wallet.
18+ pub address_mismatches : Vec < String > ,
19+ /// Balance calculation mismatches.
20+ pub balance_mismatches : Vec < String > ,
21+ /// Whether the wallet and storage are consistent.
22+ pub is_consistent : bool ,
23+ }
24+
25+ /// Result of wallet consistency recovery attempt.
26+ #[ derive( Debug , Clone ) ]
27+ pub struct ConsistencyRecovery {
28+ /// Number of UTXOs synced from storage to wallet.
29+ pub utxos_synced : usize ,
30+ /// Number of addresses synced between watch items and wallet.
31+ pub addresses_synced : usize ,
32+ /// Number of UTXOs removed from wallet (not in storage).
33+ pub utxos_removed : usize ,
34+ /// Whether the recovery was successful.
35+ pub success : bool ,
36+ }
37+
38+ /// Wallet consistency manager.
39+ pub struct ConsistencyManager < ' a > {
40+ wallet : & ' a Arc < RwLock < Wallet > > ,
41+ storage : & ' a dyn StorageManager ,
42+ watch_items : & ' a Arc < RwLock < HashSet < WatchItem > > > ,
43+ }
44+
45+ impl < ' a > ConsistencyManager < ' a > {
46+ /// Create a new consistency manager.
47+ pub fn new (
48+ wallet : & ' a Arc < RwLock < Wallet > > ,
49+ storage : & ' a dyn StorageManager ,
50+ watch_items : & ' a Arc < RwLock < HashSet < WatchItem > > > ,
51+ ) -> Self {
52+ Self {
53+ wallet,
54+ storage,
55+ watch_items,
56+ }
57+ }
58+
59+ /// Validate wallet and storage consistency.
60+ pub async fn validate_wallet_consistency ( & self ) -> Result < ConsistencyReport > {
61+ tracing:: info!( "Validating wallet and storage consistency..." ) ;
62+
63+ let mut report = ConsistencyReport {
64+ utxo_mismatches : Vec :: new ( ) ,
65+ address_mismatches : Vec :: new ( ) ,
66+ balance_mismatches : Vec :: new ( ) ,
67+ is_consistent : true ,
68+ } ;
69+
70+ // Validate UTXO consistency between wallet and storage
71+ let wallet = self . wallet . read ( ) . await ;
72+ let wallet_utxos = wallet. get_utxos ( ) . await ;
73+ let storage_utxos = self . storage . get_all_utxos ( ) . await
74+ . map_err ( |e| SpvError :: Storage ( e) ) ?;
75+
76+ // Check for UTXOs in wallet but not in storage
77+ for wallet_utxo in & wallet_utxos {
78+ if !storage_utxos. contains_key ( & wallet_utxo. outpoint ) {
79+ report. utxo_mismatches . push ( format ! (
80+ "UTXO {} exists in wallet but not in storage" ,
81+ wallet_utxo. outpoint
82+ ) ) ;
83+ report. is_consistent = false ;
84+ }
85+ }
86+
87+ // Check for UTXOs in storage but not in wallet
88+ for ( outpoint, storage_utxo) in & storage_utxos {
89+ if !wallet_utxos. iter ( ) . any ( |wu| & wu. outpoint == outpoint) {
90+ report. utxo_mismatches . push ( format ! (
91+ "UTXO {} exists in storage but not in wallet (address: {})" ,
92+ outpoint, storage_utxo. address
93+ ) ) ;
94+ report. is_consistent = false ;
95+ }
96+ }
97+
98+ // Validate address consistency between WatchItems and wallet
99+ let watch_items = self . watch_items . read ( ) . await ;
100+ let wallet_addresses = wallet. get_watched_addresses ( ) . await ;
101+
102+ // Collect addresses from watch items
103+ let watch_addresses: std:: collections:: HashSet < _ > = watch_items. iter ( )
104+ . filter_map ( |item| {
105+ if let WatchItem :: Address { address, .. } = item {
106+ Some ( address. clone ( ) )
107+ } else {
108+ None
109+ }
110+ } )
111+ . collect ( ) ;
112+
113+ let wallet_address_set: std:: collections:: HashSet < _ > = wallet_addresses. iter ( ) . cloned ( ) . collect ( ) ;
114+
115+ // Check for addresses in watch items but not in wallet
116+ for address in & watch_addresses {
117+ if !wallet_address_set. contains ( address) {
118+ report. address_mismatches . push ( format ! (
119+ "Address {} in watch items but not in wallet" ,
120+ address
121+ ) ) ;
122+ report. is_consistent = false ;
123+ }
124+ }
125+
126+ // Check for addresses in wallet but not in watch items
127+ for address in & wallet_addresses {
128+ if !watch_addresses. contains ( address) {
129+ report. address_mismatches . push ( format ! (
130+ "Address {} in wallet but not in watch items" ,
131+ address
132+ ) ) ;
133+ report. is_consistent = false ;
134+ }
135+ }
136+
137+ if report. is_consistent {
138+ tracing:: info!( "✅ Wallet consistency validation passed" ) ;
139+ } else {
140+ tracing:: warn!( "❌ Wallet consistency issues detected: {} UTXO mismatches, {} address mismatches" ,
141+ report. utxo_mismatches. len( ) , report. address_mismatches. len( ) ) ;
142+ }
143+
144+ Ok ( report)
145+ }
146+
147+ /// Attempt to recover from wallet consistency issues.
148+ pub async fn recover_wallet_consistency ( & self ) -> Result < ConsistencyRecovery > {
149+ tracing:: info!( "Attempting wallet consistency recovery..." ) ;
150+
151+ let mut recovery = ConsistencyRecovery {
152+ utxos_synced : 0 ,
153+ addresses_synced : 0 ,
154+ utxos_removed : 0 ,
155+ success : true ,
156+ } ;
157+
158+ // First, validate to see what needs fixing
159+ let report = self . validate_wallet_consistency ( ) . await ?;
160+
161+ if report. is_consistent {
162+ tracing:: info!( "No recovery needed - wallet is already consistent" ) ;
163+ return Ok ( recovery) ;
164+ }
165+
166+ let wallet = self . wallet . read ( ) . await ;
167+
168+ // Sync UTXOs from storage to wallet
169+ let storage_utxos = self . storage . get_all_utxos ( ) . await
170+ . map_err ( |e| SpvError :: Storage ( e) ) ?;
171+ let wallet_utxos = wallet. get_utxos ( ) . await ;
172+
173+ // Add missing UTXOs to wallet
174+ for ( outpoint, storage_utxo) in & storage_utxos {
175+ if !wallet_utxos. iter ( ) . any ( |wu| & wu. outpoint == outpoint) {
176+ if let Err ( e) = wallet. add_utxo ( storage_utxo. clone ( ) ) . await {
177+ tracing:: error!( "Failed to sync UTXO {} to wallet: {}" , outpoint, e) ;
178+ recovery. success = false ;
179+ } else {
180+ recovery. utxos_synced += 1 ;
181+ }
182+ }
183+ }
184+
185+ // Remove UTXOs from wallet that aren't in storage
186+ for wallet_utxo in & wallet_utxos {
187+ if !storage_utxos. contains_key ( & wallet_utxo. outpoint ) {
188+ if let Err ( e) = wallet. remove_utxo ( & wallet_utxo. outpoint ) . await {
189+ tracing:: error!( "Failed to remove UTXO {} from wallet: {}" , wallet_utxo. outpoint, e) ;
190+ recovery. success = false ;
191+ } else {
192+ recovery. utxos_removed += 1 ;
193+ }
194+ }
195+ }
196+
197+ if recovery. success {
198+ tracing:: info!( "✅ Wallet consistency recovery completed: {} UTXOs synced, {} UTXOs removed, {} addresses synced" ,
199+ recovery. utxos_synced, recovery. utxos_removed, recovery. addresses_synced) ;
200+ } else {
201+ tracing:: error!( "❌ Wallet consistency recovery partially failed" ) ;
202+ }
203+
204+ Ok ( recovery)
205+ }
206+
207+ /// Ensure wallet consistency by validating and recovering if necessary.
208+ pub async fn ensure_wallet_consistency ( & self ) -> Result < ( ) > {
209+ // First validate consistency
210+ let report = self . validate_wallet_consistency ( ) . await ?;
211+
212+ if !report. is_consistent {
213+ tracing:: warn!( "Wallet inconsistencies detected, attempting recovery..." ) ;
214+
215+ // Attempt recovery
216+ let recovery = self . recover_wallet_consistency ( ) . await ?;
217+
218+ if !recovery. success {
219+ return Err ( SpvError :: Config (
220+ "Wallet consistency recovery failed - some issues remain" . to_string ( )
221+ ) ) ;
222+ }
223+
224+ // Validate again after recovery
225+ let post_recovery_report = self . validate_wallet_consistency ( ) . await ?;
226+ if !post_recovery_report. is_consistent {
227+ return Err ( SpvError :: Config (
228+ "Wallet consistency recovery incomplete - issues remain after recovery" . to_string ( )
229+ ) ) ;
230+ }
231+
232+ tracing:: info!( "✅ Wallet consistency fully recovered" ) ;
233+ }
234+
235+ Ok ( ( ) )
236+ }
237+ }
0 commit comments