ethcore, rpc, machine: refactor block reward application and tracing (#8490)

This commit is contained in:
André Silva 2018-05-07 11:57:03 +01:00 committed by Afri Schoedon
parent e30839e85f
commit 32c32ecfda
10 changed files with 93 additions and 55 deletions

View File

@ -1015,8 +1015,10 @@ impl Engine<EthereumMachine> for AuthorityRound {
let author = *block.header().author();
benefactors.push((author, RewardKind::Author));
let rewards = match self.block_reward_contract {
let rewards: Vec<_> = match self.block_reward_contract {
Some(ref c) if block.header().number() >= self.block_reward_contract_transition => {
// NOTE: this logic should be moved to a function when another
// engine needs support for block reward contract.
let mut call = |to, data| {
let result = self.machine.execute_as_system(
block,
@ -1027,10 +1029,11 @@ impl Engine<EthereumMachine> for AuthorityRound {
result.map_err(|e| format!("{}", e))
};
c.reward(&benefactors, &mut call)?
let rewards = c.reward(&benefactors, &mut call)?;
rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect()
},
_ => {
benefactors.into_iter().map(|(author, _)| (author, self.block_reward)).collect()
benefactors.into_iter().map(|(author, reward_kind)| (author, reward_kind, self.block_reward)).collect()
},
};

View File

@ -14,13 +14,17 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! A module with types for declaring block rewards and a client interface for interacting with a
//! block reward contract.
use ethabi;
use ethabi::ParamType;
use ethereum_types::{H160, Address, U256};
use block::ExecutedBlock;
use error::Error;
use machine::EthereumMachine;
use machine::WithRewards;
use parity_machine::{Machine, WithBalances};
use trace;
use super::SystemCall;
use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward.json");
@ -37,6 +41,8 @@ pub enum RewardKind {
Uncle = 1,
/// Reward attributed to the author(s) of empty step(s) included in the block (AuthorityRound engine).
EmptyStep = 2,
/// Reward attributed by an external protocol (e.g. block reward contract).
External = 3,
}
impl From<RewardKind> for u16 {
@ -45,6 +51,17 @@ impl From<RewardKind> for u16 {
}
}
impl Into<trace::RewardType> for RewardKind {
fn into(self) -> trace::RewardType {
match self {
RewardKind::Author => trace::RewardType::Block,
RewardKind::Uncle => trace::RewardType::Uncle,
RewardKind::EmptyStep => trace::RewardType::EmptyStep,
RewardKind::External => trace::RewardType::External,
}
}
}
/// A client for the block reward contract.
pub struct BlockRewardContract {
/// Address of the contract.
@ -112,14 +129,17 @@ impl BlockRewardContract {
/// Applies the given block rewards, i.e. adds the given balance to each benefactors' address.
/// If tracing is enabled the operations are recorded.
pub fn apply_block_rewards(rewards: &[(Address, U256)], block: &mut ExecutedBlock, machine: &EthereumMachine) -> Result<(), Error> {
use parity_machine::WithBalances;
for &(ref author, ref block_reward) in rewards {
pub fn apply_block_rewards<M: Machine + WithBalances + WithRewards>(
rewards: &[(Address, RewardKind, U256)],
block: &mut M::LiveBlock,
machine: &M,
) -> Result<(), M::Error> {
for &(ref author, _, ref block_reward) in rewards {
machine.add_balance(block, author, block_reward)?;
}
machine.note_rewards(block, &rewards, &[])
let rewards: Vec<_> = rewards.into_iter().map(|&(a, k, r)| (a, k.into(), r)).collect();
machine.note_rewards(block, &rewards)
}
#[cfg(test)]

View File

@ -18,7 +18,6 @@
mod authority_round;
mod basic_authority;
mod block_reward;
mod instant_seal;
mod null_engine;
mod signer;
@ -27,6 +26,7 @@ mod transition;
mod validator_set;
mod vote_collector;
pub mod block_reward;
pub mod epoch;
pub use self::authority_round::AuthorityRound;

View File

@ -16,7 +16,9 @@
use ethereum_types::U256;
use engines::Engine;
use engines::block_reward::{self, RewardKind};
use header::BlockNumber;
use machine::WithRewards;
use parity_machine::{Header, LiveBlock, WithBalances};
/// Params for a null engine.
@ -56,7 +58,7 @@ impl<M: Default> Default for NullEngine<M> {
}
}
impl<M: WithBalances> Engine<M> for NullEngine<M> {
impl<M: WithBalances + WithRewards> Engine<M> for NullEngine<M> {
fn name(&self) -> &str {
"NullEngine"
}
@ -74,26 +76,20 @@ impl<M: WithBalances> Engine<M> for NullEngine<M> {
let n_uncles = LiveBlock::uncles(&*block).len();
let mut rewards = Vec::new();
// Bestow block reward
let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles);
let mut uncle_rewards = Vec::with_capacity(n_uncles);
self.machine.add_balance(block, &author, &result_block_reward)?;
rewards.push((author, RewardKind::Author, result_block_reward));
// bestow uncle rewards.
for u in LiveBlock::uncles(&*block) {
let uncle_author = u.author();
let result_uncle_reward = (reward * U256::from(8 + u.number() - number)).shr(3);
uncle_rewards.push((*uncle_author, result_uncle_reward));
rewards.push((*uncle_author, RewardKind::Uncle, result_uncle_reward));
}
for &(ref a, ref reward) in &uncle_rewards {
self.machine.add_balance(block, a, reward)?;
}
// note and trace.
self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards)
block_reward::apply_block_rewards(&rewards, block, &self.machine)
}
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 }

View File

@ -41,6 +41,7 @@ use ethkey::{self, Message, Signature};
use account_provider::AccountProvider;
use block::*;
use engines::{Engine, Seal, EngineError, ConstructedVerifier};
use engines::block_reward::{self, RewardKind};
use io::IoService;
use super::signer::EngineSigner;
use super::validator_set::{ValidatorSet, SimpleList};
@ -550,10 +551,13 @@ impl Engine<EthereumMachine> for Tendermint {
/// Apply the block reward on finalisation of the block.
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error>{
use parity_machine::WithBalances;
let author = *block.header().author();
self.machine.add_balance(block, &author, &self.block_reward)?;
self.machine.note_rewards(block, &[(author, self.block_reward)], &[])
block_reward::apply_block_rewards(
&[(author, RewardKind::Author, self.block_reward)],
block,
&self.machine,
)
}
fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> {

View File

@ -19,6 +19,7 @@ use std::cmp;
use std::collections::BTreeMap;
use std::sync::Arc;
use hash::{KECCAK_EMPTY_LIST_RLP};
use engines::block_reward::{self, RewardKind};
use ethash::{quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor};
use ethereum_types::{H256, H64, U256, Address};
use unexpected::{OutOfBounds, Mismatch};
@ -233,11 +234,13 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
use std::ops::Shr;
use parity_machine::{LiveBlock, WithBalances};
use parity_machine::LiveBlock;
let author = *LiveBlock::header(&*block).author();
let number = LiveBlock::header(&*block).number();
let mut rewards = Vec::new();
// Applies EIP-649 reward.
let reward = if number >= self.ethash_params.eip649_transition {
self.ethash_params.eip649_reward.unwrap_or(self.ethash_params.block_reward)
@ -253,20 +256,21 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
// Bestow block rewards.
let mut result_block_reward = reward + reward.shr(5) * U256::from(n_uncles);
let mut uncle_rewards = Vec::with_capacity(n_uncles);
if number >= self.ethash_params.mcip3_transition {
result_block_reward = self.ethash_params.mcip3_miner_reward;
let ubi_contract = self.ethash_params.mcip3_ubi_contract;
let ubi_reward = self.ethash_params.mcip3_ubi_reward;
let dev_contract = self.ethash_params.mcip3_dev_contract;
let dev_reward = self.ethash_params.mcip3_dev_reward;
self.machine.add_balance(block, &author, &result_block_reward)?;
self.machine.add_balance(block, &ubi_contract, &ubi_reward)?;
self.machine.add_balance(block, &dev_contract, &dev_reward)?;
rewards.push((author, RewardKind::Author, result_block_reward));
rewards.push((ubi_contract, RewardKind::External, ubi_reward));
rewards.push((dev_contract, RewardKind::External, dev_reward));
} else {
self.machine.add_balance(block, &author, &result_block_reward)?;
rewards.push((author, RewardKind::Author, result_block_reward));
}
// Bestow uncle rewards.
@ -278,15 +282,10 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
reward.shr(5)
};
uncle_rewards.push((*uncle_author, result_uncle_reward));
rewards.push((*uncle_author, RewardKind::Uncle, result_uncle_reward));
}
for &(ref a, ref reward) in &uncle_rewards {
self.machine.add_balance(block, a, reward)?;
}
// Note and trace.
self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards)
block_reward::apply_block_rewards(&rewards, block, &self.machine)
}
fn verify_local_seal(&self, header: &Header) -> Result<(), Error> {

View File

@ -437,22 +437,30 @@ impl ::parity_machine::WithBalances for EthereumMachine {
fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Error> {
live.state_mut().add_balance(address, amount, CleanupMode::NoEmpty).map_err(Into::into)
}
}
/// A state machine that uses block rewards.
pub trait WithRewards: ::parity_machine::Machine {
/// Note block rewards, traces each reward storing information about benefactor, amount and type
/// of reward.
fn note_rewards(
&self,
live: &mut Self::LiveBlock,
direct: &[(Address, U256)],
indirect: &[(Address, U256)],
rewards: &[(Address, RewardType, U256)],
) -> Result<(), Self::Error>;
}
impl WithRewards for EthereumMachine {
fn note_rewards(
&self,
live: &mut Self::LiveBlock,
rewards: &[(Address, RewardType, U256)],
) -> Result<(), Self::Error> {
if let Tracing::Enabled(ref mut traces) = *live.traces_mut() {
let mut tracer = ExecutiveTracer::default();
for &(address, amount) in direct {
tracer.trace_reward(address, amount, RewardType::Block);
}
for &(address, amount) in indirect {
tracer.trace_reward(address, amount, RewardType::Uncle);
for &(address, ref reward_type, amount) in rewards {
tracer.trace_reward(address, amount, reward_type.clone());
}
traces.push(tracer.drain().into());

View File

@ -141,6 +141,10 @@ pub enum RewardType {
Block,
/// Uncle
Uncle,
/// Empty step (AuthorityRound)
EmptyStep,
/// A reward directly attributed by an external protocol (e.g. block reward contract)
External,
}
impl Encodable for RewardType {
@ -148,6 +152,8 @@ impl Encodable for RewardType {
let v = match *self {
RewardType::Block => 0u32,
RewardType::Uncle => 1,
RewardType::EmptyStep => 2,
RewardType::External => 3,
};
Encodable::rlp_append(&v, s);
}
@ -158,6 +164,8 @@ impl Decodable for RewardType {
rlp.as_val().and_then(|v| Ok(match v {
0u32 => RewardType::Block,
1 => RewardType::Uncle,
2 => RewardType::EmptyStep,
3 => RewardType::External,
_ => return Err(DecoderError::Custom("Invalid value of RewardType item")),
}))
}

View File

@ -106,12 +106,4 @@ pub trait WithBalances: Machine {
/// Increment the balance of an account in the state of the live block.
fn add_balance(&self, live: &mut Self::LiveBlock, address: &Address, amount: &U256) -> Result<(), Self::Error>;
/// Note block rewards. "direct" rewards are for authors, "indirect" are for e.g. uncles.
fn note_rewards(
&self,
_live: &mut Self::LiveBlock,
_direct: &[(Address, U256)],
_indirect: &[(Address, U256)],
) -> Result<(), Self::Error> { Ok(()) }
}

View File

@ -308,6 +308,12 @@ pub enum RewardType {
/// Uncle
#[serde(rename="uncle")]
Uncle,
/// EmptyStep (AuthorityRound)
#[serde(rename="emptyStep")]
EmptyStep,
/// External (attributed as part of an external protocol)
#[serde(rename="external")]
External,
}
impl From<trace::RewardType> for RewardType {
@ -315,6 +321,8 @@ impl From<trace::RewardType> for RewardType {
match c {
trace::RewardType::Block => RewardType::Block,
trace::RewardType::Uncle => RewardType::Uncle,
trace::RewardType::EmptyStep => RewardType::EmptyStep,
trace::RewardType::External => RewardType::External,
}
}
}