Skip to content

Commit

Permalink
Power actor unit tests (part 4) (#280)
Browse files Browse the repository at this point in the history
* Implement power_and_ledge_accounted_below_threshold

And helpers:
  expect_total_power_eager
  expect_total_pledge_eager
  update_claimed_power
  current_power_total
  update_pledge_total
  create_miner_basic

* Remove unused miner addresses.

* Implement new_miner_updates_miner_above_min_power_count

* Implement power_accounting_crossing_threshold
  • Loading branch information
lemmih authored Apr 28, 2022
1 parent d239339 commit 1ebaa3a
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 5 deletions.
115 changes: 113 additions & 2 deletions actors/power/tests/harness/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ use serde::Serialize;

use fil_actor_power::ext::init::ExecParams;
use fil_actor_power::ext::miner::MinerConstructorParams;
use fil_actor_power::{ext, Claim, CreateMinerParams, CreateMinerReturn, Method, State};
use fil_actor_power::{
ext, Claim, CreateMinerParams, CreateMinerReturn, CurrentTotalPowerReturn, Method, State,
UpdateClaimedPowerParams,
};
use fil_actors_runtime::builtin::HAMT_BIT_WIDTH;
use fil_actors_runtime::runtime::Runtime;
use fil_actors_runtime::test_utils::{
Expand Down Expand Up @@ -75,7 +78,7 @@ pub fn setup() -> (Harness, MockRuntime) {
pub struct Harness {
miner_seq: i64,
seal_proof: RegisteredSealProof,
window_post_proof: RegisteredPoStProof,
pub window_post_proof: RegisteredPoStProof,
this_epoch_baseline_power: StoragePower,
this_epoch_reward_smoothed: FilterEstimate,
}
Expand Down Expand Up @@ -159,6 +162,30 @@ impl Harness {
Ok(())
}

pub fn create_miner_basic(
&mut self,
rt: &mut MockRuntime,
owner: Address,
worker: Address,
miner: Address,
) -> Result<(), ActorError> {
let label = format!("{}", self.miner_seq);
let actr_addr = Address::new_actor(label.as_bytes());
self.miner_seq += 1;
let peer = label.as_bytes().to_vec();
self.create_miner(
rt,
&owner,
&worker,
&miner,
&actr_addr,
peer,
vec![],
self.window_post_proof,
&TokenAmount::from(0),
)
}

pub fn list_miners(&self, rt: &MockRuntime) -> Vec<Address> {
let st: State = rt.get_state();
let claims: Map<_, Claim> =
Expand Down Expand Up @@ -228,6 +255,90 @@ impl Harness {
pub fn check_state(&self) {
// TODO: https://github.com/filecoin-project/builtin-actors/issues/44
}

pub fn update_pledge_total(&self, rt: &mut MockRuntime, miner: Address, delta: &TokenAmount) {
let st: State = rt.get_state();
let prev = st.total_pledge_collateral;

rt.set_caller(*MINER_ACTOR_CODE_ID, miner);
rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]);
rt.call::<PowerActor>(
Method::UpdatePledgeTotal as MethodNum,
&RawBytes::serialize(BigIntDe(delta.clone())).unwrap(),
)
.unwrap();
rt.verify();

let st: State = rt.get_state();
assert_eq!(prev + delta, st.total_pledge_collateral);
}

pub fn current_power_total(&self, rt: &mut MockRuntime) -> CurrentTotalPowerReturn {
rt.expect_validate_caller_any();
let ret: CurrentTotalPowerReturn = rt
.call::<PowerActor>(Method::CurrentTotalPower as u64, &RawBytes::default())
.unwrap()
.deserialize()
.unwrap();
rt.verify();
ret
}

pub fn update_claimed_power(
&self,
rt: &mut MockRuntime,
miner: Address,
raw_delta: &StoragePower,
qa_delta: &StoragePower,
) {
let prev_cl = self.get_claim(rt, &miner).unwrap();

let params = UpdateClaimedPowerParams {
raw_byte_delta: raw_delta.clone(),
quality_adjusted_delta: qa_delta.clone(),
};
rt.set_caller(*MINER_ACTOR_CODE_ID, miner);
rt.expect_validate_caller_type(vec![*MINER_ACTOR_CODE_ID]);
rt.call::<PowerActor>(
Method::UpdateClaimedPower as MethodNum,
&RawBytes::serialize(params).unwrap(),
)
.unwrap();
rt.verify();

let cl = self.get_claim(rt, &miner).unwrap();
let expected_raw = &prev_cl.raw_byte_power + raw_delta;
let expected_adjusted = &prev_cl.quality_adj_power + qa_delta;
if expected_raw.is_zero() {
assert!(cl.raw_byte_power.is_zero());
} else {
assert_eq!(prev_cl.raw_byte_power + raw_delta, cl.raw_byte_power);
}

if expected_adjusted.is_zero() {
assert!(cl.quality_adj_power.is_zero());
} else {
assert_eq!(prev_cl.quality_adj_power + qa_delta, cl.quality_adj_power);
}
}

pub fn expect_total_power_eager(
&self,
rt: &mut MockRuntime,
expected_raw: &StoragePower,
expected_qa: &StoragePower,
) {
let st: State = rt.get_state();

let (raw_byte_power, quality_adj_power) = st.current_total_power();
assert_eq!(expected_raw, &raw_byte_power);
assert_eq!(expected_qa, &quality_adj_power);
}

pub fn expect_total_pledge_eager(&self, rt: &mut MockRuntime, expected_pledge: &TokenAmount) {
let st: State = rt.get_state();
assert_eq!(expected_pledge, &st.total_pledge_collateral);
}
}

/// Collects all keys from a map into a vector.
Expand Down
135 changes: 132 additions & 3 deletions actors/power/tests/power_actor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use fil_actors_runtime::test_utils::{
expect_abort, expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID, CALLER_TYPES_SIGNABLE,
MINER_ACTOR_CODE_ID, SYSTEM_ACTOR_CODE_ID,
};
use fil_actors_runtime::INIT_ACTOR_ADDR;
use fil_actors_runtime::{runtime::Policy, INIT_ACTOR_ADDR};
use fvm_ipld_encoding::{BytesDe, RawBytes};
use fvm_shared::address::Address;
use fvm_shared::bigint::bigint_ser::BigIntSer;
Expand All @@ -13,10 +13,11 @@ use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ExitCode;
use fvm_shared::sector::{RegisteredPoStProof, StoragePower};
use num_traits::Zero;
use std::ops::Neg;

use fil_actor_power::{
Actor as PowerActor, CreateMinerParams, EnrollCronEventParams, Method, State,
UpdateClaimedPowerParams,
consensus_miner_min_power, Actor as PowerActor, CreateMinerParams, EnrollCronEventParams,
Method, State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS,
};

use crate::harness::*;
Expand Down Expand Up @@ -203,6 +204,63 @@ fn claimed_power_given_claim_does_not_exist_should_fail() {
h.check_state();
}

const MINER1: Address = Address::new_id(111);
const MINER2: Address = Address::new_id(112);
const MINER3: Address = Address::new_id(113);
const MINER4: Address = Address::new_id(114);
const MINER5: Address = Address::new_id(115);

#[test]
fn power_and_pledge_accounted_below_threshold() {
assert_eq!(CONSENSUS_MINER_MIN_MINERS, 4);

let small_power_unit = &StoragePower::from(1_000_000);
let small_power_unit_x2 = &(small_power_unit * 2);
let small_power_unit_x3 = &(small_power_unit * 3);

let (mut h, mut rt) = setup();

h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER1).unwrap();
h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER2).unwrap();

let ret = h.current_power_total(&mut rt);
assert_eq!(StoragePower::zero(), ret.raw_byte_power);
assert_eq!(StoragePower::zero(), ret.quality_adj_power);
assert_eq!(TokenAmount::zero(), ret.pledge_collateral);

// Add power for miner1
h.update_claimed_power(&mut rt, MINER1, small_power_unit, small_power_unit_x2);
h.expect_total_power_eager(&mut rt, small_power_unit, small_power_unit_x2);

// Add power and pledge for miner2
h.update_claimed_power(&mut rt, MINER2, small_power_unit, small_power_unit);
h.update_pledge_total(&mut rt, MINER1, &TokenAmount::from(1_000_000));
h.expect_total_power_eager(&mut rt, small_power_unit_x2, small_power_unit_x3);
h.expect_total_pledge_eager(&mut rt, &TokenAmount::from(1_000_000));

rt.verify();

// Verify claims in state.
let claim1 = h.get_claim(&rt, &MINER1).unwrap();
assert_eq!(small_power_unit, &claim1.raw_byte_power);
assert_eq!(small_power_unit_x2, &claim1.quality_adj_power);

let claim2 = h.get_claim(&rt, &MINER2).unwrap();
assert_eq!(small_power_unit, &claim2.raw_byte_power);
assert_eq!(small_power_unit, &claim2.quality_adj_power);

// Subtract power and some pledge for miner2
h.update_claimed_power(&mut rt, MINER2, &small_power_unit.neg(), &small_power_unit.neg());
h.update_pledge_total(&mut rt, MINER2, &TokenAmount::from(100_000).neg());
h.expect_total_power_eager(&mut rt, small_power_unit, small_power_unit_x2);
h.expect_total_pledge_eager(&mut rt, &TokenAmount::from(900_000));

let claim2 = h.get_claim(&rt, &MINER2).unwrap();
assert!(claim2.raw_byte_power.is_zero());
assert!(claim2.quality_adj_power.is_zero());
h.check_state();
}

#[test]
fn enroll_cron_epoch_multiple_events() {
let (h, mut rt) = setup();
Expand Down Expand Up @@ -315,6 +373,77 @@ fn enroll_cron_epoch_before_current_epoch() {
h.check_state();
}

#[test]
fn new_miner_updates_miner_above_min_power_count() {
struct TestCase {
proof: RegisteredPoStProof,
expected_miners: i64,
}

let test_cases = [
TestCase { proof: RegisteredPoStProof::StackedDRGWindow2KiBV1, expected_miners: 0 },
TestCase { proof: RegisteredPoStProof::StackedDRGWindow32GiBV1, expected_miners: 0 },
];

for test in test_cases {
let (mut h, mut rt) = setup();
h.window_post_proof = test.proof;
h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER1).unwrap();

let st: State = rt.get_state();
assert_eq!(test.expected_miners, st.miner_above_min_power_count);
}
}

#[test]
fn power_accounting_crossing_threshold() {
let small_power_unit = &StoragePower::from(1_000_000);
let small_power_unit_x10 = &(small_power_unit * 10);

let power_unit = &consensus_miner_min_power(
&Policy::default(),
RegisteredPoStProof::StackedDRGWindow32GiBV1,
)
.unwrap();
let power_unit_x10 = &(power_unit * 10);

assert!(small_power_unit < power_unit);

let (mut h, mut rt) = setup();

h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER1).unwrap();
h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER2).unwrap();
h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER3).unwrap();
h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER4).unwrap();
h.create_miner_basic(&mut rt, *OWNER, *OWNER, MINER5).unwrap();

// Use qa power 10x raw power to show it's not being used for threshold calculations.
h.update_claimed_power(&mut rt, MINER1, small_power_unit, small_power_unit_x10);
h.update_claimed_power(&mut rt, MINER2, small_power_unit, small_power_unit_x10);

h.update_claimed_power(&mut rt, MINER3, power_unit, power_unit_x10);
h.update_claimed_power(&mut rt, MINER4, power_unit, power_unit_x10);
h.update_claimed_power(&mut rt, MINER5, power_unit, power_unit_x10);

// Below threshold small miner power is counted
let expected_total_below = small_power_unit * 2 + power_unit * 3;
h.expect_total_power_eager(&mut rt, &expected_total_below, &(&expected_total_below * 10));

// Above threshold (power.ConsensusMinerMinMiners = 4) small miner power is ignored
let delta = &(power_unit - small_power_unit);
h.update_claimed_power(&mut rt, MINER2, delta, &(delta * 10));
let expected_total_above = &(power_unit * 4);
h.expect_total_power_eager(&mut rt, expected_total_above, &(expected_total_above * 10));

let st: State = rt.get_state();
assert_eq!(4, st.miner_above_min_power_count);

// Less than 4 miners above threshold again small miner power is counted again
h.update_claimed_power(&mut rt, MINER4, &delta.neg(), &(delta.neg() * 10));
h.expect_total_power_eager(&mut rt, &expected_total_below, &(&expected_total_below * 10));
h.check_state();
}

#[test]
fn enroll_cron_epoch_given_negative_epoch_should_fail() {
let (h, mut rt) = setup();
Expand Down

0 comments on commit 1ebaa3a

Please sign in to comment.