Skip to content

Commit 00db828

Browse files
authored
FIP-0077: Add Cost Opportunity For New Miner Creation (#1535)
* feat(FIP-0077): add create miner deposit * chore: remove magic numbers in tests * feat: modify the deposit amount to 10% of the previous * chore: address review comments
1 parent 747a3ed commit 00db828

File tree

13 files changed

+323
-46
lines changed

13 files changed

+323
-46
lines changed

actors/miner/src/ext.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ pub mod power {
9393
pub const SUBMIT_POREP_FOR_BULK_VERIFY_METHOD: u64 = 8;
9494
pub const CURRENT_TOTAL_POWER_METHOD: u64 = 9;
9595

96-
#[derive(Serialize_tuple, Deserialize_tuple)]
96+
#[derive(Serialize_tuple, Deserialize_tuple, Default)]
9797
pub struct CurrentTotalPowerReturn {
9898
#[serde(with = "bigint_ser")]
9999
pub raw_byte_power: StoragePower,
@@ -104,6 +104,7 @@ pub mod power {
104104
pub ramp_start_epoch: i64,
105105
pub ramp_duration_epochs: u64,
106106
}
107+
107108
#[derive(Serialize_tuple, Deserialize_tuple)]
108109
pub struct EnrollCronEventParams {
109110
pub event_epoch: ChainEpoch,

actors/miner/src/lib.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ pub use expiration_queue::*;
4747
use fil_actors_runtime::cbor::{serialize, serialize_vec};
4848
use fil_actors_runtime::reward::{FilterEstimate, ThisEpochRewardReturn};
4949
use fil_actors_runtime::runtime::builtins::Type;
50-
use fil_actors_runtime::runtime::policy_constants::MAX_SECTOR_NUMBER;
50+
use fil_actors_runtime::runtime::policy_constants::{
51+
CREATE_MINER_DEPOSIT_POWER, MAX_SECTOR_NUMBER,
52+
};
5153
use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime};
5254
use fil_actors_runtime::{
5355
ActorContext, ActorDowncast, ActorError, AsActorError, BURNT_FUNDS_ACTOR_ADDR, BatchReturn,
@@ -185,6 +187,14 @@ impl Actor {
185187
check_peer_info(rt.policy(), &params.peer_id, &params.multi_addresses)?;
186188
check_valid_post_proof_type(rt.policy(), params.window_post_proof_type)?;
187189

190+
let balance = rt.current_balance();
191+
let deposit = calculate_create_miner_deposit(rt)?;
192+
if balance < deposit {
193+
return Err(actor_error!(insufficient_funds;
194+
"not enough balance to lock for create miner deposit: \
195+
sent balance {} < deposit {}", balance.atto(), deposit.atto()));
196+
}
197+
188198
let owner = rt.resolve_address(&params.owner).ok_or_else(|| {
189199
actor_error!(illegal_argument, "unable to resolve owner address: {}", params.owner)
190200
})?;
@@ -243,7 +253,10 @@ impl Actor {
243253
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state")
244254
})?;
245255

246-
let st = State::new(policy, rt.store(), info_cid, period_start, deadline_idx)?;
256+
let store = rt.store();
257+
let mut st = State::new(policy, store, info_cid, period_start, deadline_idx)?;
258+
st.add_locked_funds(store, rt.curr_epoch(), &deposit, &REWARD_VESTING_SPEC)
259+
.map_err(|e| actor_error!(illegal_state, e))?;
247260
rt.create(&st)?;
248261
Ok(())
249262
}
@@ -5248,6 +5261,23 @@ fn activate_new_sector_infos(
52485261
Ok(())
52495262
}
52505263

5264+
/// Calculate create miner deposit by MINIMUM_CONSENSUS_POWER x StateMinerInitialPledgeCollateral / 10
5265+
/// See FIP-0077, https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0077.md
5266+
pub fn calculate_create_miner_deposit(rt: &impl Runtime) -> Result<TokenAmount, ActorError> {
5267+
let rew = request_current_epoch_block_reward(rt)?;
5268+
let pwr = request_current_total_power(rt)?;
5269+
5270+
Ok(initial_pledge_for_power(
5271+
&BigInt::from(CREATE_MINER_DEPOSIT_POWER),
5272+
&rew.this_epoch_baseline_power,
5273+
&rew.this_epoch_reward_smoothed,
5274+
&pwr.quality_adj_power_smoothed,
5275+
&rt.total_fil_circ_supply(),
5276+
rt.curr_epoch() - pwr.ramp_start_epoch,
5277+
pwr.ramp_duration_epochs,
5278+
))
5279+
}
5280+
52515281
pub struct SectorPiecesActivationInput {
52525282
pub piece_manifests: Vec<PieceActivationManifest>,
52535283
pub sector_expiry: ChainEpoch,

actors/miner/src/state.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ impl State {
208208
pub fn deadline_info(&self, policy: &Policy, current_epoch: ChainEpoch) -> DeadlineInfo {
209209
new_deadline_info_from_offset_and_epoch(policy, self.proving_period_start, current_epoch)
210210
}
211+
211212
// Returns deadline calculations for the state recorded proving period and deadline.
212213
// This is out of date if the a miner does not have an active miner cron
213214
pub fn recorded_deadline_info(

actors/miner/tests/miner_actor_test_construction.rs

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1-
use fil_actors_runtime::INIT_ACTOR_ADDR;
2-
use fil_actors_runtime::test_utils::*;
1+
use fil_actors_runtime::reward::FilterEstimate;
2+
use fil_actors_runtime::{INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR};
3+
use fil_actors_runtime::{STORAGE_POWER_ACTOR_ADDR, test_utils::*};
34

45
use fil_actor_account::Method as AccountMethod;
56
use fil_actor_miner::{
67
Actor, Deadline, Deadlines, Method, MinerConstructorParams as ConstructorParams, State,
78
};
9+
use fil_actor_power::{CurrentTotalPowerReturn, Method as PowerMethod};
10+
use fil_actor_reward::{Method as RewardMethod, ThisEpochRewardReturn};
811

912
use fvm_ipld_encoding::{BytesDe, CborStore};
1013
use fvm_shared::address::Address;
14+
use fvm_shared::bigint::BigInt;
1115
use fvm_shared::econ::TokenAmount;
1216
use fvm_shared::error::ExitCode;
13-
use fvm_shared::sector::{RegisteredPoStProof, SectorSize};
17+
use fvm_shared::sector::{RegisteredPoStProof, SectorSize, StoragePower};
1418

1519
use cid::Cid;
1620
use fvm_ipld_encoding::ipld_block::IpldBlock;
17-
use num_traits::Zero;
21+
use num_traits::{FromPrimitive, Zero};
1822

1923
mod util;
24+
use util::create_miner_deposit_for_test;
2025

2126
#[allow(dead_code)]
2227
struct TestEnv {
@@ -27,10 +32,16 @@ struct TestEnv {
2732
control_addrs: Vec<Address>,
2833
peer_id: Vec<u8>,
2934
multiaddrs: Vec<BytesDe>,
35+
power: StoragePower,
36+
reward: TokenAmount,
37+
epoch_reward_smooth: FilterEstimate,
3038
rt: MockRuntime,
3139
}
3240

3341
fn prepare_env() -> TestEnv {
42+
let reward = TokenAmount::from_whole(10);
43+
let power = StoragePower::from_i128(1 << 50).unwrap();
44+
let epoch_reward_smooth = FilterEstimate::new(reward.atto().clone(), BigInt::from(0u8));
3445
let mut env = TestEnv {
3546
receiver: Address::new_id(1000),
3647
owner: Address::new_id(100),
@@ -39,6 +50,9 @@ fn prepare_env() -> TestEnv {
3950
control_addrs: vec![Address::new_id(999), Address::new_id(998)],
4051
peer_id: vec![1, 2, 3],
4152
multiaddrs: vec![BytesDe(vec![1, 2, 3])],
53+
power,
54+
reward,
55+
epoch_reward_smooth,
4256
rt: MockRuntime::default(),
4357
};
4458

@@ -50,6 +64,12 @@ fn prepare_env() -> TestEnv {
5064
env.rt.hash_func = Box::new(hash);
5165
env.rt.caller.replace(INIT_ACTOR_ADDR);
5266
env.rt.caller_type.replace(*INIT_ACTOR_CODE_ID);
67+
// add balance for create miner deposit
68+
env.rt.add_balance(create_miner_deposit_for_test(
69+
&env.rt,
70+
&env.power,
71+
&env.epoch_reward_smooth,
72+
));
5373
env
5474
}
5575

@@ -67,10 +87,32 @@ fn constructor_params(env: &TestEnv) -> ConstructorParams {
6787
#[test]
6888
fn simple_construction() {
6989
let env = prepare_env();
90+
let current_reward = ThisEpochRewardReturn {
91+
this_epoch_baseline_power: env.power.clone(),
92+
this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(),
93+
};
94+
let current_total_power = CurrentTotalPowerReturn::default();
95+
7096
let params = constructor_params(&env);
7197

7298
env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
7399
env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);
100+
env.rt.expect_send_simple(
101+
REWARD_ACTOR_ADDR,
102+
RewardMethod::ThisEpochReward as u64,
103+
None,
104+
TokenAmount::zero(),
105+
IpldBlock::serialize_cbor(&current_reward).unwrap(),
106+
ExitCode::OK,
107+
);
108+
env.rt.expect_send_simple(
109+
STORAGE_POWER_ACTOR_ADDR,
110+
PowerMethod::CurrentTotalPower as u64,
111+
Default::default(),
112+
TokenAmount::zero(),
113+
IpldBlock::serialize_cbor(&current_total_power).unwrap(),
114+
ExitCode::OK,
115+
);
74116
env.rt.expect_send_simple(
75117
env.worker,
76118
AccountMethod::PubkeyAddress as u64,
@@ -87,7 +129,7 @@ fn simple_construction() {
87129
expect_empty(result);
88130
env.rt.verify();
89131

90-
let state = env.rt.get_state::<State>();
132+
let mut state = env.rt.get_state::<State>();
91133

92134
let info = state.get_info(&env.rt.store).unwrap();
93135
assert_eq!(env.owner, info.owner);
@@ -100,10 +142,24 @@ fn simple_construction() {
100142
assert_eq!(2349, info.window_post_partition_sectors);
101143

102144
assert_eq!(TokenAmount::zero(), state.pre_commit_deposits);
103-
assert_eq!(TokenAmount::zero(), state.locked_funds);
145+
assert_eq!(
146+
create_miner_deposit_for_test(&env.rt, &env.power, &env.epoch_reward_smooth),
147+
state.locked_funds
148+
);
149+
assert_eq!(180, state.vesting_funds.load(&env.rt.store).unwrap().len());
104150
assert_ne!(Cid::default(), state.pre_committed_sectors);
105151
assert_ne!(Cid::default(), state.sectors);
106152

153+
// reset create miner deposit vesting funds
154+
state.vesting_funds = Default::default();
155+
state.locked_funds = TokenAmount::zero();
156+
env.rt.replace_state(&state);
157+
158+
let state = env.rt.get_state::<State>();
159+
let create_deposit_vesting_funds = state.vesting_funds.load(&env.rt.store).unwrap();
160+
assert!(create_deposit_vesting_funds.is_empty());
161+
assert!(state.locked_funds.is_zero());
162+
107163
// according to original specs-actors test, this is set by running the code; magic...
108164
let proving_period_start = -2222;
109165
assert_eq!(proving_period_start, state.proving_period_start);
@@ -128,9 +184,53 @@ fn simple_construction() {
128184
util::check_state_invariants_from_mock_runtime(&env.rt);
129185
}
130186

187+
#[test]
188+
fn fails_if_insufficient_to_cover_the_miner_creation_deposit() {
189+
let env = prepare_env();
190+
env.rt.set_balance(TokenAmount::zero());
191+
let current_reward = ThisEpochRewardReturn {
192+
this_epoch_baseline_power: env.power.clone(),
193+
this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(),
194+
};
195+
let current_total_power = CurrentTotalPowerReturn::default();
196+
197+
let params = constructor_params(&env);
198+
199+
env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
200+
env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);
201+
env.rt.expect_send_simple(
202+
REWARD_ACTOR_ADDR,
203+
RewardMethod::ThisEpochReward as u64,
204+
None,
205+
TokenAmount::zero(),
206+
IpldBlock::serialize_cbor(&current_reward).unwrap(),
207+
ExitCode::OK,
208+
);
209+
env.rt.expect_send_simple(
210+
STORAGE_POWER_ACTOR_ADDR,
211+
PowerMethod::CurrentTotalPower as u64,
212+
Default::default(),
213+
TokenAmount::zero(),
214+
IpldBlock::serialize_cbor(&current_total_power).unwrap(),
215+
ExitCode::OK,
216+
);
217+
218+
expect_abort(
219+
ExitCode::USR_INSUFFICIENT_FUNDS,
220+
env.rt
221+
.call::<Actor>(Method::Constructor as u64, IpldBlock::serialize_cbor(&params).unwrap()),
222+
);
223+
env.rt.verify();
224+
}
225+
131226
#[test]
132227
fn control_addresses_are_resolved_during_construction() {
133228
let mut env = prepare_env();
229+
let current_reward = ThisEpochRewardReturn {
230+
this_epoch_baseline_power: env.power.clone(),
231+
this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(),
232+
};
233+
let current_total_power = CurrentTotalPowerReturn::default();
134234

135235
let control1 = new_bls_addr(1);
136236
let control1id = Address::new_id(555);
@@ -146,6 +246,22 @@ fn control_addresses_are_resolved_during_construction() {
146246
let params = constructor_params(&env);
147247
env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
148248
env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);
249+
env.rt.expect_send_simple(
250+
REWARD_ACTOR_ADDR,
251+
RewardMethod::ThisEpochReward as u64,
252+
None,
253+
TokenAmount::zero(),
254+
IpldBlock::serialize_cbor(&current_reward).unwrap(),
255+
ExitCode::OK,
256+
);
257+
env.rt.expect_send_simple(
258+
STORAGE_POWER_ACTOR_ADDR,
259+
PowerMethod::CurrentTotalPower as u64,
260+
Default::default(),
261+
TokenAmount::zero(),
262+
IpldBlock::serialize_cbor(&current_total_power).unwrap(),
263+
ExitCode::OK,
264+
);
149265
env.rt.expect_send_simple(
150266
env.worker,
151267
AccountMethod::PubkeyAddress as u64,

0 commit comments

Comments
 (0)