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

Reward Actor #318

Merged
merged 9 commits into from
Apr 7, 2020
14 changes: 7 additions & 7 deletions vm/actor/src/builtin/power/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use super::{SectorStorageWeightDesc, SectorTermination};
use crate::{reward, StoragePower};
use clock::ChainEpoch;
use num_bigint::BigInt;
use num_bigint::{BigInt, BigUint};
use num_traits::{FromPrimitive, Pow};
use runtime::ConsensusFaultType;
use vm::TokenAmount;
Expand All @@ -23,10 +23,10 @@ pub const CONSENSUS_FAULT_REPORTING_WINDOW: ChainEpoch = 2880; // 1 day @ 30 sec

lazy_static! {
/// Multiplier on sector pledge requirement.
pub static ref PLEDGE_FACTOR: BigInt = BigInt::from(3); // PARAM_FINISH
pub static ref PLEDGE_FACTOR: BigUint = BigUint::from(3u8); // PARAM_FINISH

/// Total expected block reward per epoch (per-winner reward * expected winners), as input to pledge requirement.
pub static ref EPOCH_TOTAL_EXPECTED_REWARD: BigInt = reward::BLOCK_REWARD_TARGET.clone() * 5; // PARAM_FINISH
pub static ref EPOCH_TOTAL_EXPECTED_REWARD: BigUint = reward::BLOCK_REWARD_TARGET.clone() * 5u8; // PARAM_FINISH

/// Minimum power of an individual miner to meet the threshold for leader election.
pub static ref CONSENSUS_MINER_MIN_POWER: StoragePower = StoragePower::from(2 << 30); // placeholder
Expand Down Expand Up @@ -101,9 +101,9 @@ pub fn pledge_for_weight(
* weight.duration
* &*EPOCH_TOTAL_EXPECTED_REWARD
* &*PLEDGE_FACTOR;
let denominator = network_power;

(numerator / denominator)
let denominator = network_power
.to_biguint()
.expect("all values should be positive")
.expect("Storage power should be positive");

numerator / denominator
}
2 changes: 1 addition & 1 deletion vm/actor/src/builtin/power/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ impl State {
epoch: ChainEpoch,
) -> Result<(), String> {
let mut mmap = Multimap::from_root(s, &self.cron_event_queue)?;
mmap.remove_all(epoch_key(epoch))?;
mmap.remove_all(&epoch_key(epoch))?;
self.cron_event_queue = mmap.root()?;
Ok(())
}
Expand Down
151 changes: 135 additions & 16 deletions vm/actor/src/builtin/reward/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@
mod state;
mod types;

pub use self::state::{Reward, State};
pub use self::state::{Reward, State, VestingFunction};
pub use self::types::*;
use crate::check_empty_params;
use crate::{
check_empty_params, request_miner_control_addrs, Multimap, BURNT_FUNDS_ACTOR_ADDR,
SYSTEM_ACTOR_ADDR,
};
use address::Address;
use ipld_blockstore::BlockStore;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use num_traits::{FromPrimitive, Zero};
use runtime::{ActorCode, Runtime};
use vm::{ActorError, ExitCode, MethodNum, Serialized, METHOD_CONSTRUCTOR};
use vm::{
ActorError, ExitCode, MethodNum, Serialized, TokenAmount, METHOD_CONSTRUCTOR, METHOD_SEND,
};

/// Reward actor methods available
#[derive(FromPrimitive)]
Expand All @@ -34,31 +39,146 @@ impl Method {
pub struct Actor;
impl Actor {
/// Constructor for Reward actor
fn constructor<BS, RT>(_rt: &RT) -> Result<(), ActorError>
fn constructor<BS, RT>(rt: &mut RT) -> Result<(), ActorError>
where
BS: BlockStore,
RT: Runtime<BS>,
{
// TODO
todo!();
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),
)
})?;

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

/// Mints a reward and puts into state reward map
fn award_block_reward<BS, RT>(_rt: &RT) -> Result<(), ActorError>
fn award_block_reward<BS, RT>(
rt: &mut RT,
params: AwardBlockRewardParams,
) -> Result<(), ActorError>
where
BS: BlockStore,
RT: Runtime<BS>,
{
// TODO add params type and implement
todo!();
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
),
));
}

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

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, bs| {
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(
bs,
&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))?;

// Burn the penalty
rt.send(
&*BURNT_FUNDS_ACTOR_ADDR,
METHOD_SEND,
&Serialized::default(),
&penalty,
)?;

Ok(())
}

/// Withdraw available funds from reward map
fn withdraw_reward<BS, RT>(_rt: &RT, _miner_in: &Address) -> Result<(), ActorError>
fn withdraw_reward<BS, RT>(rt: &mut RT, miner_in: Address) -> Result<(), ActorError>
where
BS: BlockStore,
RT: Runtime<BS>,
{
// TODO
todo!();
let maddr = rt.resolve_address(&miner_in)?;

let (owner, worker) = request_miner_control_addrs(rt, &maddr)?;

rt.validate_immediate_caller_is([owner.clone(), worker].iter())?;

let cur_epoch = rt.curr_epoch();
let withdrawable_reward =
rt.transaction::<_, Result<_, ActorError>, _>(|st: &mut State, bs| {
let withdrawn = st.withdraw_reward(bs, &maddr, cur_epoch).map_err(|e| {
ActorError::new(
ExitCode::ErrIllegalState,
format!("failed to withdraw record: {}", e),
)
})?;
Ok(withdrawn)
})??;

rt.send(
&owner,
METHOD_SEND,
&Serialized::default(),
&withdrawable_reward,
)?;
Ok(())
}

/// 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;

std::cmp::min(target_rew, treasury)
}
}

Expand All @@ -80,12 +200,11 @@ impl ActorCode for Actor {
Ok(Serialized::default())
}
Some(Method::AwardBlockReward) => {
check_empty_params(params)?;
Self::award_block_reward(rt)?;
Self::award_block_reward(rt, params.deserialize()?)?;
Ok(Serialized::default())
}
Some(Method::WithdrawReward) => {
Self::withdraw_reward(rt, &params.deserialize()?)?;
Self::withdraw_reward(rt, params.deserialize()?)?;
Ok(Serialized::default())
}
_ => Err(rt.abort(ExitCode::SysErrInvalidMethod, "Invalid method")),
Expand Down
Loading