Skip to content

Commit 5f764ed

Browse files
committed
feat(FIP-0077): add create miner deposit
1 parent b68427a commit 5f764ed

File tree

13 files changed

+346
-42
lines changed

13 files changed

+346
-42
lines changed

actors/miner/src/lib.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ 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::{MAX_SECTOR_NUMBER, MINIMUM_CONSENSUS_POWER};
5151
use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime};
5252
use fil_actors_runtime::{
5353
ActorContext, ActorDowncast, ActorError, AsActorError, BURNT_FUNDS_ACTOR_ADDR, BatchReturn,
@@ -185,6 +185,19 @@ impl Actor {
185185
check_peer_info(rt.policy(), &params.peer_id, &params.multi_addresses)?;
186186
check_valid_post_proof_type(rt.policy(), params.window_post_proof_type)?;
187187

188+
let balance = rt.current_balance();
189+
let sector_size = params
190+
.window_post_proof_type
191+
.sector_size()
192+
.map_err(|e| actor_error!(illegal_argument, "invalid sector size: {}", e))?;
193+
194+
let deposit = calculate_create_miner_deposit(rt, params.network_qap, sector_size)?;
195+
if balance < deposit {
196+
return Err(actor_error!(insufficient_funds;
197+
"not enough balance to lock for create miner deposit: \
198+
sent balance {} < deposit {}", balance.atto(), deposit.atto()));
199+
}
200+
188201
let owner = rt.resolve_address(&params.owner).ok_or_else(|| {
189202
actor_error!(illegal_argument, "unable to resolve owner address: {}", params.owner)
190203
})?;
@@ -243,7 +256,10 @@ impl Actor {
243256
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state")
244257
})?;
245258

246-
let st = State::new(policy, rt.store(), info_cid, period_start, deadline_idx)?;
259+
let store = rt.store();
260+
let mut st = State::new(policy, store, info_cid, period_start, deadline_idx)?;
261+
st.add_locked_funds(store, rt.curr_epoch(), &deposit, &REWARD_VESTING_SPEC)
262+
.map_err(|e| actor_error!(illegal_state, e))?;
247263
rt.create(&st)?;
248264
Ok(())
249265
}
@@ -762,6 +778,7 @@ impl Actor {
762778

763779
Ok(())
764780
}
781+
765782
/// Checks state of the corresponding sector pre-commitments and verifies aggregate proof of replication
766783
/// of these sectors. If valid, the sectors' deals are activated, sectors are assigned a deadline and charged pledge
767784
/// and precommit state is removed.
@@ -5627,6 +5644,40 @@ fn activate_new_sector_infos(
56275644
Ok(())
56285645
}
56295646

5647+
/// Calculate create miner deposit by MINIMUM_CONSENSUS_POWER x StateMinerInitialPledgeCollateral
5648+
/// See FIP-0077, https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0077.md
5649+
pub fn calculate_create_miner_deposit(
5650+
rt: &impl Runtime,
5651+
network_qap: FilterEstimate,
5652+
sector_size: SectorSize,
5653+
) -> Result<TokenAmount, ActorError> {
5654+
// set network pledge inputs
5655+
let rew = request_current_epoch_block_reward(rt)?;
5656+
let pwr = request_current_total_power(rt)?;
5657+
let circulating_supply = rt.total_fil_circ_supply();
5658+
let pledge_inputs = NetworkPledgeInputs {
5659+
network_qap,
5660+
network_baseline: rew.this_epoch_baseline_power,
5661+
circulating_supply,
5662+
epoch_reward: rew.this_epoch_reward_smoothed,
5663+
epochs_since_ramp_start: rt.curr_epoch() - pwr.ramp_start_epoch,
5664+
ramp_duration_epochs: pwr.ramp_duration_epochs,
5665+
};
5666+
5667+
let sector_number = MINIMUM_CONSENSUS_POWER / sector_size as i64;
5668+
let power = qa_power_for_weight(sector_size, MIN_SECTOR_EXPIRATION, &BigInt::zero());
5669+
let sector_initial_pledge = initial_pledge_for_power(
5670+
&power,
5671+
&pledge_inputs.network_baseline,
5672+
&pledge_inputs.epoch_reward,
5673+
&pledge_inputs.network_qap,
5674+
&pledge_inputs.circulating_supply,
5675+
pledge_inputs.epochs_since_ramp_start,
5676+
pledge_inputs.ramp_duration_epochs,
5677+
);
5678+
Ok(sector_initial_pledge * sector_number)
5679+
}
5680+
56305681
pub struct SectorPiecesActivationInput {
56315682
pub piece_manifests: Vec<PieceActivationManifest>,
56325683
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/src/types.rs

Lines changed: 1 addition & 0 deletions
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)]

actors/miner/tests/miner_actor_test_construction.rs

Lines changed: 137 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
use fil_actors_runtime::INIT_ACTOR_ADDR;
2-
use fil_actors_runtime::test_utils::*;
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(798245441765376000u64));
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.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_depost_vesting_funds = state.vesting_funds.load(&env.rt.store).unwrap();
160+
assert!(create_depost_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,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)