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 Ok(key) = deserialize(&key) else {
53 return Err(WalletDbError::ParseColumnValueError);
54 };
55 let Ok(value) = deserialize(&value) else {
56 return Err(WalletDbError::ParseColumnValueError);
57 };
58 scanned_blocks.push((key, value));
59 }
60
61 Ok(scanned_blocks)
62 }
63
64 pub fn get_last_scanned_block(&self) -> WalletDbResult<(u32, String)> {
67 let Ok(query_result) = self.cache.scanned_blocks.last() else {
68 return Err(WalletDbError::QueryExecutionFailed);
69 };
70 let Some((key, value)) = query_result else { return Ok((0, String::from("-"))) };
71 let key: [u8; 4] = match key.as_ref().try_into() {
72 Ok(k) => k,
73 Err(_) => return Err(WalletDbError::ParseColumnValueError),
74 };
75 let key = u32::from_be_bytes(key);
76 let Ok(value) = deserialize(&value) else {
77 return Err(WalletDbError::ParseColumnValueError);
78 };
79 Ok((key, value))
80 }
81
82 pub fn reset_scanned_blocks(&self, output: &mut Vec<String>) -> WalletDbResult<()> {
84 output.push(String::from("Resetting scanned blocks"));
85 if let Err(e) = self.cache.scanned_blocks.clear() {
86 output
87 .push(format!("[reset_scanned_blocks] Resetting scanned blocks tree failed: {e}"));
88 return Err(WalletDbError::GenericError)
89 }
90 if let Err(e) = self.cache.state_inverse_diff.clear() {
91 output.push(format!(
92 "[reset_scanned_blocks] Resetting state inverse diffs tree failed: {e}"
93 ));
94 return Err(WalletDbError::GenericError)
95 }
96 output.push(String::from("Successfully reset scanned blocks"));
97
98 Ok(())
99 }
100
101 pub async fn reset_to_height(
104 &self,
105 height: u32,
106 output: &mut Vec<String>,
107 ) -> WalletDbResult<()> {
108 output.push(format!("Resetting wallet state to block: {height}"));
109
110 if height == 0 {
113 return self.reset(output)
114 }
115
116 let (last, _) = self.get_last_scanned_block()?;
118
119 if last <= height {
121 output.push(String::from(
122 "Requested block height is greater or equal to last scanned block",
123 ));
124 return Ok(())
125 }
126
127 let mut money_tree = match self.get_money_tree().await {
129 Ok(t) => t,
130 Err(e) => {
131 output.push(format!("[reset_to_height] Money merkle tree retrieval failed: {e}"));
132 return Err(WalletDbError::GenericError)
133 }
134 };
135 let (mut dao_daos_tree, mut dao_proposals_tree) = match self.get_dao_trees().await {
136 Ok(p) => p,
137 Err(e) => {
138 output.push(format!("[reset_to_height] DAO merkle trees retrieval failed: {e}"));
139 return Err(WalletDbError::GenericError)
140 }
141 };
142
143 let mut overlay = match CacheOverlay::new(&self.cache) {
145 Ok(o) => o,
146 Err(e) => {
147 output.push(format!("[reset_to_height] Creating cache overlay failed: {e}"));
148 return Err(WalletDbError::GenericError)
149 }
150 };
151
152 for height in (height + 1..=last).rev() {
155 let inverse_diff = match self.cache.get_state_inverse_diff(&height) {
156 Ok(d) => d,
157 Err(e) => {
158 output.push(format!(
159 "[reset_to_height] Retrieving state inverse diff from cache failed: {e}"
160 ));
161 return Err(WalletDbError::GenericError)
162 }
163 };
164
165 if let Err(e) = overlay.0.add_diff(&inverse_diff) {
167 output.push(format!(
168 "[reset_to_height] Adding state inverse diff to the cache overlay failed: {e}"
169 ));
170 return Err(WalletDbError::GenericError)
171 }
172 if let Err(e) = overlay.0.apply_diff(&inverse_diff) {
173 output.push(format!("[reset_to_height] Applying state inverse diff to the cache overlay failed: {e}"));
174 return Err(WalletDbError::GenericError)
175 }
176
177 if let Err(e) = self.cache.state_inverse_diff.remove(height.to_be_bytes()) {
179 output.push(format!(
180 "[reset_to_height] Removing state inverse diff from the cache failed: {e}"
181 ));
182 return Err(WalletDbError::GenericError)
183 }
184
185 money_tree.rewind();
187 dao_daos_tree.rewind();
188 dao_proposals_tree.rewind();
189 if let Err(e) = self.cache.insert_merkle_trees(&[
190 (SLED_MERKLE_TREES_MONEY, &money_tree),
191 (SLED_MERKLE_TREES_DAO_DAOS, &dao_daos_tree),
192 (SLED_MERKLE_TREES_DAO_PROPOSALS, &dao_proposals_tree),
193 ]) {
194 output.push(format!("[reset_to_height] Updating merkle trees failed: {e}"));
195 return Err(WalletDbError::GenericError)
196 };
197
198 if let Err(e) = self.cache.sled_db.flush() {
200 output.push(format!("[reset_to_height] Flushing cache sled database failed: {e}"));
201 return Err(WalletDbError::GenericError)
202 }
203 }
204
205 self.remove_money_coins_after(&height, output)?;
207
208 self.unspent_money_coins_after(&height, output)?;
210
211 self.unfreeze_mint_authorities_after(&height, output)?;
214
215 self.unconfirm_daos_after(&height, output)?;
217
218 self.unconfirm_dao_proposals_after(&height, output)?;
220
221 self.unexec_dao_proposals_after(&height, output)?;
224
225 self.remove_dao_votes_after(&height, output)?;
228
229 self.unlock_deploy_authorities_after(&height, output)?;
231
232 self.remove_deploy_history_after(&height, output)?;
235
236 self.revert_transactions_after(&height, output)?;
239
240 output.push(String::from("Successfully reset wallet state"));
241 Ok(())
242 }
243}