Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/add miner deposit v3 #1535

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 61 additions & 2 deletions actors/miner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -181,6 +181,14 @@ impl Actor {
check_peer_info(rt.policy(), &params.peer_id, &params.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(&params.owner).ok_or_else(|| {
actor_error!(illegal_argument, "unable to resolve owner address: {}", params.owner)
})?;
Expand Down Expand Up @@ -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(())
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<TokenAmount, ActorError> {
// 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<PieceActivationManifest>,
pub sector_expiry: ChainEpoch,
Expand Down
3 changes: 3 additions & 0 deletions actors/miner/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions actors/miner/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub struct MinerConstructorParams {
#[serde(with = "strict_bytes")]
pub peer_id: Vec<u8>,
pub multi_addresses: Vec<BytesDe>,
pub network_qap: FilterEstimate,
}

#[derive(Serialize_tuple, Deserialize_tuple)]
Expand Down
144 changes: 137 additions & 7 deletions actors/miner/tests/miner_actor_test_construction.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -27,10 +30,17 @@ struct TestEnv {
control_addrs: Vec<Address>,
peer_id: Vec<u8>,
multiaddrs: Vec<BytesDe>,
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),
Expand All @@ -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(),
};

Expand All @@ -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
}

Expand All @@ -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(&current_reward).unwrap(),
ExitCode::OK,
);
env.rt.expect_send_simple(
STORAGE_POWER_ACTOR_ADDR,
PowerMethod::CurrentTotalPower as u64,
Default::default(),
TokenAmount::zero(),
IpldBlock::serialize_cbor(&current_total_power).unwrap(),
ExitCode::OK,
);
env.rt.expect_send_simple(
env.worker,
AccountMethod::PubkeyAddress as u64,
Expand All @@ -87,7 +132,7 @@ fn simple_construction() {
expect_empty(result);
env.rt.verify();

let state = env.rt.get_state::<State>();
let mut state = env.rt.get_state::<State>();

let info = state.get_info(&env.rt.store).unwrap();
assert_eq!(env.owner, info.owner);
Expand All @@ -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::<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);
Expand All @@ -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(&current_reward).unwrap(),
ExitCode::OK,
);
env.rt.expect_send_simple(
STORAGE_POWER_ACTOR_ADDR,
PowerMethod::CurrentTotalPower as u64,
Default::default(),
TokenAmount::zero(),
IpldBlock::serialize_cbor(&current_total_power).unwrap(),
ExitCode::OK,
);

expect_abort(
ExitCode::USR_INSUFFICIENT_FUNDS,
env.rt
.call::<Actor>(Method::Constructor as u64, IpldBlock::serialize_cbor(&params).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);
Expand All @@ -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(&current_reward).unwrap(),
ExitCode::OK,
);
env.rt.expect_send_simple(
STORAGE_POWER_ACTOR_ADDR,
PowerMethod::CurrentTotalPower as u64,
Default::default(),
TokenAmount::zero(),
IpldBlock::serialize_cbor(&current_total_power).unwrap(),
ExitCode::OK,
);
env.rt.expect_send_simple(
env.worker,
AccountMethod::PubkeyAddress as u64,
Expand Down
Loading