Add block reward contract config to ethash and allow off-chain contracts (#9312)

This adds block reward contract config to ethash. A new config `blockRewardContractCode` is also added to both Aura and ethash. When specified, it will execute the code directly and overrides any `blockRewardContractAddress` config. Having this `blockRewardContractCode` config allows chains to deploy hard fork by simply replacing the current config value, without the need from us to support any `multi` block reward scheme.
This commit is contained in:
Wei Tang 2018-08-29 23:17:18 +08:00 committed by André Silva
parent 1073d56245
commit 74ce0f738e
8 changed files with 261 additions and 116 deletions

View File

@ -100,7 +100,11 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
immediate_transitions: p.immediate_transitions.unwrap_or(false), immediate_transitions: p.immediate_transitions.unwrap_or(false),
block_reward: p.block_reward.map_or_else(Default::default, Into::into), block_reward: p.block_reward.map_or_else(Default::default, Into::into),
block_reward_contract_transition: p.block_reward_contract_transition.map_or(0, Into::into), block_reward_contract_transition: p.block_reward_contract_transition.map_or(0, Into::into),
block_reward_contract: p.block_reward_contract_address.map(BlockRewardContract::new), block_reward_contract: match (p.block_reward_contract_code, p.block_reward_contract_address) {
(Some(code), _) => Some(BlockRewardContract::new_from_code(Arc::new(code.into()))),
(_, Some(address)) => Some(BlockRewardContract::new_from_address(address.into())),
(None, None) => None,
},
maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into), maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into),
maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into), maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into),
empty_steps_transition: p.empty_steps_transition.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)), empty_steps_transition: p.empty_steps_transition.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)),
@ -1043,7 +1047,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
/// Apply the block reward on finalisation of the block. /// Apply the block reward on finalisation of the block.
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
let mut benefactors = Vec::new(); let mut beneficiaries = Vec::new();
if block.header().number() >= self.empty_steps_transition { if block.header().number() >= self.empty_steps_transition {
let empty_steps = if block.header().seal().is_empty() { let empty_steps = if block.header().seal().is_empty() {
// this is a new block, calculate rewards based on the empty steps messages we have accumulated // this is a new block, calculate rewards based on the empty steps messages we have accumulated
@ -1069,32 +1073,22 @@ impl Engine<EthereumMachine> for AuthorityRound {
for empty_step in empty_steps { for empty_step in empty_steps {
let author = empty_step.author()?; let author = empty_step.author()?;
benefactors.push((author, RewardKind::EmptyStep)); beneficiaries.push((author, RewardKind::EmptyStep));
} }
} }
let author = *block.header().author(); let author = *block.header().author();
benefactors.push((author, RewardKind::Author)); beneficiaries.push((author, RewardKind::Author));
let rewards: Vec<_> = 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 => { Some(ref c) if block.header().number() >= self.block_reward_contract_transition => {
// NOTE: this logic should be moved to a function when another let mut call = super::default_system_or_code_call(&self.machine, block);
// engine needs support for block reward contract.
let mut call = |to, data| {
let result = self.machine.execute_as_system(
block,
to,
U256::max_value(), // unbounded gas? maybe make configurable.
Some(data),
);
result.map_err(|e| format!("{}", e))
};
let rewards = c.reward(&benefactors, &mut call)?; let rewards = c.reward(&beneficiaries, &mut call)?;
rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect() rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect()
}, },
_ => { _ => {
benefactors.into_iter().map(|(author, reward_kind)| (author, reward_kind, self.block_reward)).collect() beneficiaries.into_iter().map(|(author, reward_kind)| (author, reward_kind, self.block_reward)).collect()
}, },
}; };

View File

@ -21,33 +21,48 @@ use ethabi;
use ethabi::ParamType; use ethabi::ParamType;
use ethereum_types::{H160, Address, U256}; use ethereum_types::{H160, Address, U256};
use std::sync::Arc;
use hash::keccak;
use error::Error; use error::Error;
use machine::WithRewards; use machine::WithRewards;
use parity_machine::{Machine, WithBalances}; use parity_machine::{Machine, WithBalances};
use trace; use trace;
use super::SystemCall; use types::BlockNumber;
use super::{SystemOrCodeCall, SystemOrCodeCallKind};
use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward.json"); use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward.json");
/// The kind of block reward. /// The kind of block reward.
/// Depending on the consensus engine the allocated block reward might have /// Depending on the consensus engine the allocated block reward might have
/// different semantics which could lead e.g. to different reward values. /// different semantics which could lead e.g. to different reward values.
#[repr(u8)]
#[derive(PartialEq, Eq, Clone, Copy, Debug)] #[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum RewardKind { pub enum RewardKind {
/// Reward attributed to the block author. /// Reward attributed to the block author.
Author = 0, Author,
/// Reward attributed to the block uncle(s).
Uncle = 1,
/// Reward attributed to the author(s) of empty step(s) included in the block (AuthorityRound engine). /// Reward attributed to the author(s) of empty step(s) included in the block (AuthorityRound engine).
EmptyStep = 2, EmptyStep,
/// Reward attributed by an external protocol (e.g. block reward contract). /// Reward attributed by an external protocol (e.g. block reward contract).
External = 3, External,
/// Reward attributed to the block uncle(s) with given difference.
Uncle(u8),
}
impl RewardKind {
/// Create `RewardKind::Uncle` from given current block number and uncle block number.
pub fn uncle(number: BlockNumber, uncle: BlockNumber) -> Self {
RewardKind::Uncle(if number > uncle && number - uncle <= u8::max_value().into() { (number - uncle) as u8 } else { 0 })
}
} }
impl From<RewardKind> for u16 { impl From<RewardKind> for u16 {
fn from(reward_kind: RewardKind) -> Self { fn from(reward_kind: RewardKind) -> Self {
reward_kind as u16 match reward_kind {
RewardKind::Author => 0,
RewardKind::EmptyStep => 2,
RewardKind::External => 3,
RewardKind::Uncle(depth) => 100 + depth as u16,
}
} }
} }
@ -55,7 +70,7 @@ impl Into<trace::RewardType> for RewardKind {
fn into(self) -> trace::RewardType { fn into(self) -> trace::RewardType {
match self { match self {
RewardKind::Author => trace::RewardType::Block, RewardKind::Author => trace::RewardType::Block,
RewardKind::Uncle => trace::RewardType::Uncle, RewardKind::Uncle(_) => trace::RewardType::Uncle,
RewardKind::EmptyStep => trace::RewardType::EmptyStep, RewardKind::EmptyStep => trace::RewardType::EmptyStep,
RewardKind::External => trace::RewardType::External, RewardKind::External => trace::RewardType::External,
} }
@ -63,38 +78,50 @@ impl Into<trace::RewardType> for RewardKind {
} }
/// A client for the block reward contract. /// A client for the block reward contract.
#[derive(PartialEq, Debug)]
pub struct BlockRewardContract { pub struct BlockRewardContract {
/// Address of the contract. kind: SystemOrCodeCallKind,
address: Address,
block_reward_contract: block_reward_contract::BlockReward, block_reward_contract: block_reward_contract::BlockReward,
} }
impl BlockRewardContract { impl BlockRewardContract {
/// Create a new block reward contract client targeting the given address. /// Create a new block reward contract client targeting the system call kind.
pub fn new(address: Address) -> BlockRewardContract { pub fn new(kind: SystemOrCodeCallKind) -> BlockRewardContract {
BlockRewardContract { BlockRewardContract {
address, kind,
block_reward_contract: block_reward_contract::BlockReward::default(), block_reward_contract: block_reward_contract::BlockReward::default(),
} }
} }
/// Calls the block reward contract with the given benefactors list (and associated reward kind) /// Create a new block reward contract client targeting the contract address.
pub fn new_from_address(address: Address) -> BlockRewardContract {
Self::new(SystemOrCodeCallKind::Address(address))
}
/// Create a new block reward contract client targeting the given code.
pub fn new_from_code(code: Arc<Vec<u8>>) -> BlockRewardContract {
let code_hash = keccak(&code[..]);
Self::new(SystemOrCodeCallKind::Code(code, code_hash))
}
/// Calls the block reward contract with the given beneficiaries list (and associated reward kind)
/// and returns the reward allocation (address - value). The block reward contract *must* be /// and returns the reward allocation (address - value). The block reward contract *must* be
/// called by the system address so the `caller` must ensure that (e.g. using /// called by the system address so the `caller` must ensure that (e.g. using
/// `machine.execute_as_system`). /// `machine.execute_as_system`).
pub fn reward( pub fn reward(
&self, &self,
benefactors: &[(Address, RewardKind)], beneficiaries: &[(Address, RewardKind)],
caller: &mut SystemCall, caller: &mut SystemOrCodeCall,
) -> Result<Vec<(Address, U256)>, Error> { ) -> Result<Vec<(Address, U256)>, Error> {
let reward = self.block_reward_contract.functions().reward(); let reward = self.block_reward_contract.functions().reward();
let input = reward.input( let input = reward.input(
benefactors.iter().map(|&(address, _)| H160::from(address)), beneficiaries.iter().map(|&(address, _)| H160::from(address)),
benefactors.iter().map(|&(_, ref reward_kind)| u16::from(*reward_kind)), beneficiaries.iter().map(|&(_, ref reward_kind)| u16::from(*reward_kind)),
); );
let output = caller(self.address, input) let output = caller(self.kind.clone(), input)
.map_err(Into::into) .map_err(Into::into)
.map_err(::engines::EngineError::FailedSystemCall)?; .map_err(::engines::EngineError::FailedSystemCall)?;
@ -127,7 +154,7 @@ impl BlockRewardContract {
} }
} }
/// Applies the given block rewards, i.e. adds the given balance to each benefactors' address. /// Applies the given block rewards, i.e. adds the given balance to each beneficiary' address.
/// If tracing is enabled the operations are recorded. /// If tracing is enabled the operations are recorded.
pub fn apply_block_rewards<M: Machine + WithBalances + WithRewards>( pub fn apply_block_rewards<M: Machine + WithBalances + WithRewards>(
rewards: &[(Address, RewardKind, U256)], rewards: &[(Address, RewardKind, U256)],
@ -139,7 +166,7 @@ pub fn apply_block_rewards<M: Machine + WithBalances + WithRewards>(
} }
let rewards: Vec<_> = rewards.into_iter().map(|&(a, k, r)| (a, k.into(), r)).collect(); let rewards: Vec<_> = rewards.into_iter().map(|&(a, k, r)| (a, k.into(), r)).collect();
machine.note_rewards(block, &rewards) machine.note_rewards(block, &rewards)
} }
#[cfg(test)] #[cfg(test)]
@ -149,6 +176,7 @@ mod test {
use spec::Spec; use spec::Spec;
use test_helpers::generate_dummy_client_with_spec_and_accounts; use test_helpers::generate_dummy_client_with_spec_and_accounts;
use engines::SystemOrCodeCallKind;
use super::{BlockRewardContract, RewardKind}; use super::{BlockRewardContract, RewardKind};
#[test] #[test]
@ -161,7 +189,7 @@ mod test {
let machine = Spec::new_test_machine(); let machine = Spec::new_test_machine();
// the spec has a block reward contract defined at the given address // the spec has a block reward contract defined at the given address
let block_reward_contract = BlockRewardContract::new( let block_reward_contract = BlockRewardContract::new_from_address(
"0000000000000000000000000000000000000042".into(), "0000000000000000000000000000000000000042".into(),
); );
@ -172,30 +200,35 @@ mod test {
vec![], vec![],
).unwrap(); ).unwrap();
let result = machine.execute_as_system( let result = match to {
block.block_mut(), SystemOrCodeCallKind::Address(to) => {
to, machine.execute_as_system(
U256::max_value(), block.block_mut(),
Some(data), to,
); U256::max_value(),
Some(data),
)
},
_ => panic!("Test reward contract is created by an address, we never reach this branch."),
};
result.map_err(|e| format!("{}", e)) result.map_err(|e| format!("{}", e))
}; };
// if no benefactors are given no rewards are attributed // if no beneficiaries are given no rewards are attributed
assert!(block_reward_contract.reward(&vec![], &mut call).unwrap().is_empty()); assert!(block_reward_contract.reward(&vec![], &mut call).unwrap().is_empty());
// the contract rewards (1000 + kind) for each benefactor // the contract rewards (1000 + kind) for each benefactor
let benefactors = vec![ let beneficiaries = vec![
("0000000000000000000000000000000000000033".into(), RewardKind::Author), ("0000000000000000000000000000000000000033".into(), RewardKind::Author),
("0000000000000000000000000000000000000034".into(), RewardKind::Uncle), ("0000000000000000000000000000000000000034".into(), RewardKind::Uncle(1)),
("0000000000000000000000000000000000000035".into(), RewardKind::EmptyStep), ("0000000000000000000000000000000000000035".into(), RewardKind::EmptyStep),
]; ];
let rewards = block_reward_contract.reward(&benefactors, &mut call).unwrap(); let rewards = block_reward_contract.reward(&beneficiaries, &mut call).unwrap();
let expected = vec![ let expected = vec![
("0000000000000000000000000000000000000033".into(), U256::from(1000)), ("0000000000000000000000000000000000000033".into(), U256::from(1000)),
("0000000000000000000000000000000000000034".into(), U256::from(1000 + 1)), ("0000000000000000000000000000000000000034".into(), U256::from(1000 + 101)),
("0000000000000000000000000000000000000035".into(), U256::from(1000 + 2)), ("0000000000000000000000000000000000000035".into(), U256::from(1000 + 2)),
]; ];

View File

@ -133,6 +133,46 @@ pub enum Seal {
/// A system-calling closure. Enacts calls on a block's state from the system address. /// A system-calling closure. Enacts calls on a block's state from the system address.
pub type SystemCall<'a> = FnMut(Address, Vec<u8>) -> Result<Vec<u8>, String> + 'a; pub type SystemCall<'a> = FnMut(Address, Vec<u8>) -> Result<Vec<u8>, String> + 'a;
/// A system-calling closure. Enacts calls on a block's state with code either from an on-chain contract, or hard-coded EVM or WASM (if enabled on-chain) codes.
pub type SystemOrCodeCall<'a> = FnMut(SystemOrCodeCallKind, Vec<u8>) -> Result<Vec<u8>, String> + 'a;
/// Kind of SystemOrCodeCall, this is either an on-chain address, or code.
#[derive(PartialEq, Debug, Clone)]
pub enum SystemOrCodeCallKind {
/// On-chain address.
Address(Address),
/// Hard-coded code.
Code(Arc<Vec<u8>>, H256),
}
/// Default SystemOrCodeCall implementation.
pub fn default_system_or_code_call<'a>(machine: &'a ::machine::EthereumMachine, block: &'a mut ::block::ExecutedBlock) -> impl FnMut(SystemOrCodeCallKind, Vec<u8>) -> Result<Vec<u8>, String> + 'a {
move |to, data| {
let result = match to {
SystemOrCodeCallKind::Address(address) => {
machine.execute_as_system(
block,
address,
U256::max_value(),
Some(data),
)
},
SystemOrCodeCallKind::Code(code, code_hash) => {
machine.execute_code_as_system(
block,
None,
Some(code),
Some(code_hash),
U256::max_value(),
Some(data),
)
},
};
result.map_err(|e| format!("{}", e))
}
}
/// Type alias for a function we can get headers by hash through. /// Type alias for a function we can get headers by hash through.
pub type Headers<'a, H> = Fn(H256) -> Option<H> + 'a; pub type Headers<'a, H> = Fn(H256) -> Option<H> + 'a;

View File

@ -89,7 +89,7 @@ impl<M: WithBalances + WithRewards> Engine<M> for NullEngine<M>
for u in LiveBlock::uncles(&*block) { for u in LiveBlock::uncles(&*block) {
let uncle_author = u.author(); let uncle_author = u.author();
let result_uncle_reward = (reward * U256::from(8 + u.number() - number)).shr(3); let result_uncle_reward = (reward * U256::from(8 + u.number() - number)).shr(3);
rewards.push((*uncle_author, RewardKind::Uncle, result_uncle_reward)); rewards.push((*uncle_author, RewardKind::uncle(number, u.number()), result_uncle_reward));
} }
block_reward::apply_block_rewards(&rewards, block, &self.machine) block_reward::apply_block_rewards(&rewards, block, &self.machine)

View File

@ -19,7 +19,7 @@ use std::cmp;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::Arc; use std::sync::Arc;
use hash::{KECCAK_EMPTY_LIST_RLP}; use hash::{KECCAK_EMPTY_LIST_RLP};
use engines::block_reward::{self, RewardKind}; use engines::block_reward::{self, BlockRewardContract, RewardKind};
use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor}; use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor};
use ethereum_types::{H256, H64, U256, Address}; use ethereum_types::{H256, H64, U256, Address};
use unexpected::{OutOfBounds, Mismatch}; use unexpected::{OutOfBounds, Mismatch};
@ -124,6 +124,10 @@ pub struct EthashParams {
pub expip2_transition: u64, pub expip2_transition: u64,
/// EXPIP-2 duration limit /// EXPIP-2 duration limit
pub expip2_duration_limit: u64, pub expip2_duration_limit: u64,
/// Block reward contract transition block.
pub block_reward_contract_transition: u64,
/// Block reward contract.
pub block_reward_contract: Option<BlockRewardContract>,
} }
impl From<ethjson::spec::EthashParams> for EthashParams { impl From<ethjson::spec::EthashParams> for EthashParams {
@ -154,6 +158,12 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
eip649_reward: p.eip649_reward.map(Into::into), eip649_reward: p.eip649_reward.map(Into::into),
expip2_transition: p.expip2_transition.map_or(u64::max_value(), Into::into), expip2_transition: p.expip2_transition.map_or(u64::max_value(), Into::into),
expip2_duration_limit: p.expip2_duration_limit.map_or(30, Into::into), expip2_duration_limit: p.expip2_duration_limit.map_or(30, Into::into),
block_reward_contract_transition: p.block_reward_contract_transition.map_or(0, Into::into),
block_reward_contract: match (p.block_reward_contract_code, p.block_reward_contract_address) {
(Some(code), _) => Some(BlockRewardContract::new_from_code(Arc::new(code.into()))),
(_, Some(address)) => Some(BlockRewardContract::new_from_address(address.into())),
(None, None) => None,
},
} }
} }
} }
@ -231,52 +241,72 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
let author = *LiveBlock::header(&*block).author(); let author = *LiveBlock::header(&*block).author();
let number = LiveBlock::header(&*block).number(); let number = LiveBlock::header(&*block).number();
let mut rewards = Vec::new(); let rewards = match self.ethash_params.block_reward_contract {
Some(ref c) if number >= self.ethash_params.block_reward_contract_transition => {
let mut beneficiaries = Vec::new();
// Applies EIP-649 reward. beneficiaries.push((author, RewardKind::Author));
let reward = if number >= self.ethash_params.eip649_transition { for u in LiveBlock::uncles(&*block) {
self.ethash_params.eip649_reward.unwrap_or(self.ethash_params.block_reward) let uncle_author = u.author();
} else { beneficiaries.push((*uncle_author, RewardKind::uncle(number, u.number())));
self.ethash_params.block_reward }
let mut call = engines::default_system_or_code_call(&self.machine, block);
let rewards = c.reward(&beneficiaries, &mut call)?;
rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect()
},
_ => {
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)
} else {
self.ethash_params.block_reward
};
// Applies ECIP-1017 eras.
let eras_rounds = self.ethash_params.ecip1017_era_rounds;
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, number);
let n_uncles = LiveBlock::uncles(&*block).len();
// Bestow block rewards.
let mut result_block_reward = reward + reward.shr(5) * U256::from(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;
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 {
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 = if eras == 0 {
(reward * U256::from(8 + u.number() - number)).shr(3)
} else {
reward.shr(5)
};
rewards.push((*uncle_author, RewardKind::uncle(number, u.number()), result_uncle_reward));
}
rewards
},
}; };
// Applies ECIP-1017 eras.
let eras_rounds = self.ethash_params.ecip1017_era_rounds;
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, number);
let n_uncles = LiveBlock::uncles(&*block).len();
// Bestow block rewards.
let mut result_block_reward = reward + reward.shr(5) * U256::from(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;
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 {
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 = if eras == 0 {
(reward * U256::from(8 + u.number() - number)).shr(3)
} else {
reward.shr(5)
};
rewards.push((*uncle_author, RewardKind::Uncle, result_uncle_reward));
}
block_reward::apply_block_rewards(&rewards, block, &self.machine) block_reward::apply_block_rewards(&rewards, block, &self.machine)
} }
@ -512,6 +542,8 @@ mod tests {
eip649_reward: None, eip649_reward: None,
expip2_transition: u64::max_value(), expip2_transition: u64::max_value(),
expip2_duration_limit: 30, expip2_duration_limit: 30,
block_reward_contract: None,
block_reward_contract_transition: 0,
} }
} }

View File

@ -29,10 +29,10 @@ use header::{BlockNumber, Header, ExtendedHeader};
use spec::CommonParams; use spec::CommonParams;
use state::{CleanupMode, Substate}; use state::{CleanupMode, Substate};
use trace::{NoopTracer, NoopVMTracer, Tracer, ExecutiveTracer, RewardType, Tracing}; use trace::{NoopTracer, NoopVMTracer, Tracer, ExecutiveTracer, RewardType, Tracing};
use transaction::{self, SYSTEM_ADDRESS, UnverifiedTransaction, SignedTransaction}; use transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction};
use tx_filter::TransactionFilter; use tx_filter::TransactionFilter;
use ethereum_types::{U256, Address}; use ethereum_types::{U256, H256, Address};
use rlp::Rlp; use rlp::Rlp;
use vm::{CallType, ActionParams, ActionValue, ParamsType}; use vm::{CallType, ActionParams, ActionValue, ParamsType};
use vm::{EnvInfo, Schedule, CreateContractAddress}; use vm::{EnvInfo, Schedule, CreateContractAddress};
@ -122,6 +122,35 @@ impl EthereumMachine {
contract_address: Address, contract_address: Address,
gas: U256, gas: U256,
data: Option<Vec<u8>>, data: Option<Vec<u8>>,
) -> Result<Vec<u8>, Error> {
let (code, code_hash) = {
let state = block.state();
(state.code(&contract_address)?,
state.code_hash(&contract_address)?)
};
self.execute_code_as_system(
block,
Some(contract_address),
code,
code_hash,
gas,
data
)
}
/// Same as execute_as_system, but execute code directly. If contract address is None, use the null sender
/// address. If code is None, then this function has no effect. The call is executed without finalization, and does
/// not form a transaction.
pub fn execute_code_as_system(
&self,
block: &mut ExecutedBlock,
contract_address: Option<Address>,
code: Option<Arc<Vec<u8>>>,
code_hash: Option<H256>,
gas: U256,
data: Option<Vec<u8>>
) -> Result<Vec<u8>, Error> { ) -> Result<Vec<u8>, Error> {
let env_info = { let env_info = {
let mut env_info = block.env_info(); let mut env_info = block.env_info();
@ -130,31 +159,27 @@ impl EthereumMachine {
}; };
let mut state = block.state_mut(); let mut state = block.state_mut();
let params = ActionParams { let params = ActionParams {
code_address: contract_address.clone(), code_address: contract_address.unwrap_or(UNSIGNED_SENDER),
address: contract_address.clone(), address: contract_address.unwrap_or(UNSIGNED_SENDER),
sender: SYSTEM_ADDRESS.clone(), sender: SYSTEM_ADDRESS,
origin: SYSTEM_ADDRESS.clone(), origin: SYSTEM_ADDRESS,
gas: gas, gas,
gas_price: 0.into(), gas_price: 0.into(),
value: ActionValue::Transfer(0.into()), value: ActionValue::Transfer(0.into()),
code: state.code(&contract_address)?, code,
code_hash: state.code_hash(&contract_address)?, code_hash,
data: data, data,
call_type: CallType::Call, call_type: CallType::Call,
params_type: ParamsType::Separate, params_type: ParamsType::Separate,
}; };
let schedule = self.schedule(env_info.number); let schedule = self.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, self, &schedule); let mut ex = Executive::new(&mut state, &env_info, self, &schedule);
let mut substate = Substate::new(); let mut substate = Substate::new();
let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer);
let output = match res { let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).map_err(|e| ::engines::EngineError::FailedSystemCall(format!("{}", e)))?;
Ok(res) => res.return_data.to_vec(), let output = res.return_data.to_vec();
Err(e) => {
warn!("Encountered error on making system call: {}", e);
Vec::new()
}
};
Ok(output) Ok(output)
} }

View File

@ -16,8 +16,9 @@
//! Authority params deserialization. //! Authority params deserialization.
use ethereum_types::Address; use hash::Address;
use uint::Uint; use uint::Uint;
use bytes::Bytes;
use super::ValidatorSet; use super::ValidatorSet;
/// Authority params deserialization. /// Authority params deserialization.
@ -51,6 +52,9 @@ pub struct AuthorityRoundParams {
/// overrides the static block reward definition). /// overrides the static block reward definition).
#[serde(rename="blockRewardContractAddress")] #[serde(rename="blockRewardContractAddress")]
pub block_reward_contract_address: Option<Address>, pub block_reward_contract_address: Option<Address>,
/// Block reward code. This overrides the block reward contract address.
#[serde(rename="blockRewardContractCode")]
pub block_reward_contract_code: Option<Bytes>,
/// Block at which maximum uncle count should be considered. /// Block at which maximum uncle count should be considered.
#[serde(rename="maximumUncleCountTransition")] #[serde(rename="maximumUncleCountTransition")]
pub maximum_uncle_count_transition: Option<Uint>, pub maximum_uncle_count_transition: Option<Uint>,

View File

@ -17,6 +17,7 @@
//! Ethash params deserialization. //! Ethash params deserialization.
use uint::{self, Uint}; use uint::{self, Uint};
use bytes::Bytes;
use hash::Address; use hash::Address;
/// Deserializable doppelganger of EthashParams. /// Deserializable doppelganger of EthashParams.
@ -48,6 +49,16 @@ pub struct EthashParams {
/// Reward per block in wei. /// Reward per block in wei.
#[serde(rename="blockReward")] #[serde(rename="blockReward")]
pub block_reward: Option<Uint>, pub block_reward: Option<Uint>,
/// Block at which the block reward contract should start being used.
#[serde(rename="blockRewardContractTransition")]
pub block_reward_contract_transition: Option<Uint>,
/// Block reward contract address (setting the block reward contract
/// overrides all other block reward parameters).
#[serde(rename="blockRewardContractAddress")]
pub block_reward_contract_address: Option<Address>,
/// Block reward code. This overrides the block reward contract address.
#[serde(rename="blockRewardContractCode")]
pub block_reward_contract_code: Option<Bytes>,
/// See main EthashParams docs. /// See main EthashParams docs.
#[serde(rename="daoHardforkTransition")] #[serde(rename="daoHardforkTransition")]
@ -183,7 +194,7 @@ mod tests {
let deserialized: Ethash = serde_json::from_str(s).unwrap(); let deserialized: Ethash = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, Ethash { assert_eq!(deserialized, Ethash {
params: EthashParams{ params: EthashParams {
minimum_difficulty: Uint(U256::from(0x020000)), minimum_difficulty: Uint(U256::from(0x020000)),
difficulty_bound_divisor: Uint(U256::from(0x0800)), difficulty_bound_divisor: Uint(U256::from(0x0800)),
difficulty_increment_divisor: None, difficulty_increment_divisor: None,
@ -191,6 +202,9 @@ mod tests {
duration_limit: Some(Uint(U256::from(0x0d))), duration_limit: Some(Uint(U256::from(0x0d))),
homestead_transition: Some(Uint(U256::from(0x42))), homestead_transition: Some(Uint(U256::from(0x42))),
block_reward: Some(Uint(U256::from(0x100))), block_reward: Some(Uint(U256::from(0x100))),
block_reward_contract_address: None,
block_reward_contract_code: None,
block_reward_contract_transition: None,
dao_hardfork_transition: Some(Uint(U256::from(0x08))), dao_hardfork_transition: Some(Uint(U256::from(0x08))),
dao_hardfork_beneficiary: Some(Address(H160::from("0xabcabcabcabcabcabcabcabcabcabcabcabcabca"))), dao_hardfork_beneficiary: Some(Address(H160::from("0xabcabcabcabcabcabcabcabcabcabcabcabcabca"))),
dao_hardfork_accounts: Some(vec![ dao_hardfork_accounts: Some(vec![
@ -256,6 +270,9 @@ mod tests {
duration_limit: None, duration_limit: None,
homestead_transition: None, homestead_transition: None,
block_reward: None, block_reward: None,
block_reward_contract_address: None,
block_reward_contract_code: None,
block_reward_contract_transition: None,
dao_hardfork_transition: None, dao_hardfork_transition: None,
dao_hardfork_beneficiary: None, dao_hardfork_beneficiary: None,
dao_hardfork_accounts: None, dao_hardfork_accounts: None,