Skip to content

Commit dee8b5d

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

File tree

18 files changed

+288
-68
lines changed

18 files changed

+288
-68
lines changed

actors/miner/src/lib.rs

Lines changed: 58 additions & 3 deletions
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,
@@ -180,6 +180,14 @@ impl Actor {
180180
check_peer_info(rt.policy(), &params.peer_id, &params.multi_addresses)?;
181181
check_valid_post_proof_type(rt.policy(), params.window_post_proof_type)?;
182182

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

241-
let st = State::new(policy, rt.store(), info_cid, period_start, deadline_idx)?;
249+
let store = rt.store();
250+
let mut st = State::new(policy, store, info_cid, period_start, deadline_idx)?;
251+
st.add_locked_funds(store, rt.curr_epoch(), &deposit, &REWARD_VESTING_SPEC)
252+
.map_err(|e| actor_error!(illegal_state, e))?;
242253
rt.create(&st)?;
243254
Ok(())
244255
}
@@ -311,6 +322,7 @@ impl Actor {
311322
.load_vesting_funds(rt.store())
312323
.map_err(|e| actor_error!(illegal_state, "failed to load vesting funds: {}", e))?;
313324
let ret = vesting_funds.funds.into_iter().map(|v| (v.epoch, v.amount)).collect_vec();
325+
314326
Ok(GetVestingFundsReturn { vesting_funds: ret })
315327
}
316328

@@ -755,6 +767,7 @@ impl Actor {
755767

756768
Ok(())
757769
}
770+
758771
/// Checks state of the corresponding sector pre-commitments and verifies aggregate proof of replication
759772
/// of these sectors. If valid, the sectors' deals are activated, sectors are assigned a deadline and charged pledge
760773
/// and precommit state is removed.
@@ -4951,7 +4964,6 @@ fn resolve_worker_address(rt: &impl Runtime, raw: Address) -> Result<ActorID, Ac
49514964
}
49524965

49534966
fn burn_funds(rt: &impl Runtime, amount: TokenAmount) -> Result<(), ActorError> {
4954-
log::debug!("storage provder {} burning {}", rt.message().receiver(), amount);
49554967
if amount.is_positive() {
49564968
extract_send_result(rt.send_simple(&BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, None, amount))?;
49574969
}
@@ -5356,6 +5368,49 @@ fn activate_new_sector_infos(
53565368
Ok(())
53575369
}
53585370

5371+
/// Calculate create miner deposit by MINIMUM_CONSENSUS_POWER x StateMinerInitialPledgeCollateral
5372+
/// See FIP-0077, https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0077.md
5373+
pub fn calculate_create_miner_deposit(
5374+
rt: &impl Runtime,
5375+
network_qap: FilterEstimate,
5376+
) -> Result<TokenAmount, ActorError> {
5377+
// set network pledge inputs
5378+
let rew = request_current_epoch_block_reward(rt)?;
5379+
let circulating_supply = rt.total_fil_circ_supply();
5380+
let pledge_inputs = NetworkPledgeInputs {
5381+
network_qap,
5382+
network_baseline: rew.this_epoch_baseline_power,
5383+
circulating_supply,
5384+
epoch_reward: rew.this_epoch_reward_smoothed,
5385+
};
5386+
5387+
/// set sector size with min power
5388+
#[cfg(feature = "min-power-2k")]
5389+
let sector_size = SectorSize::_2KiB;
5390+
#[cfg(feature = "min-power-2g")]
5391+
let sector_size = SectorSize::_8MiB;
5392+
#[cfg(feature = "min-power-32g")]
5393+
let sector_size = SectorSize::_512MiB;
5394+
#[cfg(not(any(
5395+
feature = "min-power-2k",
5396+
feature = "min-power-2g",
5397+
feature = "min-power-32g"
5398+
)))]
5399+
let sector_size = SectorSize::_32GiB;
5400+
5401+
let sector_number = MINIMUM_CONSENSUS_POWER / sector_size as i64;
5402+
let power =
5403+
qa_power_for_weight(sector_size, MIN_SECTOR_EXPIRATION, &BigInt::zero(), &BigInt::zero());
5404+
let sector_initial_pledge = initial_pledge_for_power(
5405+
&power,
5406+
&pledge_inputs.network_baseline,
5407+
&pledge_inputs.epoch_reward,
5408+
&pledge_inputs.network_qap,
5409+
&pledge_inputs.circulating_supply,
5410+
);
5411+
Ok(sector_initial_pledge * sector_number)
5412+
}
5413+
53595414
pub struct SectorPiecesActivationInput {
53605415
pub piece_manifests: Vec<PieceActivationManifest>,
53615416
pub sector_expiry: ChainEpoch,

actors/miner/src/state.rs

Lines changed: 5 additions & 0 deletions
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(
@@ -828,6 +829,7 @@ impl State {
828829
amount_unlocked
829830
));
830831
}
832+
831833
// add locked funds now
832834
vesting_funds.add_locked_funds(current_epoch, vesting_sum, self.proving_period_start, spec);
833835
self.locked_funds += vesting_sum;
@@ -888,6 +890,7 @@ impl State {
888890

889891
Ok(std::mem::take(&mut self.fee_debt))
890892
}
893+
891894
/// Unlocks an amount of funds that have *not yet vested*, if possible.
892895
/// The soonest-vesting entries are unlocked first.
893896
/// Returns the amount actually unlocked.
@@ -959,6 +962,7 @@ impl State {
959962
pub fn get_unlocked_balance(&self, actor_balance: &TokenAmount) -> anyhow::Result<TokenAmount> {
960963
let unlocked_balance =
961964
actor_balance - &self.locked_funds - &self.pre_commit_deposits - &self.initial_pledge;
965+
962966
if unlocked_balance.is_negative() {
963967
return Err(anyhow!("negative unlocked balance {}", unlocked_balance));
964968
}
@@ -990,6 +994,7 @@ impl State {
990994
}
991995

992996
let min_balance = &self.pre_commit_deposits + &self.locked_funds + &self.initial_pledge;
997+
993998
if balance < &min_balance {
994999
return Err(anyhow!("fee debt is negative: {}", self.fee_debt));
9951000
}

actors/miner/src/testing.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,7 @@ fn check_miner_balances<BS: Blockstore>(
268268
!state.fee_debt.is_negative(),
269269
format!("miner fee debt is less than zero: {}", state.fee_debt),
270270
);
271-
272-
acc.require(!(balance - &state.locked_funds - &state.pre_commit_deposits - &state.initial_pledge).is_negative(), format!("miner balance {balance} is less than sum of locked funds ({}), precommit deposit ({}) and initial pledge ({})", state.locked_funds, state.pre_commit_deposits, state.initial_pledge));
271+
acc.require(!(balance - &state.locked_funds - &state.pre_commit_deposits - &state.initial_pledge).is_negative(), format!("miner balance {balance} is less than sum of locked funds ({}), precommit deposit ({}), initial pledge ({})", state.locked_funds, state.pre_commit_deposits, state.initial_pledge));
273272

274273
// locked funds must be sum of vesting table and vesting table payments must be quantized
275274
let mut vesting_sum = TokenAmount::zero();

actors/miner/src/types.rs

Lines changed: 6 additions & 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)]
@@ -105,6 +106,11 @@ pub struct DeferredCronEventParams {
105106
pub quality_adj_power_smoothed: FilterEstimate,
106107
}
107108

109+
#[derive(Serialize_tuple, Deserialize_tuple)]
110+
pub struct LockCreateMinerDepositParams {
111+
pub amount: TokenAmount,
112+
}
113+
108114
#[derive(Serialize_tuple, Deserialize_tuple)]
109115
pub struct PoStPartition {
110116
/// Partitions are numbered per-deadline, from zero.

actors/miner/tests/apply_rewards.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,6 @@ fn funds_vest() {
4343
let rt = h.new_runtime();
4444
rt.set_balance(BIG_BALANCE.clone());
4545
h.construct_and_verify(&rt);
46-
let st = h.get_state(&rt);
47-
48-
let vesting_funds = st.load_vesting_funds(&rt.store).unwrap();
49-
50-
// Nothing vesting to start
51-
assert!(vesting_funds.funds.is_empty());
52-
assert!(st.locked_funds.is_zero());
5346

5447
// Lock some funds with AddLockedFund
5548
let amt = TokenAmount::from_atto(600_000);
@@ -115,14 +108,13 @@ fn penalty_is_partially_burnt_and_stored_as_fee_debt() {
115108
let st = h.get_state(&rt);
116109
assert!(st.fee_debt.is_zero());
117110

118-
let amt = rt.get_balance();
111+
let amt = BIG_BALANCE.clone();
119112
let penalty = &amt * 3;
120113
let reward = amt.clone();
121114

122115
// manually update actor balance to include the added funds on reward message
123116
let new_balance = &reward + &amt;
124117
rt.set_balance(new_balance);
125-
126118
rt.set_caller(*REWARD_ACTOR_CODE_ID, REWARD_ACTOR_ADDR);
127119
rt.expect_validate_caller_addr(vec![REWARD_ACTOR_ADDR]);
128120

@@ -163,8 +155,6 @@ fn rewards_pay_back_fee_debt() {
163155
h.construct_and_verify(&rt);
164156
let mut st = h.get_state(&rt);
165157

166-
assert!(st.locked_funds.is_zero());
167-
168158
let amt = rt.get_balance();
169159
let available_before = h.get_available_balance(&rt).unwrap();
170160
assert!(available_before.is_positive());

actors/miner/tests/deadline_cron.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ fn cron_on_inactive_state() {
3030
h.construct_and_verify(&rt);
3131

3232
let st = h.get_state(&rt);
33-
assert_eq!(PERIOD_OFFSET - rt.policy.wpost_proving_period, st.proving_period_start);
3433
assert!(!st.continue_deadline_cron());
3534

35+
assert_eq!(PERIOD_OFFSET - rt.policy.wpost_proving_period, st.proving_period_start);
3636
// cron does nothing and does not enroll another cron
3737
let deadline = h.deadline(&rt);
3838
rt.set_epoch(deadline.last());

actors/miner/tests/miner_actor_test_construction.rs

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1+
use fil_actors_runtime::reward::FilterEstimate;
12
use fil_actors_runtime::test_utils::*;
2-
use fil_actors_runtime::INIT_ACTOR_ADDR;
3+
use fil_actors_runtime::{INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR};
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_reward::{Method as RewardMethod, ThisEpochRewardReturn};
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,19 @@ struct TestEnv {
2730
control_addrs: Vec<Address>,
2831
peer_id: Vec<u8>,
2932
multiaddrs: Vec<BytesDe>,
33+
34+
power: StoragePower,
35+
reward: TokenAmount,
36+
epoch_reward_smooth: FilterEstimate,
37+
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));
45+
3446
let mut env = TestEnv {
3547
receiver: Address::new_id(1000),
3648
owner: Address::new_id(100),
@@ -39,6 +51,9 @@ fn prepare_env() -> TestEnv {
3951
control_addrs: vec![Address::new_id(999), Address::new_id(998)],
4052
peer_id: vec![1, 2, 3],
4153
multiaddrs: vec![BytesDe(vec![1, 2, 3])],
54+
power,
55+
reward,
56+
epoch_reward_smooth,
4257
rt: MockRuntime::default(),
4358
};
4459

@@ -50,6 +65,7 @@ fn prepare_env() -> TestEnv {
5065
env.rt.hash_func = Box::new(hash);
5166
env.rt.caller.replace(INIT_ACTOR_ADDR);
5267
env.rt.caller_type.replace(*INIT_ACTOR_CODE_ID);
68+
env.rt.add_balance(TokenAmount::from_atto(633318697598976000u64));
5369
env
5470
}
5571

@@ -61,16 +77,29 @@ fn constructor_params(env: &TestEnv) -> ConstructorParams {
6177
window_post_proof_type: RegisteredPoStProof::StackedDRGWindow32GiBV1P1,
6278
peer_id: env.peer_id.clone(),
6379
multi_addresses: env.multiaddrs.clone(),
80+
network_qap: env.epoch_reward_smooth.clone(),
6481
}
6582
}
6683

6784
#[test]
6885
fn simple_construction() {
6986
let env = prepare_env();
87+
let current_reward = ThisEpochRewardReturn {
88+
this_epoch_baseline_power: env.power.clone(),
89+
this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(),
90+
};
7091
let params = constructor_params(&env);
7192

7293
env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
7394
env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);
95+
env.rt.expect_send_simple(
96+
REWARD_ACTOR_ADDR,
97+
RewardMethod::ThisEpochReward as u64,
98+
None,
99+
TokenAmount::zero(),
100+
IpldBlock::serialize_cbor(&current_reward).unwrap(),
101+
ExitCode::OK,
102+
);
74103
env.rt.expect_send_simple(
75104
env.worker,
76105
AccountMethod::PubkeyAddress as u64,
@@ -87,7 +116,7 @@ fn simple_construction() {
87116
expect_empty(result);
88117
env.rt.verify();
89118

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

92121
let info = state.get_info(&env.rt.store).unwrap();
93122
assert_eq!(env.owner, info.owner);
@@ -100,10 +129,21 @@ fn simple_construction() {
100129
assert_eq!(2349, info.window_post_partition_sectors);
101130

102131
assert_eq!(TokenAmount::zero(), state.pre_commit_deposits);
103-
assert_eq!(TokenAmount::zero(), state.locked_funds);
132+
assert_eq!(TokenAmount::from_atto(633318697598976000u64), state.locked_funds);
133+
assert_eq!(180, state.load_vesting_funds(&env.rt.store).unwrap().funds.len());
104134
assert_ne!(Cid::default(), state.pre_committed_sectors);
105135
assert_ne!(Cid::default(), state.sectors);
106136

137+
// reset create miner deposit vesting funds
138+
state.save_vesting_funds(&env.rt.store, &fil_actor_miner::VestingFunds::new()).unwrap();
139+
state.locked_funds = TokenAmount::zero();
140+
env.rt.replace_state(&state);
141+
142+
let state = env.rt.get_state::<State>();
143+
let create_depost_vesting_funds = state.load_vesting_funds(&env.rt.store).unwrap();
144+
assert!(create_depost_vesting_funds.funds.is_empty());
145+
assert!(state.locked_funds.is_zero());
146+
107147
// according to original specs-actors test, this is set by running the code; magic...
108148
let proving_period_start = -2222;
109149
assert_eq!(proving_period_start, state.proving_period_start);
@@ -131,6 +171,10 @@ fn simple_construction() {
131171
#[test]
132172
fn control_addresses_are_resolved_during_construction() {
133173
let mut env = prepare_env();
174+
let current_reward = ThisEpochRewardReturn {
175+
this_epoch_baseline_power: env.power.clone(),
176+
this_epoch_reward_smoothed: env.epoch_reward_smooth.clone(),
177+
};
134178

135179
let control1 = new_bls_addr(1);
136180
let control1id = Address::new_id(555);
@@ -146,6 +190,14 @@ fn control_addresses_are_resolved_during_construction() {
146190
let params = constructor_params(&env);
147191
env.rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
148192
env.rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);
193+
env.rt.expect_send_simple(
194+
REWARD_ACTOR_ADDR,
195+
RewardMethod::ThisEpochReward as u64,
196+
None,
197+
TokenAmount::zero(),
198+
IpldBlock::serialize_cbor(&current_reward).unwrap(),
199+
ExitCode::OK,
200+
);
149201
env.rt.expect_send_simple(
150202
env.worker,
151203
AccountMethod::PubkeyAddress as u64,

0 commit comments

Comments
 (0)