1use super::storage;
5use crate::constants::{
6 MIXNODE_BOND_DEFAULT_RETRIEVAL_LIMIT, MIXNODE_BOND_MAX_RETRIEVAL_LIMIT,
7 MIXNODE_DETAILS_DEFAULT_RETRIEVAL_LIMIT, MIXNODE_DETAILS_MAX_RETRIEVAL_LIMIT,
8 UNBONDED_MIXNODES_DEFAULT_RETRIEVAL_LIMIT, UNBONDED_MIXNODES_MAX_RETRIEVAL_LIMIT,
9};
10use crate::mixnodes::helpers::{
11 attach_mix_details, get_mixnode_details_by_id, get_mixnode_details_by_identity,
12 get_mixnode_details_by_owner,
13};
14use crate::rewards::storage as rewards_storage;
15use cosmwasm_std::{Deps, Order, StdResult, Storage};
16use cw_storage_plus::Bound;
17use mixnet_contract_common::mixnode::{
18 MixNodeBond, MixNodeDetails, MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse,
19 PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse,
20};
21use mixnet_contract_common::{
22 IdentityKey, LayerDistribution, MixId, MixOwnershipResponse, MixnodeDetailsResponse,
23 PagedMixnodeBondsResponse,
24};
25
26pub fn query_mixnode_bonds_paged(
27 deps: Deps<'_>,
28 start_after: Option<MixId>,
29 limit: Option<u32>,
30) -> StdResult<PagedMixnodeBondsResponse> {
31 let limit = limit
32 .unwrap_or(MIXNODE_BOND_DEFAULT_RETRIEVAL_LIMIT)
33 .min(MIXNODE_BOND_MAX_RETRIEVAL_LIMIT) as usize;
34
35 let start = start_after.map(Bound::exclusive);
36
37 let nodes = storage::mixnode_bonds()
38 .range(deps.storage, start, None, Order::Ascending)
39 .take(limit)
40 .map(|res| res.map(|item| item.1))
41 .collect::<StdResult<Vec<MixNodeBond>>>()?;
42
43 let start_next_after = nodes.last().map(|node| node.mix_id);
44
45 Ok(PagedMixnodeBondsResponse::new(
46 nodes,
47 limit,
48 start_next_after,
49 ))
50}
51
52fn attach_node_details(
53 storage: &dyn Storage,
54 read_bond: StdResult<(MixId, MixNodeBond)>,
55) -> StdResult<MixNodeDetails> {
56 match read_bond {
57 Ok((_, bond)) => attach_mix_details(storage, bond),
58 Err(err) => Err(err),
59 }
60}
61
62pub fn query_mixnodes_details_paged(
63 deps: Deps<'_>,
64 start_after: Option<MixId>,
65 limit: Option<u32>,
66) -> StdResult<PagedMixnodesDetailsResponse> {
67 let limit = limit
68 .unwrap_or(MIXNODE_DETAILS_DEFAULT_RETRIEVAL_LIMIT)
69 .min(MIXNODE_DETAILS_MAX_RETRIEVAL_LIMIT) as usize;
70
71 let start = start_after.map(Bound::exclusive);
72
73 let nodes = storage::mixnode_bonds()
74 .range(deps.storage, start, None, Order::Ascending)
75 .take(limit)
76 .map(|res| attach_node_details(deps.storage, res))
77 .collect::<StdResult<Vec<MixNodeDetails>>>()?;
78
79 let start_next_after = nodes.last().map(|details| details.mix_id());
80
81 Ok(PagedMixnodesDetailsResponse::new(
82 nodes,
83 limit,
84 start_next_after,
85 ))
86}
87
88pub fn query_unbonded_mixnodes_paged(
89 deps: Deps<'_>,
90 start_after: Option<MixId>,
91 limit: Option<u32>,
92) -> StdResult<PagedUnbondedMixnodesResponse> {
93 let limit = limit
94 .unwrap_or(UNBONDED_MIXNODES_DEFAULT_RETRIEVAL_LIMIT)
95 .min(UNBONDED_MIXNODES_MAX_RETRIEVAL_LIMIT) as usize;
96
97 let start = start_after.map(Bound::exclusive);
98
99 let nodes = storage::unbonded_mixnodes()
100 .range(deps.storage, start, None, Order::Ascending)
101 .take(limit)
102 .collect::<StdResult<Vec<_>>>()?;
103
104 let start_next_after = nodes.last().map(|res| res.0);
105
106 Ok(PagedUnbondedMixnodesResponse::new(
107 nodes,
108 limit,
109 start_next_after,
110 ))
111}
112
113pub fn query_unbonded_mixnodes_by_owner_paged(
114 deps: Deps<'_>,
115 owner: String,
116 start_after: Option<MixId>,
117 limit: Option<u32>,
118) -> StdResult<PagedUnbondedMixnodesResponse> {
119 let owner = deps.api.addr_validate(&owner)?;
120
121 let limit = limit
122 .unwrap_or(UNBONDED_MIXNODES_DEFAULT_RETRIEVAL_LIMIT)
123 .min(UNBONDED_MIXNODES_MAX_RETRIEVAL_LIMIT) as usize;
124
125 let start = start_after.map(Bound::exclusive);
126
127 let nodes = storage::unbonded_mixnodes()
128 .idx
129 .owner
130 .prefix(owner)
131 .range(deps.storage, start, None, Order::Ascending)
132 .take(limit)
133 .collect::<StdResult<Vec<_>>>()?;
134
135 let start_next_after = nodes.last().map(|res| res.0);
136
137 Ok(PagedUnbondedMixnodesResponse::new(
138 nodes,
139 limit,
140 start_next_after,
141 ))
142}
143
144pub fn query_unbonded_mixnodes_by_identity_paged(
145 deps: Deps<'_>,
146 identity_key: String,
147 start_after: Option<MixId>,
148 limit: Option<u32>,
149) -> StdResult<PagedUnbondedMixnodesResponse> {
150 let limit = limit
151 .unwrap_or(UNBONDED_MIXNODES_DEFAULT_RETRIEVAL_LIMIT)
152 .min(UNBONDED_MIXNODES_MAX_RETRIEVAL_LIMIT) as usize;
153
154 let start = start_after.map(Bound::exclusive);
155
156 let nodes = storage::unbonded_mixnodes()
157 .idx
158 .identity_key
159 .prefix(identity_key)
160 .range(deps.storage, start, None, Order::Ascending)
161 .take(limit)
162 .collect::<StdResult<Vec<_>>>()?;
163
164 let start_next_after = nodes.last().map(|res| res.0);
165
166 Ok(PagedUnbondedMixnodesResponse::new(
167 nodes,
168 limit,
169 start_next_after,
170 ))
171}
172
173pub fn query_owned_mixnode(deps: Deps<'_>, address: String) -> StdResult<MixOwnershipResponse> {
174 let validated_addr = deps.api.addr_validate(&address)?;
175 let mixnode_details = get_mixnode_details_by_owner(deps.storage, validated_addr.clone())?;
176
177 Ok(MixOwnershipResponse {
178 address: validated_addr,
179 mixnode_details,
180 })
181}
182
183pub fn query_mixnode_details(deps: Deps<'_>, mix_id: MixId) -> StdResult<MixnodeDetailsResponse> {
184 let mixnode_details = get_mixnode_details_by_id(deps.storage, mix_id)?;
185
186 Ok(MixnodeDetailsResponse {
187 mix_id,
188 mixnode_details,
189 })
190}
191
192pub fn query_mixnode_details_by_identity(
194 deps: Deps<'_>,
195 mix_identity: IdentityKey,
196) -> StdResult<Option<MixNodeDetails>> {
197 get_mixnode_details_by_identity(deps.storage, mix_identity)
198}
199
200pub fn query_mixnode_rewarding_details(
201 deps: Deps<'_>,
202 mix_id: MixId,
203) -> StdResult<MixnodeRewardingDetailsResponse> {
204 let rewarding_details = rewards_storage::MIXNODE_REWARDING.may_load(deps.storage, mix_id)?;
205
206 Ok(MixnodeRewardingDetailsResponse {
207 mix_id,
208 rewarding_details,
209 })
210}
211
212pub fn query_unbonded_mixnode(deps: Deps<'_>, mix_id: MixId) -> StdResult<UnbondedMixnodeResponse> {
213 let unbonded_info = storage::unbonded_mixnodes().may_load(deps.storage, mix_id)?;
214
215 Ok(UnbondedMixnodeResponse {
216 mix_id,
217 unbonded_info,
218 })
219}
220
221pub fn query_stake_saturation(deps: Deps<'_>, mix_id: MixId) -> StdResult<StakeSaturationResponse> {
222 let mix_rewarding = match rewards_storage::MIXNODE_REWARDING.may_load(deps.storage, mix_id)? {
223 Some(mix_rewarding) => mix_rewarding,
224 None => {
225 return Ok(StakeSaturationResponse {
226 mix_id,
227 current_saturation: None,
228 uncapped_saturation: None,
229 })
230 }
231 };
232
233 let rewarding_params = rewards_storage::REWARDING_PARAMS.load(deps.storage)?;
234
235 Ok(StakeSaturationResponse {
236 mix_id,
237 current_saturation: Some(mix_rewarding.bond_saturation(&rewarding_params)),
238 uncapped_saturation: Some(mix_rewarding.uncapped_bond_saturation(&rewarding_params)),
239 })
240}
241
242pub(crate) fn query_layer_distribution(deps: Deps<'_>) -> StdResult<LayerDistribution> {
243 storage::LAYERS.load(deps.storage)
244}
245
246#[cfg(test)]
247pub(crate) mod tests {
248 use super::*;
249 use crate::interval::pending_events;
250 use crate::support::tests::fixtures::good_mixnode_pledge;
251 use crate::support::tests::test_helpers::TestSetup;
252 use crate::support::tests::{fixtures, test_helpers};
253 use cosmwasm_std::testing::mock_env;
254 use cosmwasm_std::Decimal;
255
256 #[cfg(test)]
257 mod mixnode_bonds {
258 use super::*;
259
260 #[test]
261 fn obeys_limits() {
262 let mut test = TestSetup::new();
263 test.add_dummy_mixnodes(1000);
264 let limit = 2;
265
266 let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(limit)).unwrap();
267 assert_eq!(limit, page1.nodes.len() as u32);
268 }
269
270 #[test]
271 fn has_default_limit() {
272 let mut test = TestSetup::new();
273 test.add_dummy_mixnodes(1000);
274
275 let page1 = query_mixnode_bonds_paged(test.deps(), None, None).unwrap();
277
278 assert_eq!(
279 MIXNODE_BOND_DEFAULT_RETRIEVAL_LIMIT,
280 page1.nodes.len() as u32
281 );
282 }
283
284 #[test]
285 fn has_max_limit() {
286 let mut test = TestSetup::new();
287 test.add_dummy_mixnodes(1000);
288
289 let crazy_limit = 1000;
291 let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(crazy_limit)).unwrap();
292
293 assert_eq!(MIXNODE_BOND_MAX_RETRIEVAL_LIMIT, page1.nodes.len() as u32);
295 }
296
297 #[test]
298 fn pagination_works() {
299 let mut test = TestSetup::new();
301
302 test.add_dummy_mixnode("addr1", None);
303
304 let per_page = 2;
305 let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
306
307 assert_eq!(1, page1.nodes.len());
309
310 test.add_dummy_mixnode("addr2", None);
312
313 let page1 = query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
315 assert_eq!(2, page1.nodes.len());
316
317 test.add_dummy_mixnode("addr3", None);
318
319 let another_page1 =
321 query_mixnode_bonds_paged(test.deps(), None, Some(per_page)).unwrap();
322 assert_eq!(2, another_page1.nodes.len());
323 assert_eq!(page1, another_page1);
324
325 let start_after = page1.start_next_after.unwrap();
327 let page2 =
328 query_mixnode_bonds_paged(test.deps(), Some(start_after), Some(per_page)).unwrap();
329
330 assert_eq!(1, page2.nodes.len());
331
332 test.add_dummy_mixnode("addr4", None);
334
335 let page2 =
336 query_mixnode_bonds_paged(test.deps(), Some(start_after), Some(per_page)).unwrap();
337
338 assert_eq!(2, page2.nodes.len());
340 }
341 }
342
343 #[cfg(test)]
344 mod mixnode_details {
345 use super::*;
346
347 #[test]
348 fn obeys_limits() {
349 let mut test = TestSetup::new();
350 test.add_dummy_mixnodes(1000);
351 let limit = 2;
352
353 let page1 = query_mixnodes_details_paged(test.deps(), None, Some(limit)).unwrap();
354 assert_eq!(limit, page1.nodes.len() as u32);
355 }
356
357 #[test]
358 fn has_default_limit() {
359 let mut test = TestSetup::new();
360 test.add_dummy_mixnodes(1000);
361
362 let page1 = query_mixnodes_details_paged(test.deps(), None, None).unwrap();
364
365 assert_eq!(
366 MIXNODE_DETAILS_DEFAULT_RETRIEVAL_LIMIT,
367 page1.nodes.len() as u32
368 );
369 }
370
371 #[test]
372 fn has_max_limit() {
373 let mut test = TestSetup::new();
374 test.add_dummy_mixnodes(1000);
375
376 let crazy_limit = 1000;
378 let page1 = query_mixnodes_details_paged(test.deps(), None, Some(crazy_limit)).unwrap();
379
380 assert_eq!(
382 MIXNODE_DETAILS_MAX_RETRIEVAL_LIMIT,
383 page1.nodes.len() as u32
384 );
385 }
386
387 #[test]
388 fn pagination_works() {
389 let mut test = TestSetup::new();
391
392 test.add_dummy_mixnode("addr1", None);
393
394 let per_page = 2;
395 let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
396
397 assert_eq!(1, page1.nodes.len());
399
400 test.add_dummy_mixnode("addr2", None);
402
403 let page1 = query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
405 assert_eq!(2, page1.nodes.len());
406
407 test.add_dummy_mixnode("addr3", None);
408
409 let another_page1 =
411 query_mixnodes_details_paged(test.deps(), None, Some(per_page)).unwrap();
412 assert_eq!(2, another_page1.nodes.len());
413 assert_eq!(page1, another_page1);
414
415 let start_after = page1.start_next_after.unwrap();
417 let page2 =
418 query_mixnodes_details_paged(test.deps(), Some(start_after), Some(per_page))
419 .unwrap();
420
421 assert_eq!(1, page2.nodes.len());
422
423 test.add_dummy_mixnode("addr4", None);
425
426 let page2 =
427 query_mixnodes_details_paged(test.deps(), Some(start_after), Some(per_page))
428 .unwrap();
429
430 assert_eq!(2, page2.nodes.len());
432 }
433 }
434
435 #[cfg(test)]
436 mod unbonded_mixnodes {
437 use super::*;
438 use cosmwasm_std::Addr;
439 use mixnet_contract_common::mixnode::UnbondedMixnode;
440
441 #[test]
442 fn obeys_limits() {
443 let mut deps = test_helpers::init_contract();
444 let _env = mock_env();
445 let rng = test_helpers::test_rng();
446 let limit = 2;
447
448 test_helpers::add_dummy_unbonded_mixnodes(rng, deps.as_mut(), 1000);
449 let page1 = query_unbonded_mixnodes_paged(deps.as_ref(), None, Some(limit)).unwrap();
450 assert_eq!(limit, page1.nodes.len() as u32);
451 }
452
453 #[test]
454 fn has_default_limit() {
455 let mut deps = test_helpers::init_contract();
456 let _env = mock_env();
457 let rng = test_helpers::test_rng();
458 test_helpers::add_dummy_unbonded_mixnodes(rng, deps.as_mut(), 1000);
459
460 let page1 = query_unbonded_mixnodes_paged(deps.as_ref(), None, None).unwrap();
462
463 assert_eq!(
464 UNBONDED_MIXNODES_DEFAULT_RETRIEVAL_LIMIT,
465 page1.nodes.len() as u32
466 );
467 }
468
469 #[test]
470 fn has_max_limit() {
471 let mut deps = test_helpers::init_contract();
472 let _env = mock_env();
473 let rng = test_helpers::test_rng();
474 test_helpers::add_dummy_unbonded_mixnodes(rng, deps.as_mut(), 1000);
475
476 let crazy_limit = 1000;
478 let page1 =
479 query_unbonded_mixnodes_paged(deps.as_ref(), None, Some(crazy_limit)).unwrap();
480
481 assert_eq!(
483 UNBONDED_MIXNODES_MAX_RETRIEVAL_LIMIT,
484 page1.nodes.len() as u32
485 );
486 }
487
488 #[test]
489 fn pagination_works() {
490 fn add_unbonded(storage: &mut dyn Storage, id: MixId) {
491 storage::unbonded_mixnodes()
492 .save(
493 storage,
494 id,
495 &UnbondedMixnode {
496 identity_key: format!("dummy{}", id),
497 owner: Addr::unchecked(format!("dummy{}", id)),
498 proxy: None,
499 unbonding_height: 123,
500 },
501 )
502 .unwrap();
503 }
504
505 let mut deps = test_helpers::init_contract();
507
508 add_unbonded(deps.as_mut().storage, 1);
509
510 let per_page = 2;
511 let page1 = query_unbonded_mixnodes_paged(deps.as_ref(), None, Some(per_page)).unwrap();
512
513 assert_eq!(1, page1.nodes.len());
515
516 add_unbonded(deps.as_mut().storage, 2);
518
519 let page1 = query_unbonded_mixnodes_paged(deps.as_ref(), None, Some(per_page)).unwrap();
521 assert_eq!(2, page1.nodes.len());
522
523 add_unbonded(deps.as_mut().storage, 3);
524
525 let another_page1 =
527 query_unbonded_mixnodes_paged(deps.as_ref(), None, Some(per_page)).unwrap();
528 assert_eq!(2, another_page1.nodes.len());
529 assert_eq!(page1, another_page1);
530
531 let start_after = page1.start_next_after.unwrap();
533 let page2 =
534 query_unbonded_mixnodes_paged(deps.as_ref(), Some(start_after), Some(per_page))
535 .unwrap();
536
537 assert_eq!(1, page2.nodes.len());
538
539 add_unbonded(deps.as_mut().storage, 4);
541 let page2 =
542 query_unbonded_mixnodes_paged(deps.as_ref(), Some(start_after), Some(per_page))
543 .unwrap();
544
545 assert_eq!(2, page2.nodes.len());
547 }
548 }
549
550 #[cfg(test)]
551 mod unbonded_mixnodes_by_owner {
552 use super::*;
553 use cosmwasm_std::Addr;
554 use mixnet_contract_common::mixnode::UnbondedMixnode;
555
556 fn add_unbonded_with_owner(storage: &mut dyn Storage, id: MixId, owner: &str) {
557 storage::unbonded_mixnodes()
558 .save(
559 storage,
560 id,
561 &UnbondedMixnode {
562 identity_key: format!("dummy{}", id),
563 owner: Addr::unchecked(owner),
564 proxy: None,
565 unbonding_height: 123,
566 },
567 )
568 .unwrap();
569 }
570
571 #[test]
572 fn obeys_limits() {
573 let mut deps = test_helpers::init_contract();
574 let _env = mock_env();
575 let rng = test_helpers::test_rng();
576 let limit = 2;
577 let owner = "owner";
578
579 test_helpers::add_dummy_unbonded_mixnodes_with_owner(rng, deps.as_mut(), owner, 1000);
580 let page1 = query_unbonded_mixnodes_by_owner_paged(
581 deps.as_ref(),
582 owner.into(),
583 None,
584 Some(limit),
585 )
586 .unwrap();
587 assert_eq!(limit, page1.nodes.len() as u32);
588 }
589
590 #[test]
591 fn has_default_limit() {
592 let mut deps = test_helpers::init_contract();
593 let _env = mock_env();
594 let rng = test_helpers::test_rng();
595 let owner = "owner";
596
597 test_helpers::add_dummy_unbonded_mixnodes_with_owner(rng, deps.as_mut(), owner, 1000);
598
599 let page1 =
601 query_unbonded_mixnodes_by_owner_paged(deps.as_ref(), owner.into(), None, None)
602 .unwrap();
603
604 assert_eq!(
605 UNBONDED_MIXNODES_DEFAULT_RETRIEVAL_LIMIT,
606 page1.nodes.len() as u32
607 );
608 }
609
610 #[test]
611 fn has_max_limit() {
612 let mut deps = test_helpers::init_contract();
613 let _env = mock_env();
614 let rng = test_helpers::test_rng();
615 let owner = "owner";
616
617 test_helpers::add_dummy_unbonded_mixnodes_with_owner(rng, deps.as_mut(), owner, 1000);
618
619 let crazy_limit = 1000;
621 let page1 = query_unbonded_mixnodes_by_owner_paged(
622 deps.as_ref(),
623 owner.into(),
624 None,
625 Some(crazy_limit),
626 )
627 .unwrap();
628
629 assert_eq!(
631 UNBONDED_MIXNODES_MAX_RETRIEVAL_LIMIT,
632 page1.nodes.len() as u32
633 );
634 }
635
636 #[test]
637 fn pagination_works() {
638 let mut deps = test_helpers::init_contract();
640 let owner = "owner";
641 add_unbonded_with_owner(deps.as_mut().storage, 1, owner);
642
643 let per_page = 2;
644 let page1 = query_unbonded_mixnodes_by_owner_paged(
645 deps.as_ref(),
646 owner.into(),
647 None,
648 Some(per_page),
649 )
650 .unwrap();
651
652 assert_eq!(1, page1.nodes.len());
654
655 add_unbonded_with_owner(deps.as_mut().storage, 2, owner);
657
658 let page1 = query_unbonded_mixnodes_by_owner_paged(
660 deps.as_ref(),
661 owner.into(),
662 None,
663 Some(per_page),
664 )
665 .unwrap();
666 assert_eq!(2, page1.nodes.len());
667
668 add_unbonded_with_owner(deps.as_mut().storage, 3, owner);
669
670 let another_page1 = query_unbonded_mixnodes_by_owner_paged(
672 deps.as_ref(),
673 owner.into(),
674 None,
675 Some(per_page),
676 )
677 .unwrap();
678 assert_eq!(2, another_page1.nodes.len());
679 assert_eq!(page1, another_page1);
680
681 let start_after = page1.start_next_after.unwrap();
683 let page2 = query_unbonded_mixnodes_by_owner_paged(
684 deps.as_ref(),
685 owner.into(),
686 Some(start_after),
687 Some(per_page),
688 )
689 .unwrap();
690
691 assert_eq!(1, page2.nodes.len());
692
693 add_unbonded_with_owner(deps.as_mut().storage, 4, owner);
695 let page2 = query_unbonded_mixnodes_by_owner_paged(
696 deps.as_ref(),
697 owner.into(),
698 Some(start_after),
699 Some(per_page),
700 )
701 .unwrap();
702
703 assert_eq!(2, page2.nodes.len());
705 }
706
707 #[test]
708 fn only_retrieves_nodes_with_specific_owner() {
709 let mut deps = test_helpers::init_contract();
710 let owner1 = "owner1";
711 let owner2 = "owner2";
712 let owner3 = "owner3";
713 let owner4 = "owner4";
714
715 add_unbonded_with_owner(deps.as_mut().storage, 1, owner1);
716 add_unbonded_with_owner(deps.as_mut().storage, 2, owner1);
717 add_unbonded_with_owner(deps.as_mut().storage, 3, owner2);
718 add_unbonded_with_owner(deps.as_mut().storage, 4, owner1);
719 add_unbonded_with_owner(deps.as_mut().storage, 5, owner3);
720 add_unbonded_with_owner(deps.as_mut().storage, 6, owner3);
721 add_unbonded_with_owner(deps.as_mut().storage, 7, owner4);
722 add_unbonded_with_owner(deps.as_mut().storage, 8, owner2);
723 add_unbonded_with_owner(deps.as_mut().storage, 9, owner1);
724 add_unbonded_with_owner(deps.as_mut().storage, 10, owner3);
725
726 let expected_ids1 = vec![1, 2, 4, 9];
727 let expected_ids2 = vec![3, 8];
728 let expected_ids3 = vec![5, 6, 10];
729 let expected_ids4 = vec![7];
730
731 let res1 =
732 query_unbonded_mixnodes_by_owner_paged(deps.as_ref(), owner1.into(), None, None)
733 .unwrap()
734 .nodes
735 .into_iter()
736 .map(|r| r.0)
737 .collect::<Vec<_>>();
738 assert_eq!(res1, expected_ids1);
739
740 let res2 =
741 query_unbonded_mixnodes_by_owner_paged(deps.as_ref(), owner2.into(), None, None)
742 .unwrap()
743 .nodes
744 .into_iter()
745 .map(|r| r.0)
746 .collect::<Vec<_>>();
747 assert_eq!(res2, expected_ids2);
748
749 let res3 =
750 query_unbonded_mixnodes_by_owner_paged(deps.as_ref(), owner3.into(), None, None)
751 .unwrap()
752 .nodes
753 .into_iter()
754 .map(|r| r.0)
755 .collect::<Vec<_>>();
756 assert_eq!(res3, expected_ids3);
757
758 let res4 =
759 query_unbonded_mixnodes_by_owner_paged(deps.as_ref(), owner4.into(), None, None)
760 .unwrap()
761 .nodes
762 .into_iter()
763 .map(|r| r.0)
764 .collect::<Vec<_>>();
765 assert_eq!(res4, expected_ids4);
766
767 let res5 = query_unbonded_mixnodes_by_owner_paged(
768 deps.as_ref(),
769 "doesnt-exist".into(),
770 None,
771 None,
772 )
773 .unwrap()
774 .nodes
775 .into_iter()
776 .map(|r| r.0)
777 .collect::<Vec<_>>();
778 assert!(res5.is_empty());
779 }
780 }
781
782 #[cfg(test)]
783 mod unbonded_mixnodes_by_identity {
784 use super::*;
785 use cosmwasm_std::Addr;
786 use mixnet_contract_common::mixnode::UnbondedMixnode;
787
788 fn add_unbonded_with_identity(storage: &mut dyn Storage, id: MixId, identity: &str) {
789 storage::unbonded_mixnodes()
790 .save(
791 storage,
792 id,
793 &UnbondedMixnode {
794 identity_key: identity.to_string(),
795 owner: Addr::unchecked(format!("dummy{}", id)),
796 proxy: None,
797 unbonding_height: 123,
798 },
799 )
800 .unwrap();
801 }
802
803 #[test]
804 fn obeys_limits() {
805 let mut deps = test_helpers::init_contract();
806 let _env = mock_env();
807 let rng = test_helpers::test_rng();
808 let limit = 2;
809 let identity = "foomp123";
810
811 test_helpers::add_dummy_unbonded_mixnodes_with_identity(
812 rng,
813 deps.as_mut(),
814 identity,
815 1000,
816 );
817 let page1 = query_unbonded_mixnodes_by_identity_paged(
818 deps.as_ref(),
819 identity.into(),
820 None,
821 Some(limit),
822 )
823 .unwrap();
824 assert_eq!(limit, page1.nodes.len() as u32);
825 }
826
827 #[test]
828 fn has_default_limit() {
829 let mut deps = test_helpers::init_contract();
830 let _env = mock_env();
831 let rng = test_helpers::test_rng();
832 let identity = "foomp123";
833 test_helpers::add_dummy_unbonded_mixnodes_with_identity(
834 rng,
835 deps.as_mut(),
836 identity,
837 1000,
838 );
839
840 let page1 = query_unbonded_mixnodes_by_identity_paged(
842 deps.as_ref(),
843 identity.into(),
844 None,
845 None,
846 )
847 .unwrap();
848
849 assert_eq!(
850 UNBONDED_MIXNODES_DEFAULT_RETRIEVAL_LIMIT,
851 page1.nodes.len() as u32
852 );
853 }
854
855 #[test]
856 fn has_max_limit() {
857 let mut deps = test_helpers::init_contract();
858 let _env = mock_env();
859 let rng = test_helpers::test_rng();
860 let identity = "foomp123";
861 test_helpers::add_dummy_unbonded_mixnodes_with_identity(
862 rng,
863 deps.as_mut(),
864 identity,
865 1000,
866 );
867
868 let crazy_limit = 1000;
870 let page1 = query_unbonded_mixnodes_by_identity_paged(
871 deps.as_ref(),
872 identity.into(),
873 None,
874 Some(crazy_limit),
875 )
876 .unwrap();
877
878 assert_eq!(
880 UNBONDED_MIXNODES_MAX_RETRIEVAL_LIMIT,
881 page1.nodes.len() as u32
882 );
883 }
884
885 #[test]
886 fn pagination_works() {
887 let mut deps = test_helpers::init_contract();
889 let identity = "foomp123";
890
891 add_unbonded_with_identity(deps.as_mut().storage, 1, identity);
892
893 let per_page = 2;
894 let page1 = query_unbonded_mixnodes_by_identity_paged(
895 deps.as_ref(),
896 identity.into(),
897 None,
898 Some(per_page),
899 )
900 .unwrap();
901
902 assert_eq!(1, page1.nodes.len());
904
905 add_unbonded_with_identity(deps.as_mut().storage, 2, identity);
907
908 let page1 = query_unbonded_mixnodes_by_identity_paged(
910 deps.as_ref(),
911 identity.into(),
912 None,
913 Some(per_page),
914 )
915 .unwrap();
916 assert_eq!(2, page1.nodes.len());
917
918 add_unbonded_with_identity(deps.as_mut().storage, 3, identity);
919
920 let another_page1 = query_unbonded_mixnodes_by_identity_paged(
922 deps.as_ref(),
923 identity.into(),
924 None,
925 Some(per_page),
926 )
927 .unwrap();
928 assert_eq!(2, another_page1.nodes.len());
929 assert_eq!(page1, another_page1);
930
931 let start_after = page1.start_next_after.unwrap();
933 let page2 = query_unbonded_mixnodes_by_identity_paged(
934 deps.as_ref(),
935 identity.into(),
936 Some(start_after),
937 Some(per_page),
938 )
939 .unwrap();
940
941 assert_eq!(1, page2.nodes.len());
942
943 add_unbonded_with_identity(deps.as_mut().storage, 4, identity);
945 let page2 = query_unbonded_mixnodes_by_identity_paged(
946 deps.as_ref(),
947 identity.into(),
948 Some(start_after),
949 Some(per_page),
950 )
951 .unwrap();
952
953 assert_eq!(2, page2.nodes.len());
955 }
956
957 #[test]
958 fn only_retrieves_nodes_with_specific_identity_key() {
959 let mut deps = test_helpers::init_contract();
960 let identity1 = "identity1";
961 let identity2 = "identity2";
962 let identity3 = "identity3";
963 let identity4 = "identity4";
964
965 add_unbonded_with_identity(deps.as_mut().storage, 1, identity1);
966 add_unbonded_with_identity(deps.as_mut().storage, 2, identity1);
967 add_unbonded_with_identity(deps.as_mut().storage, 3, identity2);
968 add_unbonded_with_identity(deps.as_mut().storage, 4, identity1);
969 add_unbonded_with_identity(deps.as_mut().storage, 5, identity3);
970 add_unbonded_with_identity(deps.as_mut().storage, 6, identity3);
971 add_unbonded_with_identity(deps.as_mut().storage, 7, identity4);
972 add_unbonded_with_identity(deps.as_mut().storage, 8, identity2);
973 add_unbonded_with_identity(deps.as_mut().storage, 9, identity1);
974 add_unbonded_with_identity(deps.as_mut().storage, 10, identity3);
975
976 let expected_ids1 = vec![1, 2, 4, 9];
977 let expected_ids2 = vec![3, 8];
978 let expected_ids3 = vec![5, 6, 10];
979 let expected_ids4 = vec![7];
980
981 let res1 = query_unbonded_mixnodes_by_identity_paged(
982 deps.as_ref(),
983 identity1.into(),
984 None,
985 None,
986 )
987 .unwrap()
988 .nodes
989 .into_iter()
990 .map(|r| r.0)
991 .collect::<Vec<_>>();
992 assert_eq!(res1, expected_ids1);
993
994 let res2 = query_unbonded_mixnodes_by_identity_paged(
995 deps.as_ref(),
996 identity2.into(),
997 None,
998 None,
999 )
1000 .unwrap()
1001 .nodes
1002 .into_iter()
1003 .map(|r| r.0)
1004 .collect::<Vec<_>>();
1005 assert_eq!(res2, expected_ids2);
1006
1007 let res3 = query_unbonded_mixnodes_by_identity_paged(
1008 deps.as_ref(),
1009 identity3.into(),
1010 None,
1011 None,
1012 )
1013 .unwrap()
1014 .nodes
1015 .into_iter()
1016 .map(|r| r.0)
1017 .collect::<Vec<_>>();
1018 assert_eq!(res3, expected_ids3);
1019
1020 let res4 = query_unbonded_mixnodes_by_identity_paged(
1021 deps.as_ref(),
1022 identity4.into(),
1023 None,
1024 None,
1025 )
1026 .unwrap()
1027 .nodes
1028 .into_iter()
1029 .map(|r| r.0)
1030 .collect::<Vec<_>>();
1031 assert_eq!(res4, expected_ids4);
1032
1033 let res5 = query_unbonded_mixnodes_by_owner_paged(
1034 deps.as_ref(),
1035 "doesnt-exist".into(),
1036 None,
1037 None,
1038 )
1039 .unwrap()
1040 .nodes
1041 .into_iter()
1042 .map(|r| r.0)
1043 .collect::<Vec<_>>();
1044 assert!(res5.is_empty());
1045 }
1046 }
1047
1048 #[test]
1049 fn query_for_owned_mixnode() {
1050 let mut test = TestSetup::new();
1051
1052 let address = "mix-owner".to_string();
1053
1054 let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
1056 assert!(res.mixnode_details.is_none());
1057 assert_eq!(address, res.address);
1058
1059 let id = test.add_dummy_mixnode(&address, None);
1061 let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
1062 let details = res.mixnode_details.unwrap();
1063 assert_eq!(address, details.bond_information.owner);
1064 assert_eq!(
1065 good_mixnode_pledge()[0],
1066 details.bond_information.original_pledge
1067 );
1068 assert_eq!(address, res.address);
1069
1070 let mut rewarding_details = details.rewarding_details;
1075 rewarding_details.delegates = Decimal::raw(12345);
1076 rewarding_details.unique_delegations = 1;
1077 rewards_storage::MIXNODE_REWARDING
1078 .save(test.deps_mut().storage, id, &rewarding_details)
1079 .unwrap();
1080
1081 pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, id).unwrap();
1082 let res = query_owned_mixnode(test.deps(), address.clone()).unwrap();
1083 assert!(res.mixnode_details.is_none());
1084 assert_eq!(address, res.address);
1085 }
1086
1087 #[test]
1088 fn query_for_mixnode_details() {
1089 let mut test = TestSetup::new();
1090
1091 let res = query_mixnode_details(test.deps(), 42).unwrap();
1093 assert!(res.mixnode_details.is_none());
1094 assert_eq!(42, res.mix_id);
1095
1096 let mix_id = test.add_dummy_mixnode("foomp", None);
1098 let res = query_mixnode_details(test.deps(), mix_id).unwrap();
1099 let details = res.mixnode_details.unwrap();
1100 assert_eq!(mix_id, details.bond_information.mix_id);
1101 assert_eq!(
1102 good_mixnode_pledge()[0],
1103 details.bond_information.original_pledge
1104 );
1105 assert_eq!(mix_id, res.mix_id);
1106 }
1107
1108 #[test]
1109 fn query_for_mixnode_details_by_identity() {
1110 let mut test = TestSetup::new();
1111
1112 let res = query_mixnode_details_by_identity(test.deps(), "foomp".into()).unwrap();
1114 assert!(res.is_none());
1115
1116 let mix_id = test.add_dummy_mixnode("owner", None);
1118 let expected = query_mixnode_details(test.deps(), mix_id)
1120 .unwrap()
1121 .mixnode_details
1122 .unwrap();
1123 let mix_identity = expected.bond_information.identity();
1124
1125 let res = query_mixnode_details_by_identity(test.deps(), mix_identity.into()).unwrap();
1126 assert_eq!(expected, res.unwrap());
1127 }
1128
1129 #[test]
1130 fn query_for_mixnode_rewarding_details() {
1131 let mut test = TestSetup::new();
1132
1133 let res = query_mixnode_rewarding_details(test.deps(), 42).unwrap();
1135 assert!(res.rewarding_details.is_none());
1136 assert_eq!(42, res.mix_id);
1137
1138 let mix_id = test.add_dummy_mixnode("foomp", None);
1139 let res = query_mixnode_rewarding_details(test.deps(), mix_id).unwrap();
1140 let details = res.rewarding_details.unwrap();
1141 assert_eq!(
1142 fixtures::mix_node_cost_params_fixture(),
1143 details.cost_params
1144 );
1145 assert_eq!(mix_id, res.mix_id);
1146 }
1147
1148 #[test]
1149 fn query_for_unbonded_mixnode() {
1150 let mut test = TestSetup::new();
1151
1152 let sender = "mix-owner";
1153
1154 let res = query_unbonded_mixnode(test.deps(), 42).unwrap();
1156 assert!(res.unbonded_info.is_none());
1157 assert_eq!(42, res.mix_id);
1158
1159 let mix_id = test.add_dummy_mixnode(sender, None);
1161 pending_events::unbond_mixnode(test.deps_mut(), &mock_env(), 123, mix_id).unwrap();
1162
1163 let res = query_unbonded_mixnode(test.deps(), mix_id).unwrap();
1164 assert_eq!(res.unbonded_info.unwrap().owner, sender);
1165 assert_eq!(mix_id, res.mix_id);
1166 }
1167
1168 #[test]
1169 fn query_for_stake_saturation() {
1170 let mut test = TestSetup::new();
1171
1172 let res = query_stake_saturation(test.deps(), 42).unwrap();
1174 assert!(res.current_saturation.is_none());
1175 assert!(res.uncapped_saturation.is_none());
1176 assert_eq!(42, res.mix_id);
1177
1178 let rewarding_params = rewards_storage::REWARDING_PARAMS
1179 .load(test.deps().storage)
1180 .unwrap();
1181 let saturation_point = rewarding_params.interval.stake_saturation_point;
1182
1183 let mix_id = test.add_dummy_mixnode("foomp", None);
1184
1185 let expected =
1188 Decimal::from_atomics(good_mixnode_pledge()[0].amount, 0).unwrap() / saturation_point;
1189 let res = query_stake_saturation(test.deps(), mix_id).unwrap();
1190 assert_eq!(expected, res.current_saturation.unwrap());
1191 assert_eq!(expected, res.uncapped_saturation.unwrap());
1192 assert_eq!(mix_id, res.mix_id);
1193
1194 let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
1196 .load(test.deps().storage, mix_id)
1197 .unwrap();
1198 mix_rewarding.operator = saturation_point;
1199 rewards_storage::MIXNODE_REWARDING
1200 .save(test.deps_mut().storage, mix_id, &mix_rewarding)
1201 .unwrap();
1202
1203 let res = query_stake_saturation(test.deps(), mix_id).unwrap();
1204 assert_eq!(Decimal::one(), res.current_saturation.unwrap());
1205 assert_eq!(Decimal::one(), res.uncapped_saturation.unwrap());
1206 assert_eq!(mix_id, res.mix_id);
1207
1208 let mut mix_rewarding = rewards_storage::MIXNODE_REWARDING
1210 .load(test.deps().storage, mix_id)
1211 .unwrap();
1212 mix_rewarding.delegates = mix_rewarding.operator * Decimal::percent(150);
1213 rewards_storage::MIXNODE_REWARDING
1214 .save(test.deps_mut().storage, mix_id, &mix_rewarding)
1215 .unwrap();
1216
1217 let res = query_stake_saturation(test.deps(), mix_id).unwrap();
1218 assert_eq!(Decimal::one(), res.current_saturation.unwrap());
1219 assert_eq!(Decimal::percent(250), res.uncapped_saturation.unwrap());
1220 assert_eq!(mix_id, res.mix_id);
1221 }
1222}