1use fil_actors_runtime::runtime::{ActorCode, Runtime};
5use fil_actors_runtime::{
6 actor_error, cbor, ActorError, BURNT_FUNDS_ACTOR_ADDR, EXPECTED_LEADERS_PER_EPOCH,
7 STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR,
8};
9use fvm_ipld_blockstore::Blockstore;
10use fvm_ipld_encoding::RawBytes;
11use fvm_shared::address::Address;
12use fvm_shared::bigint::bigint_ser::BigIntDe;
13use fvm_shared::econ::TokenAmount;
14use fvm_shared::sector::StoragePower;
15use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND};
16use log::{error, warn};
17use num_derive::FromPrimitive;
18use num_traits::FromPrimitive;
19
20pub use self::logic::*;
21pub use self::state::{Reward, State, VestingFunction};
22pub use self::types::*;
23
24#[cfg(feature = "fil-actor")]
25fil_actors_runtime::wasm_trampoline!(Actor);
26
27pub(crate) mod expneg;
28mod logic;
29mod state;
30pub mod testing;
31mod types;
32
33#[doc(hidden)]
35pub mod ext;
36
37pub const PENALTY_MULTIPLIER: u64 = 3;
41
42#[derive(FromPrimitive)]
44#[repr(u64)]
45pub enum Method {
46 Constructor = METHOD_CONSTRUCTOR,
47 AwardBlockReward = 2,
48 ThisEpochReward = 3,
49 UpdateNetworkKPI = 4,
50}
51
52pub struct Actor;
54impl Actor {
55 fn constructor<BS, RT>(
57 rt: &mut RT,
58 curr_realized_power: Option<StoragePower>,
59 ) -> Result<(), ActorError>
60 where
61 BS: Blockstore,
62 RT: Runtime<BS>,
63 {
64 rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?;
65
66 if let Some(power) = curr_realized_power {
67 rt.create(&State::new(power))?;
68 Ok(())
69 } else {
70 Err(actor_error!(illegal_argument, "argument should not be nil"))
71 }
72 }
73
74 fn award_block_reward<BS, RT>(
85 rt: &mut RT,
86 params: AwardBlockRewardParams,
87 ) -> Result<(), ActorError>
88 where
89 BS: Blockstore,
90 RT: Runtime<BS>,
91 {
92 rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?;
93 let prior_balance = rt.current_balance();
94 if params.penalty.is_negative() {
95 return Err(actor_error!(illegal_argument, "negative penalty {}", params.penalty));
96 }
97 if params.gas_reward.is_negative() {
98 return Err(actor_error!(
99 illegal_argument,
100 "negative gas reward {}",
101 params.gas_reward
102 ));
103 }
104 if prior_balance < params.gas_reward {
105 return Err(actor_error!(
106 illegal_state,
107 "actor current balance {} insufficient to pay gas reward {}",
108 prior_balance,
109 params.gas_reward
110 ));
111 }
112 if params.win_count <= 0 {
113 return Err(actor_error!(illegal_argument, "invalid win count {}", params.win_count));
114 }
115
116 let miner_id = rt
117 .resolve_address(¶ms.miner)
118 .ok_or_else(|| actor_error!(not_found, "failed to resolve given owner address"))?;
119
120 let penalty: TokenAmount = ¶ms.penalty * PENALTY_MULTIPLIER;
121
122 let total_reward = rt.transaction(|st: &mut State, rt| {
123 let mut block_reward: TokenAmount =
124 (&st.this_epoch_reward * params.win_count).div_floor(EXPECTED_LEADERS_PER_EPOCH);
125 let mut total_reward = ¶ms.gas_reward + &block_reward;
126 let curr_balance = rt.current_balance();
127 if total_reward > curr_balance {
128 warn!(
129 "reward actor balance {} below totalReward expected {},\
130 paying out rest of balance",
131 curr_balance, total_reward
132 );
133 total_reward = curr_balance;
134 block_reward = &total_reward - ¶ms.gas_reward;
135 if block_reward.is_negative() {
136 return Err(actor_error!(
137 illegal_state,
138 "programming error, block reward {} below zero",
139 block_reward
140 ));
141 }
142 }
143 st.total_storage_power_reward += block_reward;
144 Ok(total_reward)
145 })?;
146
147 if total_reward > prior_balance {
150 return Err(actor_error!(
151 illegal_state,
152 "reward {} exceeds balance {}",
153 total_reward,
154 prior_balance
155 ));
156 }
157
158 let reward_params = ext::miner::ApplyRewardParams { reward: total_reward.clone(), penalty };
160 let res = rt.send(
161 &Address::new_id(miner_id),
162 ext::miner::APPLY_REWARDS_METHOD,
163 RawBytes::serialize(&reward_params)?,
164 total_reward.clone(),
165 );
166 if let Err(e) = res {
167 error!(
168 "failed to send ApplyRewards call to the miner actor with funds {}, code: {:?}",
169 total_reward,
170 e.exit_code()
171 );
172 let res =
173 rt.send(&BURNT_FUNDS_ACTOR_ADDR, METHOD_SEND, RawBytes::default(), total_reward);
174 if let Err(e) = res {
175 error!(
176 "failed to send unsent reward to the burnt funds actor, code: {:?}",
177 e.exit_code()
178 );
179 }
180 }
181
182 Ok(())
183 }
184
185 fn this_epoch_reward<BS, RT>(rt: &mut RT) -> Result<ThisEpochRewardReturn, ActorError>
189 where
190 BS: Blockstore,
191 RT: Runtime<BS>,
192 {
193 rt.validate_immediate_caller_accept_any()?;
194 let st: State = rt.state()?;
195 Ok(ThisEpochRewardReturn {
196 this_epoch_baseline_power: st.this_epoch_baseline_power,
197 this_epoch_reward_smoothed: st.this_epoch_reward_smoothed,
198 })
199 }
200
201 fn update_network_kpi<BS, RT>(
205 rt: &mut RT,
206 curr_realized_power: Option<StoragePower>,
207 ) -> Result<(), ActorError>
208 where
209 BS: Blockstore,
210 RT: Runtime<BS>,
211 {
212 rt.validate_immediate_caller_is(std::iter::once(&STORAGE_POWER_ACTOR_ADDR))?;
213 let curr_realized_power = curr_realized_power
214 .ok_or_else(|| actor_error!(illegal_argument, "argument cannot be None"))?;
215
216 rt.transaction(|st: &mut State, rt| {
217 let prev = st.epoch;
218 while st.epoch < rt.curr_epoch() {
221 st.update_to_next_epoch(&curr_realized_power);
223 }
224
225 st.update_to_next_epoch_with_reward(&curr_realized_power);
226 st.update_smoothed_estimates(st.epoch - prev);
227 Ok(())
228 })?;
229 Ok(())
230 }
231}
232
233impl ActorCode for Actor {
234 fn invoke_method<BS, RT>(
235 rt: &mut RT,
236 method: MethodNum,
237 params: &RawBytes,
238 ) -> Result<RawBytes, ActorError>
239 where
240 BS: Blockstore,
241 RT: Runtime<BS>,
242 {
243 match FromPrimitive::from_u64(method) {
244 Some(Method::Constructor) => {
245 let param: Option<BigIntDe> = cbor::deserialize_params(params)?;
246 Self::constructor(rt, param.map(|v| v.0))?;
247 Ok(RawBytes::default())
248 }
249 Some(Method::AwardBlockReward) => {
250 Self::award_block_reward(rt, cbor::deserialize_params(params)?)?;
251 Ok(RawBytes::default())
252 }
253 Some(Method::ThisEpochReward) => {
254 let res = Self::this_epoch_reward(rt)?;
255 Ok(RawBytes::serialize(&res)?)
256 }
257 Some(Method::UpdateNetworkKPI) => {
258 let param: Option<BigIntDe> = cbor::deserialize_params(params)?;
259 Self::update_network_kpi(rt, param.map(|v| v.0))?;
260 Ok(RawBytes::default())
261 }
262 None => Err(actor_error!(unhandled_message, "Invalid method")),
263 }
264 }
265}