diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index c8f047b3d..8698ec36d 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -46,7 +46,7 @@ pub use expiration_queue::*; use fil_actors_runtime::cbor::{serialize, serialize_vec}; use fil_actors_runtime::reward::{FilterEstimate, ThisEpochRewardReturn}; use fil_actors_runtime::runtime::builtins::Type; -use fil_actors_runtime::runtime::policy_constants::MAX_SECTOR_NUMBER; +use fil_actors_runtime::runtime::policy_constants::{MAX_SECTOR_NUMBER, MINIMUM_CONSENSUS_POWER}; use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime}; use fil_actors_runtime::{ actor_dispatch, actor_error, deserialize_block, extract_send_result, util, ActorContext, @@ -181,6 +181,14 @@ impl Actor { check_peer_info(rt.policy(), ¶ms.peer_id, ¶ms.multi_addresses)?; check_valid_post_proof_type(rt.policy(), params.window_post_proof_type)?; + let balance = rt.current_balance(); + let deposit = calculate_create_miner_deposit(rt, params.network_qap)?; + if balance < deposit { + return Err(actor_error!(insufficient_funds; + "not enough balance to lock for create miner deposit: \ + sent balance {} < deposit {}", balance.atto(), deposit.atto())); + } + let owner = rt.resolve_address(¶ms.owner).ok_or_else(|| { actor_error!(illegal_argument, "unable to resolve owner address: {}", params.owner) })?; @@ -239,7 +247,10 @@ impl Actor { e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state") })?; - let st = State::new(policy, rt.store(), info_cid, period_start, deadline_idx)?; + let store = rt.store(); + let mut st = State::new(policy, store, info_cid, period_start, deadline_idx)?; + st.add_locked_funds(store, rt.curr_epoch(), &deposit, &REWARD_VESTING_SPEC) + .map_err(|e| actor_error!(illegal_state, e))?; rt.create(&st)?; Ok(()) } @@ -756,6 +767,7 @@ impl Actor { Ok(()) } + /// Checks state of the corresponding sector pre-commitments and verifies aggregate proof of replication /// of these sectors. If valid, the sectors' deals are activated, sectors are assigned a deadline and charged pledge /// and precommit state is removed. @@ -5642,6 +5654,53 @@ fn activate_new_sector_infos( Ok(()) } +/// Calculate create miner deposit by MINIMUM_CONSENSUS_POWER x StateMinerInitialPledgeCollateral +/// See FIP-0077, https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0077.md +pub fn calculate_create_miner_deposit( + rt: &impl Runtime, + network_qap: FilterEstimate, +) -> Result { + // set network pledge inputs + let rew = request_current_epoch_block_reward(rt)?; + let pwr = request_current_total_power(rt)?; + let circulating_supply = rt.total_fil_circ_supply(); + let pledge_inputs = NetworkPledgeInputs { + network_qap, + network_baseline: rew.this_epoch_baseline_power, + circulating_supply, + epoch_reward: rew.this_epoch_reward_smoothed, + epochs_since_ramp_start: rt.curr_epoch() - pwr.ramp_start_epoch, + ramp_duration_epochs: pwr.ramp_duration_epochs, + }; + + /// set sector size with min power + #[cfg(feature = "min-power-2k")] + let sector_size = SectorSize::_2KiB; + #[cfg(feature = "min-power-2g")] + let sector_size = SectorSize::_8MiB; + #[cfg(feature = "min-power-32g")] + let sector_size = SectorSize::_512MiB; + #[cfg(not(any( + feature = "min-power-2k", + feature = "min-power-2g", + feature = "min-power-32g" + )))] + let sector_size = SectorSize::_32GiB; + + let sector_number = MINIMUM_CONSENSUS_POWER / sector_size as i64; + let power = qa_power_for_weight(sector_size, MIN_SECTOR_EXPIRATION, &BigInt::zero()); + let sector_initial_pledge = initial_pledge_for_power( + &power, + &pledge_inputs.network_baseline, + &pledge_inputs.epoch_reward, + &pledge_inputs.network_qap, + &pledge_inputs.circulating_supply, + pledge_inputs.epochs_since_ramp_start, + pledge_inputs.ramp_duration_epochs, + ); + Ok(sector_initial_pledge * sector_number) +} + pub struct SectorPiecesActivationInput { pub piece_manifests: Vec, pub sector_expiry: ChainEpoch, diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index 0ba88729a..92f5748c2 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -214,6 +214,7 @@ impl State { pub fn deadline_info(&self, policy: &Policy, current_epoch: ChainEpoch) -> DeadlineInfo { new_deadline_info_from_offset_and_epoch(policy, self.proving_period_start, current_epoch) } + // Returns deadline calculations for the state recorded proving period and deadline. // This is out of date if the a miner does not have an active miner cron pub fn recorded_deadline_info( @@ -877,6 +878,7 @@ impl State { amount_unlocked )); } + // add locked funds now vesting_funds.add_locked_funds(current_epoch, vesting_sum, self.proving_period_start, spec); self.locked_funds += vesting_sum; @@ -937,6 +939,7 @@ impl State { Ok(std::mem::take(&mut self.fee_debt)) } + /// Unlocks an amount of funds that have *not yet vested*, if possible. /// The soonest-vesting entries are unlocked first. /// Returns the amount actually unlocked. diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index a0a97d9c3..8c36cc96b 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -45,6 +45,7 @@ pub struct MinerConstructorParams { #[serde(with = "strict_bytes")] pub peer_id: Vec, pub multi_addresses: Vec, + pub network_qap: FilterEstimate, } #[derive(Serialize_tuple, Deserialize_tuple)] diff --git a/actors/miner/tests/miner_actor_test_construction.rs b/actors/miner/tests/miner_actor_test_construction.rs index 332a54eca..a3605b0f3 100644 --- a/actors/miner/tests/miner_actor_test_construction.rs +++ b/actors/miner/tests/miner_actor_test_construction.rs @@ -1,20 +1,23 @@ -use fil_actors_runtime::test_utils::*; -use fil_actors_runtime::INIT_ACTOR_ADDR; - use fil_actor_account::Method as AccountMethod; use fil_actor_miner::{ Actor, Deadline, Deadlines, Method, MinerConstructorParams as ConstructorParams, State, }; +use fil_actor_power::{CurrentTotalPowerReturn, Method as PowerMethod}; +use fil_actor_reward::{Method as RewardMethod, ThisEpochRewardReturn}; +use fil_actors_runtime::reward::FilterEstimate; +use fil_actors_runtime::{test_utils::*, STORAGE_POWER_ACTOR_ADDR}; +use fil_actors_runtime::{INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR}; use fvm_ipld_encoding::{BytesDe, CborStore}; use fvm_shared::address::Address; +use fvm_shared::bigint::BigInt; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; -use fvm_shared::sector::{RegisteredPoStProof, SectorSize}; +use fvm_shared::sector::{RegisteredPoStProof, SectorSize, StoragePower}; use cid::Cid; use fvm_ipld_encoding::ipld_block::IpldBlock; -use num_traits::Zero; +use num_traits::{FromPrimitive, Zero}; mod util; @@ -27,10 +30,17 @@ struct TestEnv { control_addrs: Vec
, peer_id: Vec, multiaddrs: Vec, + power: StoragePower, + reward: TokenAmount, + epoch_reward_smooth: FilterEstimate, rt: MockRuntime, } fn prepare_env() -> TestEnv { + let reward = TokenAmount::from_whole(10); + let power = StoragePower::from_i128(1 << 50).unwrap(); + let epoch_reward_smooth = FilterEstimate::new(reward.atto().clone(), BigInt::from(0u8)); + let mut env = TestEnv { receiver: Address::new_id(1000), owner: Address::new_id(100), @@ -39,6 +49,9 @@ fn prepare_env() -> TestEnv { control_addrs: vec![Address::new_id(999), Address::new_id(998)], peer_id: vec![1, 2, 3], multiaddrs: vec![BytesDe(vec![1, 2, 3])], + power, + reward, + epoch_reward_smooth, rt: MockRuntime::default(), }; @@ -50,6 +63,8 @@ fn prepare_env() -> TestEnv { env.rt.hash_func = Box::new(hash); env.rt.caller.replace(INIT_ACTOR_ADDR); env.rt.caller_type.replace(*INIT_ACTOR_CODE_ID); + // add balance for create miner deposit + env.rt.add_balance(TokenAmount::from_atto(633318697598976000u64)); env } @@ -61,16 +76,46 @@ fn constructor_params(env: &TestEnv) -> ConstructorParams { window_post_proof_type: RegisteredPoStProof::StackedDRGWindow32GiBV1P1, peer_id: env.peer_id.clone(), multi_addresses: env.multiaddrs.clone(), + network_qap: env.epoch_reward_smooth.clone(), } } #[test] fn simple_construction() { let env = prepare_env(); + let current_reward = ThisEpochRewardReturn { + this_epoch_baseline_power: env.power.clone(), + this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(), + }; + let current_total_power = CurrentTotalPowerReturn { + raw_byte_power: Default::default(), + quality_adj_power: Default::default(), + pledge_collateral: Default::default(), + quality_adj_power_smoothed: Default::default(), + ramp_start_epoch: Default::default(), + ramp_duration_epochs: Default::default(), + }; + let params = constructor_params(&env); env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + env.rt.expect_send_simple( + REWARD_ACTOR_ADDR, + RewardMethod::ThisEpochReward as u64, + None, + TokenAmount::zero(), + IpldBlock::serialize_cbor(¤t_reward).unwrap(), + ExitCode::OK, + ); + env.rt.expect_send_simple( + STORAGE_POWER_ACTOR_ADDR, + PowerMethod::CurrentTotalPower as u64, + Default::default(), + TokenAmount::zero(), + IpldBlock::serialize_cbor(¤t_total_power).unwrap(), + ExitCode::OK, + ); env.rt.expect_send_simple( env.worker, AccountMethod::PubkeyAddress as u64, @@ -87,7 +132,7 @@ fn simple_construction() { expect_empty(result); env.rt.verify(); - let state = env.rt.get_state::(); + let mut state = env.rt.get_state::(); let info = state.get_info(&env.rt.store).unwrap(); assert_eq!(env.owner, info.owner); @@ -100,10 +145,21 @@ fn simple_construction() { assert_eq!(2349, info.window_post_partition_sectors); assert_eq!(TokenAmount::zero(), state.pre_commit_deposits); - assert_eq!(TokenAmount::zero(), state.locked_funds); + assert_eq!(TokenAmount::from_atto(633318697598976000u64), state.locked_funds); + assert_eq!(180, state.load_vesting_funds(&env.rt.store).unwrap().funds.len()); assert_ne!(Cid::default(), state.pre_committed_sectors); assert_ne!(Cid::default(), state.sectors); + // reset create miner deposit vesting funds + state.save_vesting_funds(&env.rt.store, &fil_actor_miner::VestingFunds::new()).unwrap(); + state.locked_funds = TokenAmount::zero(); + env.rt.replace_state(&state); + + let state = env.rt.get_state::(); + let create_depost_vesting_funds = state.load_vesting_funds(&env.rt.store).unwrap(); + assert!(create_depost_vesting_funds.funds.is_empty()); + assert!(state.locked_funds.is_zero()); + // according to original specs-actors test, this is set by running the code; magic... let proving_period_start = -2222; assert_eq!(proving_period_start, state.proving_period_start); @@ -128,9 +184,67 @@ fn simple_construction() { util::check_state_invariants_from_mock_runtime(&env.rt); } +#[test] +fn fails_if_insufficient_to_cover_the_miner_creation_deposit() { + let env = prepare_env(); + env.rt.set_balance(TokenAmount::zero()); + let current_reward = ThisEpochRewardReturn { + this_epoch_baseline_power: env.power.clone(), + this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(), + }; + let current_total_power = CurrentTotalPowerReturn { + raw_byte_power: Default::default(), + quality_adj_power: Default::default(), + pledge_collateral: Default::default(), + quality_adj_power_smoothed: Default::default(), + ramp_start_epoch: Default::default(), + ramp_duration_epochs: Default::default(), + }; + + let params = constructor_params(&env); + + env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); + env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + env.rt.expect_send_simple( + REWARD_ACTOR_ADDR, + RewardMethod::ThisEpochReward as u64, + None, + TokenAmount::zero(), + IpldBlock::serialize_cbor(¤t_reward).unwrap(), + ExitCode::OK, + ); + env.rt.expect_send_simple( + STORAGE_POWER_ACTOR_ADDR, + PowerMethod::CurrentTotalPower as u64, + Default::default(), + TokenAmount::zero(), + IpldBlock::serialize_cbor(¤t_total_power).unwrap(), + ExitCode::OK, + ); + + expect_abort( + ExitCode::USR_INSUFFICIENT_FUNDS, + env.rt + .call::(Method::Constructor as u64, IpldBlock::serialize_cbor(¶ms).unwrap()), + ); + env.rt.verify(); +} + #[test] fn control_addresses_are_resolved_during_construction() { let mut env = prepare_env(); + let current_reward = ThisEpochRewardReturn { + this_epoch_baseline_power: env.power.clone(), + this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(), + }; + let current_total_power = CurrentTotalPowerReturn { + raw_byte_power: Default::default(), + quality_adj_power: Default::default(), + pledge_collateral: Default::default(), + quality_adj_power_smoothed: Default::default(), + ramp_start_epoch: Default::default(), + ramp_duration_epochs: Default::default(), + }; let control1 = new_bls_addr(1); let control1id = Address::new_id(555); @@ -146,6 +260,22 @@ fn control_addresses_are_resolved_during_construction() { let params = constructor_params(&env); env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + env.rt.expect_send_simple( + REWARD_ACTOR_ADDR, + RewardMethod::ThisEpochReward as u64, + None, + TokenAmount::zero(), + IpldBlock::serialize_cbor(¤t_reward).unwrap(), + ExitCode::OK, + ); + env.rt.expect_send_simple( + STORAGE_POWER_ACTOR_ADDR, + PowerMethod::CurrentTotalPower as u64, + Default::default(), + TokenAmount::zero(), + IpldBlock::serialize_cbor(¤t_total_power).unwrap(), + ExitCode::OK, + ); env.rt.expect_send_simple( env.worker, AccountMethod::PubkeyAddress as u64, diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index fdbceb349..11d44707d 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -38,7 +38,7 @@ use fvm_shared::{ActorID, HAMT_BIT_WIDTH, METHOD_SEND}; use itertools::Itertools; use lazy_static::lazy_static; use multihash_codetable::MultihashDigest; -use num_traits::Signed; +use num_traits::{FromPrimitive, Signed}; use fil_actor_account::Method as AccountMethod; use fil_actor_market::{ @@ -159,6 +159,7 @@ pub struct ActorHarness { pub epoch_qa_power_smooth: FilterEstimate, pub base_fee: TokenAmount, + pub create_depost: TokenAmount, pub options: HarnessOptions, } @@ -210,7 +211,7 @@ impl ActorHarness { epoch_qa_power_smooth: FilterEstimate::new(pwr, BigInt::from(0)), base_fee: TokenAmount::zero(), - + create_depost: TokenAmount::from_atto(633318697598976000u64), options, } } @@ -227,6 +228,27 @@ impl ActorHarness { check_state_invariants_from_mock_runtime(rt); } + pub fn check_create_miner_depost_and_reset_state(&self, rt: &MockRuntime) { + let mut st = self.get_state(&rt); + let create_depost_vesting_funds = st.load_vesting_funds(&rt.store).unwrap(); + + // create miner deposit + assert!(create_depost_vesting_funds.funds.len() == 180); + assert!(st.locked_funds == self.create_depost); + + // reset create miner deposit vesting funds + st.save_vesting_funds(&rt.store(), &VestingFunds::new()).unwrap(); + st.locked_funds = TokenAmount::zero(); + rt.replace_state(&st); + rt.set_balance(rt.get_balance() - &self.create_depost); + + let st = self.get_state(&rt); + let create_depost_vesting_funds = st.load_vesting_funds(&rt.store).unwrap(); + + assert!(create_depost_vesting_funds.funds.is_empty()); + assert!(st.locked_funds.is_zero()); + } + pub fn new_runtime(&self) -> MockRuntime { let mut rt = MockRuntime::default(); @@ -253,6 +275,24 @@ impl ActorHarness { } pub fn construct_and_verify(&self, rt: &MockRuntime) { + let reward = TokenAmount::from_whole(10); + let power = StoragePower::from_i128(1 << 50).unwrap(); + let epoch_reward_smooth = FilterEstimate::new(reward.atto().clone(), BigInt::from(0u8)); + + let current_reward = ThisEpochRewardReturn { + this_epoch_baseline_power: power.clone(), + this_epoch_reward_smoothed: epoch_reward_smooth.clone(), + }; + + let current_total_power = CurrentTotalPowerReturn { + raw_byte_power: Default::default(), + quality_adj_power: Default::default(), + pledge_collateral: Default::default(), + quality_adj_power_smoothed: Default::default(), + ramp_start_epoch: Default::default(), + ramp_duration_epochs: Default::default(), + }; + let params = ConstructorParams { owner: self.owner, worker: self.worker, @@ -260,6 +300,7 @@ impl ActorHarness { window_post_proof_type: self.window_post_proof_type, peer_id: vec![0], multi_addresses: vec![], + network_qap: epoch_reward_smooth, }; rt.actor_code_cids.borrow_mut().insert(self.owner, *ACCOUNT_ACTOR_CODE_ID); @@ -268,8 +309,25 @@ impl ActorHarness { rt.actor_code_cids.borrow_mut().insert(*a, *ACCOUNT_ACTOR_CODE_ID); } + rt.add_balance(self.create_depost.clone()); rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR); rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]); + rt.expect_send_simple( + REWARD_ACTOR_ADDR, + RewardMethod::ThisEpochReward as u64, + None, + TokenAmount::zero(), + IpldBlock::serialize_cbor(¤t_reward).unwrap(), + ExitCode::OK, + ); + rt.expect_send_simple( + STORAGE_POWER_ACTOR_ADDR, + ext::power::CURRENT_TOTAL_POWER_METHOD, + Default::default(), + TokenAmount::zero(), + IpldBlock::serialize_cbor(¤t_total_power).unwrap(), + ExitCode::OK, + ); rt.expect_send_simple( self.worker, AccountMethod::PubkeyAddress as u64, @@ -284,6 +342,7 @@ impl ActorHarness { .unwrap(); expect_empty(result); rt.verify(); + self.check_create_miner_depost_and_reset_state(rt); } pub fn set_peer_id(&self, rt: &MockRuntime, new_id: Vec) { @@ -2106,7 +2165,7 @@ impl ActorHarness { ); } - let params = ApplyRewardParams { reward: amt, penalty: penalty }; + let params = ApplyRewardParams { reward: amt, penalty }; rt.call::(Method::ApplyRewards as u64, IpldBlock::serialize_cbor(¶ms).unwrap()) .unwrap(); rt.verify(); diff --git a/actors/power/src/ext.rs b/actors/power/src/ext.rs index 9c821c1d0..dfe9ce71c 100644 --- a/actors/power/src/ext.rs +++ b/actors/power/src/ext.rs @@ -46,6 +46,7 @@ pub mod miner { #[serde(with = "strict_bytes")] pub peer_id: Vec, pub multi_addresses: Vec, + pub network_qap: FilterEstimate, } #[derive(Serialize_tuple, Deserialize_tuple)] @@ -60,6 +61,7 @@ pub mod miner { pub mod reward { use super::*; + pub const THIS_EPOCH_REWARD_METHOD: u64 = 3; pub const UPDATE_NETWORK_KPI: u64 = 4; #[derive(FromPrimitive)] diff --git a/actors/power/src/lib.rs b/actors/power/src/lib.rs index f89944951..f8a59940b 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -82,6 +82,7 @@ impl Actor { rt.validate_immediate_caller_accept_any()?; let value = rt.message().value_received(); + let state: State = rt.state()?; let constructor_params = RawBytes::serialize(ext::miner::MinerConstructorParams { owner: params.owner, worker: params.worker, @@ -89,6 +90,7 @@ impl Actor { peer_id: params.peer, multi_addresses: params.multiaddrs, control_addresses: Default::default(), + network_qap: state.this_epoch_qa_power_smoothed, })?; let miner_actor_code_cid = rt.get_code_cid_for_type(Type::Miner); diff --git a/actors/power/tests/harness/mod.rs b/actors/power/tests/harness/mod.rs index 720251d65..9452b4c95 100644 --- a/actors/power/tests/harness/mod.rs +++ b/actors/power/tests/harness/mod.rs @@ -135,6 +135,9 @@ impl Harness { rt.set_balance(value.clone()); rt.expect_validate_caller_any(); + // set constructor miner expectation + let st: State = rt.get_state(); + let network_qap = st.this_epoch_qa_power_smoothed.clone(); let miner_ctor_params = MinerConstructorParams { owner: *owner, worker: *worker, @@ -142,6 +145,7 @@ impl Harness { window_post_proof_type, peer_id: peer.clone(), multi_addresses: multiaddrs.clone(), + network_qap, }; let expected_init_params = ExecParams { code_cid: *MINER_ACTOR_CODE_ID, @@ -156,6 +160,7 @@ impl Harness { IpldBlock::serialize_cbor(&create_miner_ret).unwrap(), ExitCode::OK, ); + let params = CreateMinerParams { owner: *owner, worker: *worker, diff --git a/actors/power/tests/power_actor_tests.rs b/actors/power/tests/power_actor_tests.rs index 554539433..c597905c8 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -1,5 +1,10 @@ use fil_actor_power::ext::init::{ExecParams, EXEC_METHOD}; use fil_actor_power::ext::miner::MinerConstructorParams; +use fil_actor_power::{ + consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, CreateMinerReturn, + EnrollCronEventParams, Method, MinerRawPowerParams, MinerRawPowerReturn, NetworkRawPowerReturn, + State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS, +}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::test_utils::{ expect_abort, expect_abort_contains_message, ACCOUNT_ACTOR_CODE_ID, EVM_ACTOR_CODE_ID, @@ -17,12 +22,6 @@ use fvm_shared::MethodNum; use num_traits::Zero; use std::ops::Neg; -use fil_actor_power::{ - consensus_miner_min_power, Actor as PowerActor, Actor, CreateMinerParams, CreateMinerReturn, - EnrollCronEventParams, Method, MinerRawPowerParams, MinerRawPowerReturn, NetworkRawPowerReturn, - State, UpdateClaimedPowerParams, CONSENSUS_MINER_MIN_MINERS, -}; - use fvm_ipld_encoding::ipld_block::IpldBlock; use crate::harness::*; @@ -100,6 +99,9 @@ fn create_miner_given_send_to_init_actor_fails_should_fail() { rt.set_balance(TokenAmount::from_atto(10)); rt.expect_validate_caller_any(); + let st: State = rt.get_state(); + let network_qap = st.this_epoch_qa_power_smoothed.clone(); + let message_params = ExecParams { code_cid: *MINER_ACTOR_CODE_ID, constructor_params: RawBytes::serialize(MinerConstructorParams { @@ -109,6 +111,7 @@ fn create_miner_given_send_to_init_actor_fails_should_fail() { peer_id: peer, multi_addresses: multiaddrs, control_addresses: Default::default(), + network_qap, }) .unwrap(), }; @@ -1041,7 +1044,10 @@ fn create_miner_restricted_correctly() { }) .unwrap(); + let deposit = TokenAmount::from_atto(320); rt.set_caller(*EVM_ACTOR_CODE_ID, *OWNER); + rt.set_received(deposit.clone()); + rt.set_balance(deposit.clone()); // cannot call the unexported method expect_abort_contains_message( @@ -1053,6 +1059,9 @@ fn create_miner_restricted_correctly() { // can call the exported method rt.expect_validate_caller_any(); + + let st: State = rt.get_state(); + let network_qap = st.this_epoch_qa_power_smoothed.clone(); let expected_init_params = ExecParams { code_cid: *MINER_ACTOR_CODE_ID, constructor_params: RawBytes::serialize(MinerConstructorParams { @@ -1062,6 +1071,7 @@ fn create_miner_restricted_correctly() { window_post_proof_type: RegisteredPoStProof::StackedDRGWinning2KiBV1, peer_id: peer, multi_addresses: multiaddrs, + network_qap, }) .unwrap(), }; @@ -1070,7 +1080,7 @@ fn create_miner_restricted_correctly() { INIT_ACTOR_ADDR, EXEC_METHOD, IpldBlock::serialize_cbor(&expected_init_params).unwrap(), - TokenAmount::zero(), + deposit, IpldBlock::serialize_cbor(&create_miner_ret).unwrap(), ExitCode::OK, ); diff --git a/integration_tests/src/tests/power_scenario_tests.rs b/integration_tests/src/tests/power_scenario_tests.rs index e2b2ab9f5..4af7c6265 100644 --- a/integration_tests/src/tests/power_scenario_tests.rs +++ b/integration_tests/src/tests/power_scenario_tests.rs @@ -3,7 +3,7 @@ use fil_actor_init::Method as InitMethod; use fil_actor_miner::{ max_prove_commit_duration, Method as MinerMethod, MinerConstructorParams, MIN_SECTOR_EXPIRATION, }; -use fil_actor_power::{CreateMinerParams, Method as PowerMethod}; +use fil_actor_power::{CreateMinerParams, Method as PowerMethod, State as PowerState}; use fil_actors_runtime::runtime::Policy; use fil_actors_runtime::{ @@ -19,12 +19,12 @@ use fvm_shared::sector::{RegisteredPoStProof, RegisteredSealProof}; use fvm_shared::METHOD_SEND; use num_traits::Zero; use vm_api::trace::ExpectInvocation; -use vm_api::util::{apply_ok, serialize_ok}; +use vm_api::util::{apply_ok, DynBlockstore}; use vm_api::VM; use crate::expects::Expect; use crate::util::{ - assert_invariants, create_accounts, create_miner, expect_invariants, + assert_invariants, create_accounts, create_miner, create_miner_internal, expect_invariants, invariant_failure_patterns, miner_dline_info, miner_precommit_one_sector_v2, PrecommitMetadata, }; use crate::{FIRST_TEST_USER_ADDR, TEST_FAUCET_ADDR}; @@ -32,17 +32,12 @@ use crate::{FIRST_TEST_USER_ADDR, TEST_FAUCET_ADDR}; #[vm_test] pub fn power_create_miner_test(v: &dyn VM) { let owner = Address::new_bls(&[1; fvm_shared::address::BLS_PUB_LEN]).unwrap(); - v.execute_message( - &TEST_FAUCET_ADDR, - &owner, - &TokenAmount::from_atto(10_000u32), - METHOD_SEND, - None, - ) - .unwrap(); + let value = TokenAmount::from_atto(10_000u32); + v.execute_message(&TEST_FAUCET_ADDR, &owner, &value, METHOD_SEND, None).unwrap(); + + let post_proof = RegisteredPoStProof::StackedDRGWindow32GiBV1P1; let multiaddrs = vec![BytesDe("multiaddr".as_bytes().to_vec())]; let peer_id = "miner".as_bytes().to_vec(); - let post_proof = RegisteredPoStProof::StackedDRGWindow32GiBV1P1; let params = CreateMinerParams { owner, worker: owner, @@ -50,18 +45,11 @@ pub fn power_create_miner_test(v: &dyn VM) { peer: peer_id.clone(), multiaddrs: multiaddrs.clone(), }; - - let res = v - .execute_message( - &owner, - &STORAGE_POWER_ACTOR_ADDR, - &TokenAmount::from_atto(1000u32), - PowerMethod::CreateMiner as u64, - Some(serialize_ok(¶ms)), - ) - .unwrap(); + let res = create_miner_internal(v, ¶ms, &value); let owner_id = v.resolve_id_address(&owner).unwrap().id().unwrap(); + let state = PowerState::new(&DynBlockstore::wrap(v.blockstore())).unwrap(); + let network_qap = state.this_epoch_qa_power_smoothed.clone(); let expect = ExpectInvocation { // send to power actor from: owner_id, @@ -88,6 +76,7 @@ pub fn power_create_miner_test(v: &dyn VM) { peer_id, control_addresses: vec![], multi_addresses: multiaddrs, + network_qap, }) .unwrap(), ), diff --git a/integration_tests/src/util/mod.rs b/integration_tests/src/util/mod.rs index 440eb0ffc..839d14e46 100644 --- a/integration_tests/src/util/mod.rs +++ b/integration_tests/src/util/mod.rs @@ -41,6 +41,7 @@ use crate::{MinerBalances, NetworkStats, TEST_FAUCET_ADDR}; mod workflows; const ACCOUNT_SEED: u64 = 93837778; +pub const CREATE_MINER_DEPOSIT: u128 = 319999994978159820800; /// Returns addresses of created accounts in ID format pub fn create_accounts(v: &dyn VM, count: u64, balance: &TokenAmount) -> Vec
{ diff --git a/integration_tests/src/util/workflows.rs b/integration_tests/src/util/workflows.rs index 2ff417c3c..65d1fbbb8 100644 --- a/integration_tests/src/util/workflows.rs +++ b/integration_tests/src/util/workflows.rs @@ -90,6 +90,7 @@ use super::make_bitfield; use super::market_pending_deal_allocations_raw; use super::miner_dline_info; use super::sector_deadline; +use super::CREATE_MINER_DEPOSIT; pub fn cron_tick(v: &dyn VM) { apply_ok_implicit( @@ -102,6 +103,17 @@ pub fn cron_tick(v: &dyn VM) { ); } +pub fn owner_add_create_miner_deposit(v: &dyn VM, owner: &Address) { + apply_ok( + v, + &TEST_FAUCET_ADDR, + owner, + &TokenAmount::from_atto(CREATE_MINER_DEPOSIT), + fvm_shared::METHOD_SEND, + None::, + ); +} + pub fn create_miner( v: &dyn VM, owner: &Address, @@ -118,22 +130,57 @@ pub fn create_miner( peer: peer_id, multiaddrs, }; + let res: CreateMinerReturn = + create_miner_internal(v, ¶ms, balance).ret.unwrap().deserialize().unwrap(); + (res.id_address, res.robust_address) +} + +pub fn create_miner_internal( + v: &dyn VM, + params: &CreateMinerParams, + balance: &TokenAmount, +) -> vm_api::MessageResult { + let owner = ¶ms.owner; + // sent deposit to owner + owner_add_create_miner_deposit(v, owner); + let deposit = TokenAmount::from_atto(CREATE_MINER_DEPOSIT); let params = IpldBlock::serialize_cbor(¶ms).unwrap().unwrap(); - let res: CreateMinerReturn = v + let ret = v .execute_message( owner, &STORAGE_POWER_ACTOR_ADDR, - balance, + &deposit, PowerMethod::CreateMiner as u64, Some(params), ) - .unwrap() - .ret - .unwrap() - .deserialize() .unwrap(); - (res.id_address, res.robust_address) + + let res: CreateMinerReturn = ret.ret.as_ref().unwrap().deserialize().unwrap(); + + let wrap_store = DynBlockstore::wrap(v.blockstore()); + vm_api::util::mutate_state(v, &res.id_address, |st: &mut MinerState| { + // checkcreate miner deposit + assert!(st.load_vesting_funds(&wrap_store).unwrap().funds.len() == 180); + assert!(st.locked_funds == TokenAmount::from_atto(CREATE_MINER_DEPOSIT)); + + // reset create miner deposit vesting funds + st.save_vesting_funds(&wrap_store, &fil_actor_miner::VestingFunds::new()).unwrap(); + st.locked_funds = TokenAmount::zero(); + }); + + let state: MinerState = get_state(v, &res.id_address).unwrap(); + assert!(state.load_vesting_funds(&wrap_store).unwrap().funds.is_empty()); + assert!(state.locked_funds.is_zero()); + + let mut actor_state = v.actor(&res.id_address).unwrap(); + actor_state.balance = balance.clone(); + v.set_actor(&res.id_address, actor_state); + + let actual_balance = v.balance(&res.id_address); + assert_eq!(&actual_balance, balance); + + ret } #[allow(clippy::too_many_arguments)] diff --git a/vm_api/src/util/mod.rs b/vm_api/src/util/mod.rs index eed336c03..e5c7d6cb6 100644 --- a/vm_api/src/util/mod.rs +++ b/vm_api/src/util/mod.rs @@ -72,6 +72,8 @@ pub fn apply_ok_implicit( assert_eq!(code, res.code, "expected code {}, got {} ({})", code, res.code, res.message); res.ret.map_or(RawBytes::default(), |b| RawBytes::new(b.data)) } + +/// Convenience function to get the state of an actor pub fn get_state(v: &dyn VM, a: &Address) -> Option { let cid = v.actor(a).unwrap().state; v.blockstore().get(&cid).unwrap().map(|slice| fvm_ipld_encoding::from_slice(&slice).unwrap())