1use std::{
2 collections::{BTreeMap, HashMap, VecDeque},
3 sync::{Arc, Mutex},
4};
5
6use ark_ff::Zero;
7use mina_core::constants::constraint_constants;
8use mina_curves::pasta::Fp;
9use mina_p2p_messages::bigint::InvalidBigInt;
10
11use crate::{
12 scan_state::{
13 conv::to_ledger_hash,
14 currency::{Amount, Signed, Slot},
15 transaction_logic::{
16 local_state::{
17 apply_zkapp_command_first_pass_aux, apply_zkapp_command_second_pass_aux,
18 LocalStateEnv,
19 },
20 protocol_state::{GlobalState, ProtocolStateView},
21 transaction_applied::ZkappCommandApplied,
22 transaction_partially_applied::ZkappCommandPartiallyApplied,
23 zkapp_command::ZkAppCommand,
24 AccountState,
25 },
26 },
27 Account, AccountId, AccountIndex, Address, HashesMatrix, Mask, MerklePath,
28};
29
30use super::{sparse_ledger_impl::SparseLedgerImpl, LedgerIntf};
31
32#[derive(Clone, Debug)]
33pub struct SparseLedger {
34 inner: Arc<Mutex<SparseLedgerImpl<AccountId, Account>>>,
36}
37
38impl PartialEq for SparseLedger {
39 fn eq(&self, other: &Self) -> bool {
40 if Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner) {
41 return true;
42 }
43
44 let this = self.inner.try_lock().unwrap();
45 let other = other.inner.try_lock().unwrap();
46
47 this.eq(&other)
48 }
49}
50
51impl SparseLedger {
52 fn with<F, R>(&self, fun: F) -> R
53 where
54 F: FnOnce(&mut SparseLedgerImpl<AccountId, Account>) -> R,
55 {
56 let mut inner = self.inner.try_lock().expect("lock failed");
57 fun(&mut inner)
58 }
59}
60
61impl SparseLedger {
62 pub fn create(depth: usize, root_hash: Fp) -> Self {
63 let inner = SparseLedgerImpl::create(depth, root_hash);
64 Self {
65 inner: Arc::new(Mutex::new(inner)),
66 }
67 }
68
69 pub fn of_ledger_subset_exn(oledger: Mask, keys: &[AccountId]) -> Self {
70 let inner = SparseLedgerImpl::of_ledger_subset_exn(oledger, keys);
71 Self {
72 inner: Arc::new(Mutex::new(inner)),
73 }
74 }
75
76 pub fn copy_content(&self) -> Self {
77 let inner = self.with(|this| this.clone());
78 Self {
79 inner: Arc::new(Mutex::new(inner)),
80 }
81 }
82
83 pub fn has_locked_tokens_exn(&self, global_slot: Slot, account_id: AccountId) -> bool {
84 self.with(|this| this.has_locked_tokens_exn(global_slot, account_id))
85 }
86
87 pub fn iteri<F>(&self, fun: F)
88 where
89 F: Fn(Address, &Account),
90 {
91 self.with(|this| this.iteri(fun))
92 }
93
94 pub fn add_path(
95 &mut self,
96 merkle_path: &[MerklePath],
97 account_id: AccountId,
98 account: Account,
99 ) {
100 self.with(|this| this.add_path(merkle_path, account_id, account))
101 }
102
103 #[inline(never)]
104 pub fn get_exn(&self, addr: &Address) -> Box<Account> {
105 self.with(|this| Box::new(this.get_exn(addr).clone()))
106 }
107
108 pub fn set_exn(&mut self, addr: Address, value: Box<Account>) {
109 self.with(|this| this.set_exn(addr, value))
110 }
111
112 pub fn find_index_exn(&self, key: AccountId) -> Address {
113 self.with(|this| this.find_index_exn(key))
114 }
115
116 pub fn path_exn(&mut self, addr: Address) -> Vec<MerklePath> {
117 self.with(|this| this.path_exn(addr))
118 }
119
120 pub fn merkle_root(&mut self) -> Fp {
121 self.with(|this| this.merkle_root())
122 }
123
124 pub fn get_account(&self, key: &AccountId) -> Box<Account> {
125 let account = self.with(|this| {
126 let addr = this.get_index(key)?;
127 this.get(addr)
128 });
129 account.unwrap_or_else(|| Box::new(Account::empty()))
130 }
131
132 pub fn apply_zkapp_first_pass_unchecked_with_states(
133 &mut self,
134 states: &mut Vec<(GlobalState<SparseLedger>, LocalStateEnv<SparseLedger>)>,
135 global_slot: Slot,
136 state_view: &ProtocolStateView,
137 fee_excess: Signed<Amount>,
138 supply_increase: Signed<Amount>,
139 second_pass_ledger: &Self,
140 zkapp_command: &ZkAppCommand,
141 ) -> Result<ZkappCommandPartiallyApplied<SparseLedger>, String> {
142 apply_zkapp_command_first_pass_aux(
143 constraint_constants(),
144 global_slot,
145 state_view,
146 states,
147 |acc, global_state, local_state| {
148 let GlobalState {
149 first_pass_ledger,
150 second_pass_ledger: _,
151 fee_excess,
152 supply_increase,
153 protocol_state,
154 block_global_slot,
155 } = global_state.clone();
156
157 let mut local_state = local_state.clone();
158 local_state.ledger = local_state.ledger.copy_content();
159
160 acc.insert(
161 0,
162 (
163 GlobalState {
164 first_pass_ledger: first_pass_ledger.copy_content(),
165 second_pass_ledger: second_pass_ledger.copy_content(),
166 fee_excess,
167 supply_increase,
168 protocol_state,
169 block_global_slot,
170 },
171 local_state,
172 ),
173 );
174 },
175 Some(fee_excess),
176 Some(supply_increase),
177 self,
178 zkapp_command,
179 )
180 }
181
182 pub fn apply_zkapp_second_pass_unchecked_with_states(
183 &mut self,
184 init: &mut Vec<(GlobalState<SparseLedger>, LocalStateEnv<SparseLedger>)>,
185 c: ZkappCommandPartiallyApplied<Self>,
186 ) -> Result<ZkappCommandApplied, String> {
187 let account_update_applied = apply_zkapp_command_second_pass_aux(
188 constraint_constants(),
189 init,
190 |acc, global_state, local_state| {
191 let GlobalState {
192 first_pass_ledger,
193 second_pass_ledger,
194 fee_excess,
195 supply_increase,
196 protocol_state,
197 block_global_slot,
198 } = global_state.clone();
199
200 let mut local_state = local_state.clone();
201 local_state.ledger = local_state.ledger.copy_content();
202
203 acc.insert(
204 0,
205 (
206 GlobalState {
207 first_pass_ledger: first_pass_ledger.copy_content(),
208 second_pass_ledger: second_pass_ledger.copy_content(),
209 fee_excess,
210 supply_increase,
211 protocol_state,
212 block_global_slot,
213 },
214 local_state,
215 ),
216 );
217 },
219 self,
220 c,
221 )?;
222
223 let rev_states = init;
224 let will_succeed = account_update_applied.command.status.is_applied();
225
226 rev_states.reverse();
227 let nintermediate = rev_states.len().saturating_sub(2);
229
230 for (_global_state, local_state) in rev_states.iter_mut().skip(1).take(nintermediate) {
231 local_state.will_succeed = will_succeed;
232 }
233
234 Ok(account_update_applied)
236 }
237}
238
239impl LedgerIntf for SparseLedger {
240 type Location = Address;
241
242 fn get(&self, addr: &Self::Location) -> Option<Box<Account>> {
244 self.with(|this| this.get(addr))
245 }
246
247 fn location_of_account(&self, account_id: &AccountId) -> Option<Self::Location> {
249 self.with(|this| this.location_of_account(account_id))
250 }
251
252 fn set(&mut self, addr: &Self::Location, account: Box<Account>) {
254 self.with(|this| this.set(addr, account))
255 }
256
257 fn get_or_create(
259 &mut self,
260 account_id: &AccountId,
261 ) -> Result<(AccountState, Box<Account>, Self::Location), String> {
262 self.with(|this| this.get_or_create(account_id))
263 }
264
265 fn create_new_account(&mut self, account_id: AccountId, to_set: Account) -> Result<(), ()> {
267 self.with(|this| this.create_new_account(account_id, to_set))
268 }
269
270 fn remove_accounts_exn(&mut self, _account_ids: &[AccountId]) {
272 unimplemented!("remove_accounts_exn: not implemented")
273 }
274
275 fn merkle_root(&mut self) -> Fp {
277 self.merkle_root()
278 }
279
280 fn empty(depth: usize) -> Self {
282 Self::create(depth, Fp::zero())
283 }
284
285 fn create_masked(&self) -> Self {
286 self.copy_content()
287 }
288
289 fn apply_mask(&mut self, mask: Self) {
290 let mask_inner = mask.with(|this| this.clone());
291 self.with(|this| *this = mask_inner);
292 }
293
294 fn account_locations(&self) -> Vec<Self::Location> {
295 self.with(|this| this.account_locations())
296 }
297}
298
299impl From<&SparseLedger> for mina_p2p_messages::v2::MinaBaseSparseLedgerBaseStableV2 {
300 fn from(value: &SparseLedger) -> Self {
301 use mina_p2p_messages::v2::{
302 MinaBaseAccountBinableArgStableV2, MinaBaseAccountIdStableV2,
303 MinaBaseSparseLedgerBaseStableV2Tree,
304 };
305
306 let value = value.inner.try_lock().unwrap();
307
308 assert!(value.hashes_matrix.get(&Address::root()).is_some());
309
310 let indexes: Vec<_> = value
311 .indexes_list
312 .iter()
313 .map(|id| {
314 let addr = value.indexes.get(id).unwrap();
315
316 let index = addr.to_index();
317 let index: mina_p2p_messages::number::UInt64 = index.as_u64().into();
318
319 let id: MinaBaseAccountIdStableV2 = id.clone().into();
320
321 (id, index)
322 })
323 .collect();
324
325 fn build_tree(
326 addr: Address,
327 matrix: &HashesMatrix,
328 ledger_depth: usize,
329 values: &BTreeMap<AccountIndex, Account>,
330 ) -> MinaBaseSparseLedgerBaseStableV2Tree {
331 if addr.length() == ledger_depth {
332 let account_index = addr.to_index();
333
334 return match values.get(&account_index).cloned() {
335 Some(account) => {
336 let account: MinaBaseAccountBinableArgStableV2 = (&account).into();
337 MinaBaseSparseLedgerBaseStableV2Tree::Account(Box::new(account))
338 }
339 None => {
340 let hash = matrix.get(&addr).unwrap();
341 MinaBaseSparseLedgerBaseStableV2Tree::Hash(to_ledger_hash(hash))
342 }
343 };
344 }
345
346 let child_left = addr.child_left();
347 let child_right = addr.child_right();
348
349 let is_level_above_accounts = addr.length() == ledger_depth - 1;
350
351 let is_left = matrix.get(&child_left).is_some()
352 || (is_level_above_accounts && values.get(&child_left.to_index()).is_some());
353 let is_right = matrix.get(&child_right).is_some()
354 || (is_level_above_accounts && values.get(&child_right.to_index()).is_some());
355
356 if is_left && is_right {
357 let hash = matrix.get(&addr).unwrap();
358 let left_node = build_tree(child_left, matrix, ledger_depth, values);
359 let right_node = build_tree(child_right, matrix, ledger_depth, values);
360
361 MinaBaseSparseLedgerBaseStableV2Tree::Node(
362 to_ledger_hash(hash),
363 Box::new(left_node),
364 Box::new(right_node),
365 )
366 } else {
367 assert!(!is_left && !is_right);
368 let hash = matrix.get(&addr).unwrap();
369 MinaBaseSparseLedgerBaseStableV2Tree::Hash(to_ledger_hash(hash))
370 }
371 }
372
373 let tree = build_tree(
374 Address::root(),
375 &value.hashes_matrix,
376 value.depth,
377 &value.values,
378 );
379
380 let depth: u64 = value.depth.try_into().unwrap();
381
382 Self {
383 indexes: indexes.into_iter().collect(),
384 depth: depth.into(),
385 tree,
386 }
387 }
388}
389
390impl TryFrom<&mina_p2p_messages::v2::MinaBaseSparseLedgerBaseStableV2> for SparseLedger {
391 type Error = InvalidBigInt;
392
393 fn try_from(
394 value: &mina_p2p_messages::v2::MinaBaseSparseLedgerBaseStableV2,
395 ) -> Result<Self, Self::Error> {
396 use mina_p2p_messages::v2::{
397 MinaBaseSparseLedgerBaseStableV2Tree,
398 MinaBaseSparseLedgerBaseStableV2Tree::{Account, Hash, Node},
399 };
400
401 fn build_matrix(
402 matrix: &mut HashesMatrix,
403 addr: Address,
404 node: &MinaBaseSparseLedgerBaseStableV2Tree,
405 values: &mut BTreeMap<AccountIndex, crate::Account>,
406 ) -> Result<(), InvalidBigInt> {
407 match node {
408 Account(account) => {
409 let account: crate::Account = (&**account).try_into()?;
410 values.insert(addr.to_index(), account);
412 }
413 Hash(hash) => {
414 matrix.set(&addr, hash.to_field()?);
415 }
416 Node(hash, left, right) => {
417 matrix.set(&addr, hash.to_field()?);
418 build_matrix(matrix, addr.child_left(), left, values)?;
419 build_matrix(matrix, addr.child_right(), right, values)?;
420 }
421 }
422 Ok(())
423 }
424
425 let depth = value.depth.as_u64() as usize;
426 let mut indexes = HashMap::with_capacity(value.indexes.len());
427 let mut indexes_list = VecDeque::with_capacity(value.indexes.len());
428 let mut hashes_matrix = HashesMatrix::new(depth);
429 let mut values = BTreeMap::new();
430
431 for (account_id, account_index) in value.indexes.iter() {
432 let account_id: AccountId = account_id.try_into()?;
433 let account_index = AccountIndex::from(account_index.as_u64() as usize);
434
435 let addr = Address::from_index(account_index, depth);
436
437 indexes.insert(account_id.clone(), addr);
438 indexes_list.push_back(account_id);
439 }
440
441 build_matrix(
442 &mut hashes_matrix,
443 Address::root(),
444 &value.tree,
445 &mut values,
446 )?;
447
448 Ok(Self {
449 inner: Arc::new(Mutex::new(SparseLedgerImpl {
450 values,
451 indexes,
452 hashes_matrix,
453 depth,
454 indexes_list,
455 })),
456 })
457 }
458}