Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Reward actor to new spec #480

Merged
merged 7 commits into from
Jun 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions types/src/sector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ pub type SectorNumber = u64;
/// Unit of storage power (measured in bytes)
pub type StoragePower = BigUint;

/// The unit of spacetime committed to the network
pub type Spacetime = BigUint;

pub type SectorQuality = BigUint;

/// SectorSize indicates one of a set of possible sizes in the network.
Expand Down
18 changes: 17 additions & 1 deletion vm/actor/src/builtin/miner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,23 @@ use vm::{ActorError, ExitCode, MethodNum, Serialized, METHOD_CONSTRUCTOR};
#[repr(u64)]
pub enum Method {
Constructor = METHOD_CONSTRUCTOR,
// TODO include other methods on impl
ControlAddresses = 2,
ChangeWorkerAddress = 3,
ChangePeerID = 4,
SubmitWindowedPoSt = 5,
PreCommitSector = 6,
ProveCommitSector = 7,
ExtendSectorExpiration = 8,
TerminateSectors = 9,
DeclareFaults = 10,
DeclareFaultsRecovered = 11,
OnDeferredCronEvent = 12,
CheckSectorProven = 13,
AddLockedFund = 14,
ReportConsensusFault = 15,
WithdrawBalance = 16,
ConfirmSectorProofsValid = 17,
ChangeMultiaddrs = 18,
}

/// Miner Actor
Expand Down
1 change: 1 addition & 0 deletions vm/actor/src/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod init;
pub mod market;
pub mod miner;
pub mod multisig;
pub mod network;
pub mod paych;
pub mod power;
pub mod reward;
Expand Down
13 changes: 13 additions & 0 deletions vm/actor/src/builtin/network.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

pub const EPOCH_DURATION_SECONDS: u64 = 25;
pub const SECONDS_IN_HOUR: u64 = 3600;
pub const SECONDS_IN_DAY: u64 = 86400;
pub const SECONDS_IN_YEAR: u64 = 31556925;
pub const EPOCHS_IN_HOUR: u64 = SECONDS_IN_HOUR / EPOCH_DURATION_SECONDS;
pub const EPOCHS_IN_DAY: u64 = SECONDS_IN_DAY / EPOCH_DURATION_SECONDS;
pub const EPOCHS_IN_YEAR: u64 = SECONDS_IN_YEAR / EPOCH_DURATION_SECONDS;

/// The expected number of block producers in each epoch.
pub const EXPECTED_LEADERS_PER_EPOCH: u64 = 5;
ec2 marked this conversation as resolved.
Show resolved Hide resolved
198 changes: 124 additions & 74 deletions vm/actor/src/builtin/reward/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,32 @@ mod types;

pub use self::state::{Reward, State, VestingFunction};
pub use self::types::*;
use crate::{check_empty_params, Multimap, BURNT_FUNDS_ACTOR_ADDR, SYSTEM_ACTOR_ADDR};
use crate::network::EXPECTED_LEADERS_PER_EPOCH;
use crate::{
check_empty_params, miner, BURNT_FUNDS_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR,
};
use clock::ChainEpoch;
use fil_types::StoragePower;
use ipld_blockstore::BlockStore;
use num_bigint::biguint_ser::BigUintSer;
use num_bigint::biguint_ser::{BigUintDe, BigUintSer};
use num_bigint::BigUint;
use num_derive::FromPrimitive;
use num_traits::{FromPrimitive, Zero};
use num_traits::{CheckedSub, FromPrimitive};
use runtime::{ActorCode, Runtime};
use vm::{
ActorError, ExitCode, MethodNum, Serialized, TokenAmount, METHOD_CONSTRUCTOR, METHOD_SEND,
};

// * Updated to specs-actors commit: 52599b21919df07f44d7e61cc028e265ec18f700

/// Reward actor methods available
#[derive(FromPrimitive)]
#[repr(u64)]
pub enum Method {
Constructor = METHOD_CONSTRUCTOR,
AwardBlockReward = 2,
LastPerEpochReward = 3,
// TODO add UpdateNetworkKPI
UpdateNetworkKPI = 4,
}

/// Reward Actor
Expand All @@ -37,18 +45,22 @@ impl Actor {
{
rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?;

let empty_root = Multimap::new(rt.store()).root().map_err(|e| {
ActorError::new(
ExitCode::ErrIllegalState,
format!("failed to construct state: {}", e),
)
})?;
// TODO revisit based on issue: https://github.com/filecoin-project/specs-actors/issues/317

rt.create(&State::new(empty_root))?;
rt.create(&State::new())?;
Ok(())
}

/// Mints a reward and puts into state reward map
/// Awards a reward to a block producer.
/// This method is called only by the system actor, implicitly, as the last message in the evaluation of a block.
/// The system actor thus computes the parameters and attached value.
///
/// The reward includes two components:
/// - the epoch block reward, computed and paid from the reward actor's balance,
/// - the block gas reward, expected to be transferred to the reward actor with this invocation.
///
/// The reward is reduced before the residual is credited to the block producer, by:
/// - a penalty amount, provided as a parameter, which is burnt,
fn award_block_reward<BS, RT>(
rt: &mut RT,
params: AwardBlockRewardParams,
Expand All @@ -59,66 +71,46 @@ impl Actor {
{
rt.validate_immediate_caller_is(std::iter::once(&*SYSTEM_ACTOR_ADDR))?;
let balance = rt.current_balance()?;
if balance < params.gas_reward {
return Err(ActorError::new(
ExitCode::ErrInsufficientFunds,
format!(
"actor current balance {} insufficient to pay gas reward {}",
balance, params.gas_reward
),
));
}
assert!(
balance >= params.gas_reward,
"actor current balance {} insufficient to pay gas reward {}",
balance,
params.gas_reward
);

if params.ticket_count == 0 {
return Err(ActorError::new(
ExitCode::ErrIllegalArgument,
"cannot give block reward for zero tickets".to_owned(),
));
}
assert!(
params.ticket_count > 0,
"cannot give block reward for zero tickets"
);

let miner_addr = rt
.resolve_address(&params.miner)
// TODO revisit later if all address resolutions end up being the same exit code
.map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e.msg().to_string()))?;

let prior_balance = rt.current_balance()?;

let miner = rt.resolve_address(&params.miner)?;

let prior_bal = rt.current_balance()?;

let cur_epoch = rt.curr_epoch();
let penalty: TokenAmount = rt
.transaction::<_, Result<_, String>, _>(|st: &mut State, rt| {
let block_rew = Self::compute_block_reward(
st,
&prior_bal - &params.gas_reward,
params.ticket_count,
);
let total_reward = block_rew + &params.gas_reward;

// Cap the penalty at the total reward value.
let penalty = std::cmp::min(params.penalty, total_reward.clone());
// Reduce the payable reward by the penalty.
let rew_payable = total_reward - &penalty;
if (&rew_payable + &penalty) > prior_bal {
return Err(format!(
"reward payable {} + penalty {} exceeds balance {}",
rew_payable, penalty, prior_bal
));
}

// Record new reward into reward map.
if rew_payable > TokenAmount::zero() {
st.add_reward(
rt.store(),
&miner,
Reward {
start_epoch: cur_epoch,
end_epoch: cur_epoch + REWARD_VESTING_PERIOD,
value: rew_payable,
amount_withdrawn: TokenAmount::zero(),
vesting_function: REWARD_VESTING_FUNCTION,
},
)?;
}
//
Ok(penalty)
})?
.map_err(|e| ActorError::new(ExitCode::ErrIllegalState, e))?;
let state: State = rt.state()?;
let block_reward = state.last_per_epoch_reward / EXPECTED_LEADERS_PER_EPOCH;
let total_reward = block_reward + params.gas_reward;

// Cap the penalty at the total reward value.
let penalty = std::cmp::min(&params.penalty, &total_reward);

// Reduce the payable reward by the penalty.
let reward_payable = total_reward.clone() - penalty;

assert!(
reward_payable <= prior_balance - penalty,
"Total reward exceeds balance of actor"
);

rt.send(
&miner_addr,
miner::Method::AddLockedFund as u64,
&Serialized::serialize(&BigUintSer(&reward_payable)).unwrap(),
&reward_payable,
)?;

// Burn the penalty
rt.send(
Expand All @@ -142,11 +134,64 @@ impl Actor {
}

/// Withdraw available funds from reward map
fn compute_block_reward(st: &State, balance: TokenAmount, ticket_count: u64) -> TokenAmount {
let treasury = balance - &st.reward_total;
let target_rew = BLOCK_REWARD_TARGET.clone() * ticket_count;
fn compute_per_epoch_reward(st: &mut State, _ticket_count: u64) -> TokenAmount {
// TODO update when finished in specs
let new_simple_supply = minting_function(
&SIMPLE_TOTAL,
&(BigUint::from(st.reward_epochs_paid) << MINTING_INPUT_FIXED_POINT),
);
let new_baseline_supply = minting_function(&*BASELINE_TOTAL, &st.effective_network_time);

let new_simple_minted = new_simple_supply
.checked_sub(&st.simple_supply)
.unwrap_or_default();
let new_baseline_minted = new_baseline_supply
.checked_sub(&st.baseline_supply)
.unwrap_or_default();

st.simple_supply = new_simple_supply;
st.baseline_supply = new_baseline_supply;

let per_epoch_reward = new_simple_minted + new_baseline_minted;
st.last_per_epoch_reward = per_epoch_reward.clone();
per_epoch_reward
}

std::cmp::min(target_rew, treasury)
fn new_baseline_power(_st: &State, _reward_epochs_paid: ChainEpoch) -> StoragePower {
// TODO: this is not the final baseline function or value, PARAM_FINISH
BigUint::from(BASELINE_POWER)
}

// Called at the end of each epoch by the power actor (in turn by its cron hook).
// This is only invoked for non-empty tipsets. The impact of this is that block rewards are paid out over
// a schedule defined by non-empty tipsets, not by elapsed time/epochs.
// This is not necessarily what we want, and may change.
fn update_network_kpi<BS, RT>(
rt: &mut RT,
curr_realized_power: StoragePower,
) -> Result<(), ActorError>
where
BS: BlockStore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_is(std::iter::once(&*STORAGE_POWER_ACTOR_ADDR))?;

rt.transaction(|st: &mut State, _| {
// By the time this is called, the rewards for this epoch have been paid to miners.
st.reward_epochs_paid += 1;
st.realized_power = curr_realized_power;

st.baseline_power = Self::new_baseline_power(st, st.reward_epochs_paid);
st.cumsum_baseline += &st.baseline_power;

// Cap realized power in computing CumsumRealized so that progress is only relative to the current epoch.
let capped_realized_power = std::cmp::min(&st.baseline_power, &st.realized_power);
st.cumsum_realized += capped_realized_power;
st.effective_network_time =
st.get_effective_network_time(&st.cumsum_baseline, &st.cumsum_realized);
Self::compute_per_epoch_reward(st, 1);
})?;
Ok(())
}
}

Expand Down Expand Up @@ -175,6 +220,11 @@ impl ActorCode for Actor {
let res = Self::last_per_epoch_reward(rt)?;
Ok(Serialized::serialize(BigUintSer(&res))?)
}
Some(Method::UpdateNetworkKPI) => {
let BigUintDe(param) = params.deserialize()?;
Self::update_network_kpi(rt, param)?;
Ok(Serialized::default())
}
_ => Err(rt.abort(ExitCode::SysErrInvalidMethod, "Invalid method")),
}
}
Expand Down
Loading