1use darkfi_serial::deserialize;
20
21use crate::{
22 cache::CacheOverlay,
23 dao::{SLED_MERKLE_TREES_DAO_DAOS, SLED_MERKLE_TREES_DAO_PROPOSALS},
24 error::{WalletDbError, WalletDbResult},
25 money::SLED_MERKLE_TREES_MONEY,
26 Drk,
27};
28
29impl Drk {
30 pub fn get_scanned_block_hash(&self, height: &u32) -> WalletDbResult<String> {
32 let Ok(query_result) = self.cache.scanned_blocks.get(height.to_be_bytes()) else {
33 return Err(WalletDbError::QueryExecutionFailed);
34 };
35 let Some(hash_bytes) = query_result else {
36 return Err(WalletDbError::RowNotFound);
37 };
38 let Ok(hash) = deserialize(&hash_bytes) else {
39 return Err(WalletDbError::ParseColumnValueError);
40 };
41 Ok(hash)
42 }
43
44 pub fn get_scanned_block_records(&self) -> WalletDbResult<Vec<(u32, String)>> {
46 let mut scanned_blocks = vec![];
47
48 for record in self.cache.scanned_blocks.iter() {
49 let Ok((key, value)) = record else {
50 return Err(WalletDbError::QueryExecutionFailed);
51 };
52 let key: [u8; 4] = match key.as_ref().try_into() {
53 Ok(k) => k,
54 Err(_) => return Err(WalletDbError::ParseColumnValueError),
55 };
56 let key = u32::from_be_bytes(key);
57 let Ok(value) = deserialize(&value) else {
58 return Err(WalletDbError::ParseColumnValueError);
59 };
60 scanned_blocks.push((key, value));
61 }
62
63 Ok(scanned_blocks)
64 }
65
66 pub fn get_last_scanned_block(&self) -> WalletDbResult<(u32, String)> {
69 let Ok(query_result) = self.cache.scanned_blocks.last() else {
70 return Err(WalletDbError::QueryExecutionFailed);
71 };
72 let Some((key, value)) = query_result else { return Ok((0, String::from("-"))) };
73 let key: [u8; 4] = match key.as_ref().try_into() {
74 Ok(k) => k,
75 Err(_) => return Err(WalletDbError::ParseColumnValueError),
76 };
77 let key = u32::from_be_bytes(key);
78 let Ok(value) = deserialize(&value) else {
79 return Err(WalletDbError::ParseColumnValueError);
80 };
81 Ok((key, value))
82 }
83
84 pub fn reset_scanned_blocks(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
86 output.push(String::from("Resetting scanned blocks"));
87 if let Err(e) = self.cache.scanned_blocks.clear() {
88 output
89 .push(format!("[reset_scanned_blocks] Resetting scanned blocks tree failed: {e}"));
90 return Err(WalletDbError::GenericError)
91 }
92 if let Err(e) = self.cache.state_inverse_diff.clear() {
93 output.push(format!(
94 "[reset_scanned_blocks] Resetting state inverse diffs tree failed: {e}"
95 ));
96 return Err(WalletDbError::GenericError)
97 }
98 output.push(String::from("Successfully reset scanned blocks"));
99
100 Ok(())
101 }
102
103 pub async fn reset_to_height(
106 &self,
107 height: u32,
108 output: &mut Vec<String>,
109 ) -> WalletDbResult<()> {
110 output.push(format!("Resetting wallet state to block: {height}"));
111
112 if height == 0 {
115 return self.reset(output)
116 }
117
118 let (last, _) = self.get_last_scanned_block()?;
120
121 if last <= height {
123 output.push(String::from(
124 "Requested block height is greater or equal to last scanned block",
125 ));
126 return Ok(())
127 }
128
129 let mut money_tree = match self.get_money_tree().await {
131 Ok(t) => t,
132 Err(e) => {
133 output.push(format!("[reset_to_height] Money merkle tree retrieval failed: {e}"));
134 return Err(WalletDbError::GenericError)
135 }
136 };
137 let (mut dao_daos_tree, mut dao_proposals_tree) = match self.get_dao_trees().await {
138 Ok(p) => p,
139 Err(e) => {
140 output.push(format!("[reset_to_height] DAO merkle trees retrieval failed: {e}"));
141 return Err(WalletDbError::GenericError)
142 }
143 };
144
145 let mut overlay = match CacheOverlay::new(&self.cache) {
147 Ok(o) => o,
148 Err(e) => {
149 output.push(format!("[reset_to_height] Creating cache overlay failed: {e}"));
150 return Err(WalletDbError::GenericError)
151 }
152 };
153
154 for height in (height + 1..=last).rev() {
157 let inverse_diff = match self.cache.get_state_inverse_diff(&height) {
158 Ok(d) => d,
159 Err(e) => {
160 output.push(format!(
161 "[reset_to_height] Retrieving state inverse diff from cache failed: {e}"
162 ));
163 return Err(WalletDbError::GenericError)
164 }
165 };
166
167 if let Err(e) = overlay.0.add_diff(&inverse_diff) {
169 output.push(format!(
170 "[reset_to_height] Adding state inverse diff to the cache overlay failed: {e}"
171 ));
172 return Err(WalletDbError::GenericError)
173 }
174 if let Err(e) = overlay.0.apply_diff(&inverse_diff) {
175 output.push(format!("[reset_to_height] Applying state inverse diff to the cache overlay failed: {e}"));
176 return Err(WalletDbError::GenericError)
177 }
178
179 if let Err(e) = self.cache.state_inverse_diff.remove(height.to_be_bytes()) {
181 output.push(format!(
182 "[reset_to_height] Removing state inverse diff from the cache failed: {e}"
183 ));
184 return Err(WalletDbError::GenericError)
185 }
186
187 money_tree.rewind();
189 dao_daos_tree.rewind();
190 dao_proposals_tree.rewind();
191 if let Err(e) = self.cache.insert_merkle_trees(&[
192 (SLED_MERKLE_TREES_MONEY, &money_tree),
193 (SLED_MERKLE_TREES_DAO_DAOS, &dao_daos_tree),
194 (SLED_MERKLE_TREES_DAO_PROPOSALS, &dao_proposals_tree),
195 ]) {
196 output.push(format!("[reset_to_height] Updating merkle trees failed: {e}"));
197 return Err(WalletDbError::GenericError)
198 };
199
200 if let Err(e) = self.cache.sled_db.flush() {
202 output.push(format!("[reset_to_height] Flushing cache sled database failed: {e}"));
203 return Err(WalletDbError::GenericError)
204 }
205 }
206
207 self.remove_money_coins_after(&height, output)?;
209
210 self.unspent_money_coins_after(&height, output)?;
212
213 self.unfreeze_mint_authorities_after(&height, output)?;
216
217 self.unconfirm_daos_after(&height, output)?;
219
220 self.unconfirm_dao_proposals_after(&height, output)?;
222
223 self.unexec_dao_proposals_after(&height, output)?;
226
227 self.remove_dao_votes_after(&height, output)?;
230
231 self.unlock_deploy_authorities_after(&height, output)?;
233
234 self.remove_deploy_history_after(&height, output)?;
237
238 self.revert_transactions_after(&height, output)?;
241
242 output.push(String::from("Successfully reset wallet state"));
243 Ok(())
244 }
245}