Skip to content

Commit fb28a97

Browse files
committed
feat(FIP-0077): add create miner deposit
1 parent b4ed8cf commit fb28a97

File tree

13 files changed

+358
-48
lines changed

13 files changed

+358
-48
lines changed

Diff for: actors/miner/src/lib.rs

+61-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub use expiration_queue::*;
4646
use fil_actors_runtime::cbor::{serialize, serialize_vec};
4747
use fil_actors_runtime::reward::{FilterEstimate, ThisEpochRewardReturn};
4848
use fil_actors_runtime::runtime::builtins::Type;
49-
use fil_actors_runtime::runtime::policy_constants::MAX_SECTOR_NUMBER;
49+
use fil_actors_runtime::runtime::policy_constants::{MAX_SECTOR_NUMBER, MINIMUM_CONSENSUS_POWER};
5050
use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime};
5151
use fil_actors_runtime::{
5252
actor_dispatch, actor_error, deserialize_block, extract_send_result, util, ActorContext,
@@ -181,6 +181,14 @@ impl Actor {
181181
check_peer_info(rt.policy(), &params.peer_id, &params.multi_addresses)?;
182182
check_valid_post_proof_type(rt.policy(), params.window_post_proof_type)?;
183183

184+
let balance = rt.current_balance();
185+
let deposit = calculate_create_miner_deposit(rt, params.network_qap)?;
186+
if balance < deposit {
187+
return Err(actor_error!(insufficient_funds;
188+
"not enough balance to lock for create miner deposit: \
189+
sent balance {} < deposit {}", balance.atto(), deposit.atto()));
190+
}
191+
184192
let owner = rt.resolve_address(&params.owner).ok_or_else(|| {
185193
actor_error!(illegal_argument, "unable to resolve owner address: {}", params.owner)
186194
})?;
@@ -239,7 +247,10 @@ impl Actor {
239247
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state")
240248
})?;
241249

242-
let st = State::new(policy, rt.store(), info_cid, period_start, deadline_idx)?;
250+
let store = rt.store();
251+
let mut st = State::new(policy, store, info_cid, period_start, deadline_idx)?;
252+
st.add_locked_funds(store, rt.curr_epoch(), &deposit, &REWARD_VESTING_SPEC)
253+
.map_err(|e| actor_error!(illegal_state, e))?;
243254
rt.create(&st)?;
244255
Ok(())
245256
}
@@ -756,6 +767,7 @@ impl Actor {
756767

757768
Ok(())
758769
}
770+
759771
/// Checks state of the corresponding sector pre-commitments and verifies aggregate proof of replication
760772
/// of these sectors. If valid, the sectors' deals are activated, sectors are assigned a deadline and charged pledge
761773
/// and precommit state is removed.
@@ -5642,6 +5654,53 @@ fn activate_new_sector_infos(
56425654
Ok(())
56435655
}
56445656

5657+
/// Calculate create miner deposit by MINIMUM_CONSENSUS_POWER x StateMinerInitialPledgeCollateral
5658+
/// See FIP-0077, https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0077.md
5659+
pub fn calculate_create_miner_deposit(
5660+
rt: &impl Runtime,
5661+
network_qap: FilterEstimate,
5662+
) -> Result<TokenAmount, ActorError> {
5663+
// set network pledge inputs
5664+
let rew = request_current_epoch_block_reward(rt)?;
5665+
let pwr = request_current_total_power(rt)?;
5666+
let circulating_supply = rt.total_fil_circ_supply();
5667+
let pledge_inputs = NetworkPledgeInputs {
5668+
network_qap,
5669+
network_baseline: rew.this_epoch_baseline_power,
5670+
circulating_supply,
5671+
epoch_reward: rew.this_epoch_reward_smoothed,
5672+
epochs_since_ramp_start: rt.curr_epoch() - pwr.ramp_start_epoch,
5673+
ramp_duration_epochs: pwr.ramp_duration_epochs,
5674+
};
5675+
5676+
/// set sector size with min power
5677+
#[cfg(feature = "min-power-2k")]
5678+
let sector_size = SectorSize::_2KiB;
5679+
#[cfg(feature = "min-power-2g")]
5680+
let sector_size = SectorSize::_8MiB;
5681+
#[cfg(feature = "min-power-32g")]
5682+
let sector_size = SectorSize::_512MiB;
5683+
#[cfg(not(any(
5684+
feature = "min-power-2k",
5685+
feature = "min-power-2g",
5686+
feature = "min-power-32g"
5687+
)))]
5688+
let sector_size = SectorSize::_32GiB;
5689+
5690+
let sector_number = MINIMUM_CONSENSUS_POWER / sector_size as i64;
5691+
let power = qa_power_for_weight(sector_size, MIN_SECTOR_EXPIRATION, &BigInt::zero());
5692+
let sector_initial_pledge = initial_pledge_for_power(
5693+
&power,
5694+
&pledge_inputs.network_baseline,
5695+
&pledge_inputs.epoch_reward,
5696+
&pledge_inputs.network_qap,
5697+
&pledge_inputs.circulating_supply,
5698+
pledge_inputs.epochs_since_ramp_start,
5699+
pledge_inputs.ramp_duration_epochs,
5700+
);
5701+
Ok(sector_initial_pledge * sector_number)
5702+
}
5703+
56455704
pub struct SectorPiecesActivationInput {
56465705
pub piece_manifests: Vec<PieceActivationManifest>,
56475706
pub sector_expiry: ChainEpoch,

Diff for: actors/miner/src/state.rs

+3
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ impl State {
214214
pub fn deadline_info(&self, policy: &Policy, current_epoch: ChainEpoch) -> DeadlineInfo {
215215
new_deadline_info_from_offset_and_epoch(policy, self.proving_period_start, current_epoch)
216216
}
217+
217218
// Returns deadline calculations for the state recorded proving period and deadline.
218219
// This is out of date if the a miner does not have an active miner cron
219220
pub fn recorded_deadline_info(
@@ -877,6 +878,7 @@ impl State {
877878
amount_unlocked
878879
));
879880
}
881+
880882
// add locked funds now
881883
vesting_funds.add_locked_funds(current_epoch, vesting_sum, self.proving_period_start, spec);
882884
self.locked_funds += vesting_sum;
@@ -937,6 +939,7 @@ impl State {
937939

938940
Ok(std::mem::take(&mut self.fee_debt))
939941
}
942+
940943
/// Unlocks an amount of funds that have *not yet vested*, if possible.
941944
/// The soonest-vesting entries are unlocked first.
942945
/// Returns the amount actually unlocked.

Diff for: actors/miner/src/types.rs

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub struct MinerConstructorParams {
4545
#[serde(with = "strict_bytes")]
4646
pub peer_id: Vec<u8>,
4747
pub multi_addresses: Vec<BytesDe>,
48+
pub network_qap: FilterEstimate,
4849
}
4950

5051
#[derive(Serialize_tuple, Deserialize_tuple)]

Diff for: actors/miner/tests/miner_actor_test_construction.rs

+137-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
use fil_actors_runtime::test_utils::*;
2-
use fil_actors_runtime::INIT_ACTOR_ADDR;
3-
41
use fil_actor_account::Method as AccountMethod;
52
use fil_actor_miner::{
63
Actor, Deadline, Deadlines, Method, MinerConstructorParams as ConstructorParams, State,
74
};
5+
use fil_actor_power::{CurrentTotalPowerReturn, Method as PowerMethod};
6+
use fil_actor_reward::{Method as RewardMethod, ThisEpochRewardReturn};
7+
use fil_actors_runtime::reward::FilterEstimate;
8+
use fil_actors_runtime::{test_utils::*, STORAGE_POWER_ACTOR_ADDR};
9+
use fil_actors_runtime::{INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR};
810

911
use fvm_ipld_encoding::{BytesDe, CborStore};
1012
use fvm_shared::address::Address;
13+
use fvm_shared::bigint::BigInt;
1114
use fvm_shared::econ::TokenAmount;
1215
use fvm_shared::error::ExitCode;
13-
use fvm_shared::sector::{RegisteredPoStProof, SectorSize};
16+
use fvm_shared::sector::{RegisteredPoStProof, SectorSize, StoragePower};
1417

1518
use cid::Cid;
1619
use fvm_ipld_encoding::ipld_block::IpldBlock;
17-
use num_traits::Zero;
20+
use num_traits::{FromPrimitive, Zero};
1821

1922
mod util;
2023

@@ -27,10 +30,17 @@ struct TestEnv {
2730
control_addrs: Vec<Address>,
2831
peer_id: Vec<u8>,
2932
multiaddrs: Vec<BytesDe>,
33+
power: StoragePower,
34+
reward: TokenAmount,
35+
epoch_reward_smooth: FilterEstimate,
3036
rt: MockRuntime,
3137
}
3238

3339
fn prepare_env() -> TestEnv {
40+
let reward = TokenAmount::from_whole(10);
41+
let power = StoragePower::from_i128(1 << 50).unwrap();
42+
let epoch_reward_smooth = FilterEstimate::new(reward.atto().clone(), BigInt::from(0u8));
43+
3444
let mut env = TestEnv {
3545
receiver: Address::new_id(1000),
3646
owner: Address::new_id(100),
@@ -39,6 +49,9 @@ fn prepare_env() -> TestEnv {
3949
control_addrs: vec![Address::new_id(999), Address::new_id(998)],
4050
peer_id: vec![1, 2, 3],
4151
multiaddrs: vec![BytesDe(vec![1, 2, 3])],
52+
power,
53+
reward,
54+
epoch_reward_smooth,
4255
rt: MockRuntime::default(),
4356
};
4457

@@ -50,6 +63,8 @@ fn prepare_env() -> TestEnv {
5063
env.rt.hash_func = Box::new(hash);
5164
env.rt.caller.replace(INIT_ACTOR_ADDR);
5265
env.rt.caller_type.replace(*INIT_ACTOR_CODE_ID);
66+
// add balance for create miner deposit
67+
env.rt.add_balance(TokenAmount::from_atto(633318697598976000u64));
5368
env
5469
}
5570

@@ -61,16 +76,46 @@ fn constructor_params(env: &TestEnv) -> ConstructorParams {
6176
window_post_proof_type: RegisteredPoStProof::StackedDRGWindow32GiBV1P1,
6277
peer_id: env.peer_id.clone(),
6378
multi_addresses: env.multiaddrs.clone(),
79+
network_qap: env.epoch_reward_smooth.clone(),
6480
}
6581
}
6682

6783
#[test]
6884
fn simple_construction() {
6985
let env = prepare_env();
86+
let current_reward = ThisEpochRewardReturn {
87+
this_epoch_baseline_power: env.power.clone(),
88+
this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(),
89+
};
90+
let current_total_power = CurrentTotalPowerReturn {
91+
raw_byte_power: Default::default(),
92+
quality_adj_power: Default::default(),
93+
pledge_collateral: Default::default(),
94+
quality_adj_power_smoothed: Default::default(),
95+
ramp_start_epoch: Default::default(),
96+
ramp_duration_epochs: Default::default(),
97+
};
98+
7099
let params = constructor_params(&env);
71100

72101
env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
73102
env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);
103+
env.rt.expect_send_simple(
104+
REWARD_ACTOR_ADDR,
105+
RewardMethod::ThisEpochReward as u64,
106+
None,
107+
TokenAmount::zero(),
108+
IpldBlock::serialize_cbor(&current_reward).unwrap(),
109+
ExitCode::OK,
110+
);
111+
env.rt.expect_send_simple(
112+
STORAGE_POWER_ACTOR_ADDR,
113+
PowerMethod::CurrentTotalPower as u64,
114+
Default::default(),
115+
TokenAmount::zero(),
116+
IpldBlock::serialize_cbor(&current_total_power).unwrap(),
117+
ExitCode::OK,
118+
);
74119
env.rt.expect_send_simple(
75120
env.worker,
76121
AccountMethod::PubkeyAddress as u64,
@@ -87,7 +132,7 @@ fn simple_construction() {
87132
expect_empty(result);
88133
env.rt.verify();
89134

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

92137
let info = state.get_info(&env.rt.store).unwrap();
93138
assert_eq!(env.owner, info.owner);
@@ -100,10 +145,21 @@ fn simple_construction() {
100145
assert_eq!(2349, info.window_post_partition_sectors);
101146

102147
assert_eq!(TokenAmount::zero(), state.pre_commit_deposits);
103-
assert_eq!(TokenAmount::zero(), state.locked_funds);
148+
assert_eq!(TokenAmount::from_atto(633318697598976000u64), state.locked_funds);
149+
assert_eq!(180, state.load_vesting_funds(&env.rt.store).unwrap().funds.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.save_vesting_funds(&env.rt.store, &fil_actor_miner::VestingFunds::new()).unwrap();
155+
state.locked_funds = TokenAmount::zero();
156+
env.rt.replace_state(&state);
157+
158+
let state = env.rt.get_state::<State>();
159+
let create_depost_vesting_funds = state.load_vesting_funds(&env.rt.store).unwrap();
160+
assert!(create_depost_vesting_funds.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,67 @@ 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 {
196+
raw_byte_power: Default::default(),
197+
quality_adj_power: Default::default(),
198+
pledge_collateral: Default::default(),
199+
quality_adj_power_smoothed: Default::default(),
200+
ramp_start_epoch: Default::default(),
201+
ramp_duration_epochs: Default::default(),
202+
};
203+
204+
let params = constructor_params(&env);
205+
206+
env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
207+
env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);
208+
env.rt.expect_send_simple(
209+
REWARD_ACTOR_ADDR,
210+
RewardMethod::ThisEpochReward as u64,
211+
None,
212+
TokenAmount::zero(),
213+
IpldBlock::serialize_cbor(&current_reward).unwrap(),
214+
ExitCode::OK,
215+
);
216+
env.rt.expect_send_simple(
217+
STORAGE_POWER_ACTOR_ADDR,
218+
PowerMethod::CurrentTotalPower as u64,
219+
Default::default(),
220+
TokenAmount::zero(),
221+
IpldBlock::serialize_cbor(&current_total_power).unwrap(),
222+
ExitCode::OK,
223+
);
224+
225+
expect_abort(
226+
ExitCode::USR_INSUFFICIENT_FUNDS,
227+
env.rt
228+
.call::<Actor>(Method::Constructor as u64, IpldBlock::serialize_cbor(&params).unwrap()),
229+
);
230+
env.rt.verify();
231+
}
232+
131233
#[test]
132234
fn control_addresses_are_resolved_during_construction() {
133235
let mut env = prepare_env();
236+
let current_reward = ThisEpochRewardReturn {
237+
this_epoch_baseline_power: env.power.clone(),
238+
this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(),
239+
};
240+
let current_total_power = CurrentTotalPowerReturn {
241+
raw_byte_power: Default::default(),
242+
quality_adj_power: Default::default(),
243+
pledge_collateral: Default::default(),
244+
quality_adj_power_smoothed: Default::default(),
245+
ramp_start_epoch: Default::default(),
246+
ramp_duration_epochs: Default::default(),
247+
};
134248

135249
let control1 = new_bls_addr(1);
136250
let control1id = Address::new_id(555);
@@ -146,6 +260,22 @@ fn control_addresses_are_resolved_during_construction() {
146260
let params = constructor_params(&env);
147261
env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
148262
env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);
263+
env.rt.expect_send_simple(
264+
REWARD_ACTOR_ADDR,
265+
RewardMethod::ThisEpochReward as u64,
266+
None,
267+
TokenAmount::zero(),
268+
IpldBlock::serialize_cbor(&current_reward).unwrap(),
269+
ExitCode::OK,
270+
);
271+
env.rt.expect_send_simple(
272+
STORAGE_POWER_ACTOR_ADDR,
273+
PowerMethod::CurrentTotalPower as u64,
274+
Default::default(),
275+
TokenAmount::zero(),
276+
IpldBlock::serialize_cbor(&current_total_power).unwrap(),
277+
ExitCode::OK,
278+
);
149279
env.rt.expect_send_simple(
150280
env.worker,
151281
AccountMethod::PubkeyAddress as u64,

0 commit comments

Comments
 (0)