diff --git a/proof_of_stake/src/lib.rs b/proof_of_stake/src/lib.rs index 6ccea401f3c..03ce5d8af23 100644 --- a/proof_of_stake/src/lib.rs +++ b/proof_of_stake/src/lib.rs @@ -753,9 +753,18 @@ where } Ok(false) } - None => Ok(storage_api::iter_prefix_bytes(storage, &prefix)? - .next() - .is_some()), + None => { + let iter = storage_api::iter_prefix_bytes(storage, &prefix)?; + for res in iter { + let (key, _) = res?; + if let Some((bond_id, _epoch)) = is_bond_key(&key) { + if bond_id.source != bond_id.validator { + return Ok(true); + } + } + } + Ok(false) + } } } diff --git a/proof_of_stake/src/tests.rs b/proof_of_stake/src/tests.rs index d4390ef17a3..9f74553f1e7 100644 --- a/proof_of_stake/src/tests.rs +++ b/proof_of_stake/src/tests.rs @@ -293,6 +293,22 @@ proptest! { } } +proptest! { + // Generate arb valid input for `test_is_delegator` + #![proptest_config(Config { + cases: 100, + .. Config::default() + })] + #[test] + fn test_is_delegator( + + genesis_validators in arb_genesis_validators(2..3, None), + + ) { + test_is_delegator_aux(genesis_validators) + } +} + fn arb_params_and_genesis_validators( num_max_validator_slots: Option, val_size: Range, @@ -6560,3 +6576,109 @@ fn test_slashed_bond_amount_aux(validators: Vec) { let diff = val_stake - self_bond_amount - del_bond_amount; assert!(diff <= 2.into()); } + +fn test_is_delegator_aux(mut validators: Vec) { + validators.sort_by(|a, b| b.tokens.cmp(&a.tokens)); + + let validator1 = validators[0].address.clone(); + let validator2 = validators[1].address.clone(); + + let mut storage = TestWlStorage::default(); + let params = OwnedPosParams { + unbonding_len: 4, + ..Default::default() + }; + + // Genesis + let mut current_epoch = storage.storage.block.epoch; + let params = test_init_genesis( + &mut storage, + params, + validators.clone().into_iter(), + current_epoch, + ) + .unwrap(); + storage.commit_block().unwrap(); + + // Get delegators with some tokens + let staking_token = staking_token_address(&storage); + let delegator1 = address::testing::gen_implicit_address(); + let delegator2 = address::testing::gen_implicit_address(); + let del_balance = token::Amount::native_whole(1000); + credit_tokens(&mut storage, &staking_token, &delegator1, del_balance) + .unwrap(); + credit_tokens(&mut storage, &staking_token, &delegator2, del_balance) + .unwrap(); + + // Advance to epoch 1 + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Delegate in epoch 1 to validator1 + let del1_epoch = current_epoch; + super::bond_tokens( + &mut storage, + Some(&delegator1), + &validator1, + 1000.into(), + current_epoch, + None, + ) + .unwrap(); + + // Advance to epoch 2 + current_epoch = advance_epoch(&mut storage, ¶ms); + super::process_slashes(&mut storage, current_epoch).unwrap(); + + // Delegate in epoch 2 to validator2 + let del2_epoch = current_epoch; + super::bond_tokens( + &mut storage, + Some(&delegator2), + &validator2, + 1000.into(), + current_epoch, + None, + ) + .unwrap(); + + // Checks + assert!(super::is_validator(&storage, &validator1).unwrap()); + assert!(super::is_validator(&storage, &validator2).unwrap()); + assert!(!super::is_delegator(&storage, &validator1, None).unwrap()); + assert!(!super::is_delegator(&storage, &validator2, None).unwrap()); + + assert!(!super::is_validator(&storage, &delegator1).unwrap()); + assert!(!super::is_validator(&storage, &delegator2).unwrap()); + assert!(super::is_delegator(&storage, &delegator1, None).unwrap()); + assert!(super::is_delegator(&storage, &delegator2, None).unwrap()); + + for epoch in Epoch::default().iter_range(del1_epoch.0 + params.pipeline_len) + { + assert!( + !super::is_delegator(&storage, &delegator1, Some(epoch)).unwrap() + ); + } + assert!( + super::is_delegator( + &storage, + &delegator1, + Some(del1_epoch + params.pipeline_len) + ) + .unwrap() + ); + for epoch in Epoch::default().iter_range(del2_epoch.0 + params.pipeline_len) + { + assert!( + !super::is_delegator(&storage, &delegator2, Some(epoch)).unwrap() + ); + } + assert!( + super::is_delegator( + &storage, + &delegator2, + Some(del2_epoch + params.pipeline_len) + ) + .unwrap() + ); +}