From a4bbe269b7c5d3b2da335b1846da74e9794b5643 Mon Sep 17 00:00:00 2001 From: qians Date: Tue, 26 Mar 2024 17:50:37 +0800 Subject: [PATCH] feat(FIP-0077): add create miner deposit --- actors/miner/src/lib.rs | 55 ++++++- actors/miner/src/state.rs | 1 + actors/miner/src/types.rs | 1 + .../tests/miner_actor_test_construction.rs | 144 +++++++++++++++++- actors/miner/tests/util.rs | 66 +++++++- actors/power/src/ext.rs | 2 + actors/power/src/lib.rs | 2 + actors/power/tests/harness/mod.rs | 5 + actors/power/tests/power_actor_tests.rs | 13 +- .../src/tests/power_scenario_tests.rs | 35 ++--- integration_tests/src/util/mod.rs | 1 + integration_tests/src/util/workflows.rs | 61 +++++++- vm_api/src/util/mod.rs | 2 + 13 files changed, 346 insertions(+), 42 deletions(-) diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 7a5587b7a..831441820 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -47,7 +47,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::{ ActorContext, ActorDowncast, ActorError, AsActorError, BURNT_FUNDS_ACTOR_ADDR, BatchReturn, @@ -185,6 +185,19 @@ 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 sector_size = params + .window_post_proof_type + .sector_size() + .map_err(|e| actor_error!(illegal_argument, "invalid sector size: {}", e))?; + + let deposit = calculate_create_miner_deposit(rt, params.network_qap, sector_size)?; + 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) })?; @@ -243,7 +256,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(()) } @@ -762,6 +778,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. @@ -5627,6 +5644,40 @@ 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, + sector_size: SectorSize, +) -> 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, + }; + + 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 2fc5c0cbd..014ebb230 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -208,6 +208,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( diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index e8e2d24f8..1299f9043 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 f15e3570a..b7d5673d8 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::INIT_ACTOR_ADDR; -use fil_actors_runtime::test_utils::*; - 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::{INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR}; +use fil_actors_runtime::{STORAGE_POWER_ACTOR_ADDR, test_utils::*}; 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(798245441765376000u64)); 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.vesting_funds.load(&env.rt.store).unwrap().len()); assert_ne!(Cid::default(), state.pre_committed_sectors); assert_ne!(Cid::default(), state.sectors); + // reset create miner deposit vesting funds + state.vesting_funds = Default::default(); + state.locked_funds = TokenAmount::zero(); + env.rt.replace_state(&state); + + let state = env.rt.get_state::(); + let create_depost_vesting_funds = state.vesting_funds.load(&env.rt.store).unwrap(); + assert!(create_depost_vesting_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 842dde8bb..2697ba308 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::{ @@ -156,6 +156,8 @@ pub struct ActorHarness { pub epoch_reward_smooth: FilterEstimate, pub epoch_qa_power_smooth: FilterEstimate, + pub create_depost: TokenAmount, + pub options: HarnessOptions, } @@ -205,6 +207,8 @@ impl ActorHarness { epoch_reward_smooth: FilterEstimate::new(rwd.atto().clone(), BigInt::from(0)), epoch_qa_power_smooth: FilterEstimate::new(pwr, BigInt::from(0)), + create_depost: TokenAmount::from_atto(798245441765376000u64), + options, } } @@ -221,6 +225,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.vesting_funds.load(&rt.store).unwrap(); + + // create miner deposit + assert!(create_depost_vesting_funds.len() == 180); + assert!(st.locked_funds == self.create_depost); + + // reset create miner deposit vesting funds + st.vesting_funds = Default::default(); + 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.vesting_funds.load(&rt.store).unwrap(); + + assert!(create_depost_vesting_funds.is_empty()); + assert!(st.locked_funds.is_zero()); + } + pub fn new_runtime(&self) -> MockRuntime { let mut rt = MockRuntime::default(); @@ -247,6 +272,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, @@ -254,6 +297,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); @@ -262,8 +306,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, @@ -280,6 +341,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) { @@ -1991,7 +2053,7 @@ impl ActorHarness { expect_burn(rt, penalty.clone()); } - 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 3bff45dca..1fe37d3e5 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 8fa7d541c..06444b74f 100644 --- a/actors/power/src/lib.rs +++ b/actors/power/src/lib.rs @@ -83,6 +83,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, @@ -90,6 +91,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 99c53d51f..fb59b0e3c 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 8040c20fc..72b17c2bd 100644 --- a/actors/power/tests/power_actor_tests.rs +++ b/actors/power/tests/power_actor_tests.rs @@ -101,6 +101,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 { @@ -110,6 +113,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(), }; @@ -1058,7 +1062,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( @@ -1070,6 +1077,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 { @@ -1079,6 +1089,7 @@ fn create_miner_restricted_correctly() { window_post_proof_type: RegisteredPoStProof::StackedDRGWinning2KiBV1, peer_id: peer, multi_addresses: multiaddrs, + network_qap, }) .unwrap(), }; @@ -1087,7 +1098,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 0f96e9412..2bc580c6a 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::{ MIN_SECTOR_EXPIRATION, Method as MinerMethod, MinerConstructorParams, max_prove_commit_duration, }; -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::{ @@ -20,29 +20,24 @@ use fvm_shared::sector::{RegisteredPoStProof, RegisteredSealProof}; use num_traits::Zero; use vm_api::VM; use vm_api::trace::ExpectInvocation; -use vm_api::util::{apply_ok, serialize_ok}; +use vm_api::util::{DynBlockstore, apply_ok}; use crate::expects::Expect; use crate::util::{ - PrecommitMetadata, assert_invariants, create_accounts, create_miner, expect_invariants, - invariant_failure_patterns, miner_dline_info, miner_precommit_one_sector_v2, + PrecommitMetadata, assert_invariants, create_accounts, create_miner, create_miner_internal, + expect_invariants, invariant_failure_patterns, miner_dline_info, miner_precommit_one_sector_v2, }; 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 be9fd82ac..60d13daf6 100644 --- a/integration_tests/src/util/mod.rs +++ b/integration_tests/src/util/mod.rs @@ -40,6 +40,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 94997bdac..b5e9094a3 100644 --- a/integration_tests/src/util/workflows.rs +++ b/integration_tests/src/util/workflows.rs @@ -85,6 +85,7 @@ use vm_api::util::{apply_code, apply_ok, apply_ok_implicit}; use crate::expects::Expect; use crate::*; +use super::CREATE_MINER_DEPOSIT; use super::make_bitfield; use super::market_pending_deal_allocations_raw; use super::miner_dline_info; @@ -101,6 +102,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, @@ -117,22 +129,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.vesting_funds.load(&wrap_store).unwrap().len() == 180); + assert!(st.locked_funds == TokenAmount::from_atto(CREATE_MINER_DEPOSIT)); + + // reset create miner deposit vesting funds + st.vesting_funds = Default::default(); + st.locked_funds = TokenAmount::zero(); + }); + + let state: MinerState = get_state(v, &res.id_address).unwrap(); + assert!(state.vesting_funds.load(&wrap_store).unwrap().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 8e0052929..45529ebab 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())