1use std::collections::BTreeMap;
3
4use anyhow::anyhow;
5
6use crate::{
7 common::{
8 key_format::{KeyFormat, KeyFormatAtom},
9 quantity::Quantity,
10 },
11 consensus::{
12 address::Address,
13 beacon::EpochTime,
14 staking::{Account, DebondingDelegation, Delegation},
15 state::StateError,
16 },
17 key_format,
18 storage::mkvs::ImmutableMKVS,
19};
20
21pub struct ImmutableState<'a, T: ImmutableMKVS> {
23 mkvs: &'a T,
24}
25
26impl<'a, T: ImmutableMKVS> ImmutableState<'a, T> {
27 pub fn new(mkvs: &'a T) -> ImmutableState<'a, T> {
29 ImmutableState { mkvs }
30 }
31}
32
33key_format!(AccountsKeyFmt, 0x50, Address);
34key_format!(TotalSupplyKeyFmt, 0x51, ());
35key_format!(CommonPoolKeyFmt, 0x52, ());
36key_format!(DelegationKeyFmt, 0x53, (Address, Address));
37key_format!(
38 DebondingDelegationKeyFmt,
39 0x54,
40 (Address, Address, EpochTime)
41);
42key_format!(LastBlockFees, 0x57, ());
43key_format!(GovernanceDepositsKeyFmt, 0x59, ());
44
45impl<'a, T: ImmutableMKVS> ImmutableState<'a, T> {
46 pub fn account(&self, address: Address) -> Result<Account, StateError> {
48 match self.mkvs.get(&AccountsKeyFmt(address).encode()) {
49 Ok(Some(b)) => {
50 cbor::from_slice(&b).map_err(|err| StateError::Unavailable(anyhow!(err)))
51 }
52 Ok(None) => Ok(Account::default()),
53 Err(err) => Err(StateError::Unavailable(anyhow!(err))),
54 }
55 }
56
57 fn load_stored_balance<K: KeyFormat>(&self, key_format: K) -> Result<Quantity, StateError> {
58 match self.mkvs.get(&key_format.encode()) {
59 Ok(Some(b)) => {
60 cbor::from_slice(&b).map_err(|err| StateError::Unavailable(anyhow!(err)))
61 }
62 Ok(None) => Ok(Quantity::default()),
63 Err(err) => Err(StateError::Unavailable(anyhow!(err))),
64 }
65 }
66
67 pub fn total_supply(&self) -> Result<Quantity, StateError> {
69 self.load_stored_balance(TotalSupplyKeyFmt(()))
70 }
71
72 pub fn common_pool(&self) -> Result<Quantity, StateError> {
74 self.load_stored_balance(CommonPoolKeyFmt(()))
75 }
76
77 pub fn last_block_fees(&self) -> Result<Quantity, StateError> {
79 self.load_stored_balance(LastBlockFees(()))
80 }
81
82 pub fn governance_deposits(&self) -> Result<Quantity, StateError> {
84 self.load_stored_balance(GovernanceDepositsKeyFmt(()))
85 }
86
87 pub fn addresses(&self) -> Result<Vec<Address>, StateError> {
89 let mut it = self.mkvs.iter();
90 it.seek(&AccountsKeyFmt::default().encode_partial(0));
91
92 Ok(it
93 .map_while(|(key, _)| AccountsKeyFmt::decode(&key))
94 .map(|AccountsKeyFmt(addr)| addr)
95 .collect())
96 }
97
98 pub fn delegation(
100 &self,
101 delegator_addr: Address,
102 escrow_addr: Address,
103 ) -> Result<Delegation, StateError> {
104 match self
105 .mkvs
106 .get(&DelegationKeyFmt((escrow_addr, delegator_addr)).encode())
107 {
108 Ok(Some(b)) => {
109 cbor::from_slice(&b).map_err(|err| StateError::Unavailable(anyhow!(err)))
110 }
111 Ok(None) => Ok(Delegation::default()),
112 Err(err) => Err(StateError::Unavailable(anyhow!(err))),
113 }
114 }
115
116 pub fn delegations(
118 &self,
119 ) -> Result<BTreeMap<Address, BTreeMap<Address, Delegation>>, StateError> {
120 let mut it = self.mkvs.iter();
121 it.seek(&DelegationKeyFmt::default().encode_partial(0));
122
123 let mut result: BTreeMap<Address, BTreeMap<Address, Delegation>> = BTreeMap::new();
124
125 while let Some((DelegationKeyFmt((escrow_addr, delegator_addr)), value)) = it
126 .next()
127 .and_then(|(key, value)| DelegationKeyFmt::decode(&key).zip(value.into()))
128 {
129 if !result.contains_key(&escrow_addr) {
130 result.insert(escrow_addr.clone(), BTreeMap::new());
131 }
132 let inner = result.get_mut(&escrow_addr).expect("inner map must exist");
133
134 inner.insert(
135 delegator_addr,
136 cbor::from_slice(&value).map_err(|err| StateError::Unavailable(anyhow!(err)))?,
137 );
138 }
139
140 Ok(result)
141 }
142
143 pub fn debonding_delegation(
145 &self,
146 delegator_addr: Address,
147 escrow_addr: Address,
148 epoch: EpochTime,
149 ) -> Result<DebondingDelegation, StateError> {
150 match self
151 .mkvs
152 .get(&DebondingDelegationKeyFmt((delegator_addr, escrow_addr, epoch)).encode())
153 {
154 Ok(Some(b)) => {
155 cbor::from_slice(&b).map_err(|err| StateError::Unavailable(anyhow!(err)))
156 }
157 Ok(None) => Ok(DebondingDelegation::default()),
158 Err(err) => Err(StateError::Unavailable(anyhow!(err))),
159 }
160 }
161
162 pub fn debonding_delegations(
164 &self,
165 ) -> Result<BTreeMap<Address, BTreeMap<Address, Vec<DebondingDelegation>>>, StateError> {
166 let mut it = self.mkvs.iter();
167 it.seek(&DebondingDelegationKeyFmt::default().encode_partial(0));
168
169 let mut result: BTreeMap<Address, BTreeMap<Address, Vec<DebondingDelegation>>> =
170 BTreeMap::new();
171
172 while let Some((DebondingDelegationKeyFmt((delegator_addr, escrow_addr, _)), value)) = it
173 .next()
174 .and_then(|(key, value)| DebondingDelegationKeyFmt::decode(&key).zip(value.into()))
175 {
176 if !result.contains_key(&escrow_addr) {
177 result.insert(escrow_addr.clone(), BTreeMap::new());
178 }
179 let inner = result.get_mut(&escrow_addr).expect("inner map must exist");
180 if !inner.contains_key(&delegator_addr) {
181 inner.insert(delegator_addr.clone(), Vec::new());
182 }
183 let in_inner = inner
184 .get_mut(&delegator_addr)
185 .expect("inner vec in inner map must exist");
186 in_inner.push(
187 cbor::from_slice(&value).map_err(|err| StateError::Unavailable(anyhow!(err)))?,
188 );
189 }
190
191 Ok(result)
192 }
193}
194
195#[cfg(test)]
196mod test {
197 use crate::{
198 common::crypto::{hash::Hash, signature::PublicKey},
199 consensus::staking::{EscrowAccount, GeneralAccount, SharePool},
200 storage::mkvs::{
201 interop::{Fixture, ProtocolServer},
202 Root, RootType, Tree,
203 },
204 };
205
206 use super::*;
207
208 #[test]
209 fn test_staking_state_interop() {
210 let server = ProtocolServer::new(Fixture::ConsensusMock.into());
218 let mock_consensus_root = Root {
219 version: 1,
220 root_type: RootType::State,
221 hash: Hash::from("8e39bf193f8a954ab8f8d7cb6388c591fd0785ea060bbd8e3752e266b54499d3"),
222 ..Default::default()
223 };
224 let mkvs = Tree::builder()
225 .with_capacity(100_000, 10_000_000)
226 .with_root(mock_consensus_root)
227 .build(server.read_sync());
228 let staking_state = ImmutableState::new(&mkvs);
229
230 let pk =
231 PublicKey::from("7e57baaad01fffffffffffffffffffffffffffffffffffffffffffffffffffff");
232 let pk2 =
233 PublicKey::from("7e57baaad02fffffffffffffffffffffffffffffffffffffffffffffffffffff");
234 let pk3 =
235 PublicKey::from("7e57baaad03fffffffffffffffffffffffffffffffffffffffffffffffffffff");
236 let expected_addrs = vec![
237 Address::from_pk(&pk),
238 Address::from_pk(&pk2),
239 Address::from_pk(&pk3),
240 ];
241
242 let addrs = staking_state
244 .addresses()
245 .expect("addresses query should work");
246 assert_eq!(expected_addrs, addrs, "expected addresses should match");
247
248 let mut accounts = Vec::new();
249 for addr in &addrs {
250 let acc = staking_state
251 .account(addr.clone())
252 .expect("accounts query should work");
253 accounts.push(acc);
254 }
255
256 let expected_accounts = vec![
257 Account {
258 general: GeneralAccount {
259 balance: Quantity::from(23u32),
260 nonce: 13,
261 ..Default::default()
262 },
263 escrow: EscrowAccount {
264 active: SharePool {
265 balance: Quantity::from(100u32),
266 total_shares: Quantity::from(10u32),
267 },
268 debonding: SharePool {
269 balance: Quantity::from(5u32),
270 total_shares: Quantity::from(5u32),
271 },
272 ..Default::default()
273 },
274 ..Default::default()
275 },
276 Account {
277 general: GeneralAccount {
278 balance: Quantity::from(23u32),
279 nonce: 1,
280 ..Default::default()
281 },
282 escrow: EscrowAccount {
283 active: SharePool {
284 balance: Quantity::from(500u32),
285 total_shares: Quantity::from(5u32),
286 },
287 ..Default::default()
288 },
289 ..Default::default()
290 },
291 Account {
292 general: GeneralAccount {
293 balance: Quantity::from(113u32),
294 nonce: 17,
295 ..Default::default()
296 },
297 escrow: EscrowAccount {
298 active: SharePool {
299 balance: Quantity::from(400u32),
300 total_shares: Quantity::from(35u32),
301 },
302 ..Default::default()
303 },
304 ..Default::default()
305 },
306 ];
307 assert_eq!(
308 expected_accounts, accounts,
309 "expected addresses should match"
310 );
311
312 let delegations = staking_state
314 .delegations()
315 .expect("delegations query should work");
316 for (escrow_addr, dels) in &delegations {
317 for (delegator_addr, del) in dels.clone() {
318 let d = staking_state
319 .delegation(delegator_addr, escrow_addr.clone())
320 .expect("delegation query should work");
321 assert_eq!(del, d, "delegation should match")
322 }
323 }
324
325 let mut expected_delegations: BTreeMap<Address, BTreeMap<Address, Delegation>> =
326 BTreeMap::new();
327 expected_delegations.insert(
329 addrs[0].clone(),
330 [
331 (
332 addrs[0].clone(),
333 Delegation {
334 shares: Quantity::from(5u32),
335 },
336 ),
337 (
338 addrs[1].clone(),
339 Delegation {
340 shares: Quantity::from(5u32),
341 },
342 ),
343 ]
344 .iter()
345 .cloned()
346 .collect(),
347 );
348 expected_delegations.insert(
350 addrs[1].clone(),
351 [(
352 addrs[2].clone(),
353 Delegation {
354 shares: Quantity::from(5u32),
355 },
356 )]
357 .iter()
358 .cloned()
359 .collect(),
360 );
361 expected_delegations.insert(
363 addrs[2].clone(),
364 [
365 (
366 addrs[0].clone(),
367 Delegation {
368 shares: Quantity::from(20u32),
369 },
370 ),
371 (
372 addrs[1].clone(),
373 Delegation {
374 shares: Quantity::from(6u32),
375 },
376 ),
377 (
378 addrs[2].clone(),
379 Delegation {
380 shares: Quantity::from(10u32),
381 },
382 ),
383 ]
384 .iter()
385 .cloned()
386 .collect(),
387 );
388 assert_eq!(
389 expected_delegations, delegations,
390 "expected delegations should match"
391 );
392
393 let debonding_delegations = staking_state
395 .debonding_delegations()
396 .expect("debonding delegations query should work");
397 for (escrow_addr, debss) in &debonding_delegations {
398 for (delegator_addr, debs) in debss {
399 for deb in debs {
400 let d = staking_state
401 .debonding_delegation(
402 delegator_addr.clone(),
403 escrow_addr.clone(),
404 deb.debond_end_time,
405 )
406 .expect("debonding delegation query should work");
407 assert_eq!(deb.clone(), d, "debonding delegation should match")
408 }
409 }
410 }
411 let mut expected_debonding: BTreeMap<Address, BTreeMap<Address, Vec<DebondingDelegation>>> =
412 BTreeMap::new();
413 expected_debonding.insert(
415 addrs[0].clone(),
416 [
417 (
418 addrs[0].clone(),
419 vec![DebondingDelegation {
420 shares: Quantity::from(1u32),
421 debond_end_time: 33,
422 }],
423 ),
424 (
425 addrs[1].clone(),
426 vec![
427 DebondingDelegation {
428 shares: Quantity::from(1u32),
429 debond_end_time: 15,
430 },
431 DebondingDelegation {
432 shares: Quantity::from(1u32),
433 debond_end_time: 21,
434 },
435 ],
436 ),
437 (
438 addrs[2].clone(),
439 vec![DebondingDelegation {
440 shares: Quantity::from(2u32),
441 debond_end_time: 100,
442 }],
443 ),
444 ]
445 .iter()
446 .cloned()
447 .collect(),
448 );
449 assert_eq!(
450 expected_debonding, debonding_delegations,
451 "expected debonding delegations should match"
452 );
453
454 let total_supply = staking_state
456 .total_supply()
457 .expect("total supply query should work");
458 assert_eq!(
459 Quantity::from(10000u32),
460 total_supply,
461 "total supply should match expected"
462 );
463
464 let common_pool = staking_state
465 .common_pool()
466 .expect("common pool query should work");
467 assert_eq!(
468 Quantity::from(1000u32),
469 common_pool,
470 "common pool should match expected"
471 );
472
473 let last_block_fees = staking_state
474 .last_block_fees()
475 .expect("last block fees query should work");
476 assert_eq!(
477 Quantity::from(33u32),
478 last_block_fees,
479 "last block fees should match expected"
480 );
481
482 let governance_deposits = staking_state
483 .governance_deposits()
484 .expect("governance deposits query should work");
485 assert_eq!(
486 Quantity::from(12u32),
487 governance_deposits,
488 "governance deposits should match expected"
489 );
490 }
491}