From cf6135bf38e23f618f2edbb1134964abf68ff98d Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 8 Aug 2018 18:36:25 +0800 Subject: [PATCH 01/10] Add execute_code_as_system and throw error if system call fails --- ethcore/src/machine.rs | 54 ++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index d487fff9b77..7348a249eef 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -29,10 +29,10 @@ use header::{BlockNumber, Header, ExtendedHeader}; use spec::CommonParams; use state::{CleanupMode, Substate}; 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 ethereum_types::{U256, Address}; +use ethereum_types::{U256, H256, Address}; use bytes::BytesRef; use rlp::Rlp; use vm::{CallType, ActionParams, ActionValue, ParamsType}; @@ -123,6 +123,35 @@ impl EthereumMachine { contract_address: Address, gas: U256, data: Option>, + ) -> Result, 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
, + code: Option>>, + code_hash: Option, + gas: U256, + data: Option> ) -> Result, Error> { let env_info = { let mut env_info = block.env_info(); @@ -131,17 +160,18 @@ impl EthereumMachine { }; let mut state = block.state_mut(); + let params = ActionParams { - code_address: contract_address.clone(), - address: contract_address.clone(), - sender: SYSTEM_ADDRESS.clone(), - origin: SYSTEM_ADDRESS.clone(), - gas: gas, + code_address: contract_address.unwrap_or(UNSIGNED_SENDER), + address: contract_address.unwrap_or(UNSIGNED_SENDER), + sender: SYSTEM_ADDRESS, + origin: SYSTEM_ADDRESS, + gas, gas_price: 0.into(), value: ActionValue::Transfer(0.into()), - code: state.code(&contract_address)?, - code_hash: state.code_hash(&contract_address)?, - data: data, + code, + code_hash, + data, call_type: CallType::Call, params_type: ParamsType::Separate, }; @@ -149,9 +179,7 @@ impl EthereumMachine { let mut ex = Executive::new(&mut state, &env_info, self, &schedule); let mut substate = Substate::new(); let mut output = Vec::new(); - if let Err(e) = ex.call(params, &mut substate, BytesRef::Flexible(&mut output), &mut NoopTracer, &mut NoopVMTracer) { - warn!("Encountered error on making system call: {}", e); - } + ex.call(params, &mut substate, BytesRef::Flexible(&mut output), &mut NoopTracer, &mut NoopVMTracer).map_err(|e| ::engines::EngineError::FailedSystemCall(format!("{}", e)))?; Ok(output) } From 88d1eaef62cbdd560a621bc3ee079cfd89c62215 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 8 Aug 2018 18:55:40 +0800 Subject: [PATCH 02/10] Add SystemOrCodeCallKind and change block reward contract to use it --- ethcore/src/engines/authority_round/mod.rs | 30 +++++++++---- ethcore/src/engines/block_reward.rs | 49 +++++++++++++++------- ethcore/src/engines/mod.rs | 12 ++++++ 3 files changed, 69 insertions(+), 22 deletions(-) diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 0c4906be565..cc09c68254b 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -35,6 +35,7 @@ use ethjson; use machine::{AuxiliaryData, Call, EthereumMachine}; use hash::keccak; use header::{Header, BlockNumber, ExtendedHeader}; +use super::SystemOrCodeCallKind; use super::signer::EngineSigner; use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; use self::finality::RollingFinality; @@ -100,7 +101,7 @@ impl From for AuthorityRoundParams { immediate_transitions: p.immediate_transitions.unwrap_or(false), 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: p.block_reward_contract_address.map(BlockRewardContract::new), + block_reward_contract: p.block_reward_contract_address.map(BlockRewardContract::new_from_address), 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), empty_steps_transition: p.empty_steps_transition.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)), @@ -1081,12 +1082,27 @@ impl Engine for AuthorityRound { // 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, - to, - U256::max_value(), // unbounded gas? maybe make configurable. - Some(data), - ); + let result = match to { + SystemOrCodeCallKind::Address(address) => { + self.machine.execute_as_system( + block, + address, + U256::max_value(), + Some(data), + ) + }, + SystemOrCodeCallKind::Code(code, code_hash) => { + self.machine.execute_code_as_system( + block, + None, + Some(code), + Some(code_hash), + U256::max_value(), + Some(data), + ) + }, + }; + result.map_err(|e| format!("{}", e)) }; diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/src/engines/block_reward.rs index 7144ed78d64..d4cd432fe9c 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -21,11 +21,13 @@ use ethabi; use ethabi::ParamType; use ethereum_types::{H160, Address, U256}; +use std::sync::Arc; +use hash::keccak; use error::Error; use machine::WithRewards; use parity_machine::{Machine, WithBalances}; use trace; -use super::SystemCall; +use super::{SystemOrCodeCall, SystemOrCodeCallKind}; use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward.json"); @@ -64,20 +66,31 @@ impl Into for RewardKind { /// A client for the block reward contract. pub struct BlockRewardContract { - /// Address of the contract. - address: Address, + kind: SystemOrCodeCallKind, block_reward_contract: block_reward_contract::BlockReward, } impl BlockRewardContract { - /// Create a new block reward contract client targeting the given address. - pub fn new(address: Address) -> BlockRewardContract { + /// Create a new block reward contract client targeting the system call kind. + pub fn new(kind: SystemOrCodeCallKind) -> BlockRewardContract { BlockRewardContract { - address, + kind, block_reward_contract: block_reward_contract::BlockReward::default(), } } + /// 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>) -> BlockRewardContract { + let code_hash = keccak(&code[..]); + + Self::new(SystemOrCodeCallKind::Code(code, code_hash)) + } + /// Calls the block reward contract with the given benefactors list (and associated reward kind) /// 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 @@ -85,7 +98,7 @@ impl BlockRewardContract { pub fn reward( &self, benefactors: &[(Address, RewardKind)], - caller: &mut SystemCall, + caller: &mut SystemOrCodeCall, ) -> Result, Error> { let reward = self.block_reward_contract.functions().reward(); @@ -94,7 +107,7 @@ impl BlockRewardContract { benefactors.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(::engines::EngineError::FailedSystemCall)?; @@ -149,6 +162,7 @@ mod test { use spec::Spec; use test_helpers::generate_dummy_client_with_spec_and_accounts; + use engines::SystemOrCodeCallKind; use super::{BlockRewardContract, RewardKind}; #[test] @@ -161,7 +175,7 @@ mod test { let machine = Spec::new_test_machine(); // 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(), ); @@ -172,12 +186,17 @@ mod test { vec![], ).unwrap(); - let result = machine.execute_as_system( - block.block_mut(), - to, - U256::max_value(), - Some(data), - ); + let result = match to { + SystemOrCodeCallKind::Address(to) => { + machine.execute_as_system( + block.block_mut(), + 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)) }; diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 8f67a039f56..8ea05e1c61a 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -133,6 +133,18 @@ pub enum Seal { /// A system-calling closure. Enacts calls on a block's state from the system address. pub type SystemCall<'a> = FnMut(Address, Vec) -> Result, 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) -> Result, String> + 'a; + +/// Kind of SystemOrCodeCall, this is either an on-chain address, or code. +#[derive(Clone)] +pub enum SystemOrCodeCallKind { + /// On-chain address. + Address(Address), + /// Hard-coded code. + Code(Arc>, H256), +} + /// Type alias for a function we can get headers by hash through. pub type Headers<'a, H> = Fn(H256) -> Option + 'a; From d09d0312bc6aa02f97a7ede9390958d93468de2c Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 8 Aug 2018 19:03:40 +0800 Subject: [PATCH 03/10] Add block_reward_contract_code param --- ethcore/src/engines/authority_round/mod.rs | 6 +++++- json/src/spec/authority_round.rs | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index cc09c68254b..ab15eda6d2c 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -101,7 +101,11 @@ impl From for AuthorityRoundParams { immediate_transitions: p.immediate_transitions.unwrap_or(false), 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: p.block_reward_contract_address.map(BlockRewardContract::new_from_address), + 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)), + (None, None) => None, + }, 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), empty_steps_transition: p.empty_steps_transition.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)), diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index e355c6fe951..af52656509b 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -18,6 +18,7 @@ use ethereum_types::Address; use uint::Uint; +use bytes::Bytes; use super::ValidatorSet; /// Authority params deserialization. @@ -51,6 +52,9 @@ pub struct AuthorityRoundParams { /// overrides the static block reward definition). #[serde(rename="blockRewardContractAddress")] pub block_reward_contract_address: Option
, + /// Block reward code. This overrides the block reward contract address. + #[serde(rename="blockRewardContractCode")] + pub block_reward_contract_code: Option, /// Block at which maximum uncle count should be considered. #[serde(rename="maximumUncleCountTransition")] pub maximum_uncle_count_transition: Option, From 6c4d2c473babd9d91012e2e6ac72fcf529940775 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 8 Aug 2018 19:40:36 +0800 Subject: [PATCH 04/10] Add reward contract support for ethash --- ethcore/src/engines/authority_round/mod.rs | 2 +- ethcore/src/engines/block_reward.rs | 29 +++-- ethcore/src/engines/mod.rs | 2 +- ethcore/src/ethereum/ethash.rs | 139 +++++++++++++++------ json/src/spec/authority_round.rs | 2 +- json/src/spec/ethash.rs | 11 ++ 6 files changed, 132 insertions(+), 53 deletions(-) diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index ab15eda6d2c..7278d28cca6 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -103,7 +103,7 @@ impl From for AuthorityRoundParams { 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)), + (_, 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), diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/src/engines/block_reward.rs index d4cd432fe9c..0fce7d2d1a1 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -34,22 +34,31 @@ use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward. /// The kind of block reward. /// Depending on the consensus engine the allocated block reward might have /// different semantics which could lead e.g. to different reward values. -#[repr(u8)] #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub enum RewardKind { /// Reward attributed to the block author. - Author = 0, - /// Reward attributed to the block uncle(s). - Uncle = 1, + Author, /// 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). - External = 3, + External, + + /// Reward attributed to the block uncle(s) without any reference of block difference. This is used by all except Ethash engine's reward contract. + Uncle, + /// Reward attributed to the block uncle(s) with given difference. This is used by Ethash engine's reward contract. + UncleWithDifference(u16), } impl From for u16 { fn from(reward_kind: RewardKind) -> Self { - reward_kind as u16 + match reward_kind { + RewardKind::Author => 0, + RewardKind::EmptyStep => 2, + RewardKind::External => 3, + + RewardKind::Uncle => 1, + RewardKind::UncleWithDifference(diff) => 100 + diff, + } } } @@ -57,7 +66,8 @@ impl Into for RewardKind { fn into(self) -> trace::RewardType { match self { RewardKind::Author => trace::RewardType::Block, - RewardKind::Uncle => trace::RewardType::Uncle, + RewardKind::Uncle | RewardKind::UncleWithDifference(_) => + trace::RewardType::Uncle, RewardKind::EmptyStep => trace::RewardType::EmptyStep, RewardKind::External => trace::RewardType::External, } @@ -65,6 +75,7 @@ impl Into for RewardKind { } /// A client for the block reward contract. +#[derive(PartialEq, Debug)] pub struct BlockRewardContract { kind: SystemOrCodeCallKind, block_reward_contract: block_reward_contract::BlockReward, @@ -152,7 +163,7 @@ pub fn apply_block_rewards( } 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)] diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 8ea05e1c61a..f5903470f29 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -137,7 +137,7 @@ pub type SystemCall<'a> = FnMut(Address, Vec) -> Result, String> + ' pub type SystemOrCodeCall<'a> = FnMut(SystemOrCodeCallKind, Vec) -> Result, String> + 'a; /// Kind of SystemOrCodeCall, this is either an on-chain address, or code. -#[derive(Clone)] +#[derive(PartialEq, Debug, Clone)] pub enum SystemOrCodeCallKind { /// On-chain address. Address(Address), diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index ca19983d3e1..3957b5f0d3b 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -19,7 +19,8 @@ use std::cmp; use std::collections::BTreeMap; use std::sync::Arc; use hash::{KECCAK_EMPTY_LIST_RLP}; -use engines::block_reward::{self, RewardKind}; +use engines::SystemOrCodeCallKind; +use engines::block_reward::{self, BlockRewardContract, RewardKind}; use ethash::{quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor}; use ethereum_types::{H256, H64, U256, Address}; use unexpected::{OutOfBounds, Mismatch}; @@ -124,6 +125,10 @@ pub struct EthashParams { pub expip2_transition: u64, /// EXPIP-2 duration limit pub expip2_duration_limit: u64, + /// Block reward contract transition block. + pub block_reward_contract_transition: u64, + /// Block reward contract. + pub block_reward_contract: Option, } impl From for EthashParams { @@ -154,6 +159,12 @@ impl From for EthashParams { eip649_reward: p.eip649_reward.map(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), + 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,51 +242,95 @@ impl Engine for Arc { let author = *LiveBlock::header(&*block).author(); 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 benefactors = 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; + benefactors.push((author, RewardKind::Author)); + for u in LiveBlock::uncles(&*block) { + let uncle_author = u.author(); + let uncle_diff = (number - u.number()) as u16; + benefactors.push((*uncle_author, RewardKind::UncleWithDifference(uncle_diff))); + } - rewards.push((author, RewardKind::Author, result_block_reward)); - rewards.push((ubi_contract, RewardKind::External, ubi_reward)); - rewards.push((dev_contract, RewardKind::External, dev_reward)); + let mut call = |to, data| { + let result = match to { + SystemOrCodeCallKind::Address(address) => { + self.machine.execute_as_system( + block, + address, + U256::max_value(), + Some(data), + ) + }, + SystemOrCodeCallKind::Code(code, code_hash) => { + self.machine.execute_code_as_system( + block, + None, + Some(code), + Some(code_hash), + U256::max_value(), + Some(data), + ) + }, + }; + + result.map_err(|e| format!("{}", e)) + }; + + let rewards = c.reward(&benefactors, &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)); + } - } 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) + }; - // 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)); + } - rewards.push((*uncle_author, RewardKind::Uncle, result_uncle_reward)); - } + rewards + }, + }; block_reward::apply_block_rewards(&rewards, block, &self.machine) } @@ -531,6 +586,8 @@ mod tests { eip649_reward: None, expip2_transition: u64::max_value(), expip2_duration_limit: 30, + block_reward_contract: None, + block_reward_contract_transition: 0, } } diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index af52656509b..b4fcf4d78cb 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -16,7 +16,7 @@ //! Authority params deserialization. -use ethereum_types::Address; +use hash::Address; use uint::Uint; use bytes::Bytes; use super::ValidatorSet; diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index 19fd0966273..b3c9bff983e 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -17,6 +17,7 @@ //! Ethash params deserialization. use uint::{self, Uint}; +use bytes::Bytes; use hash::Address; /// Deserializable doppelganger of EthashParams. @@ -47,6 +48,16 @@ pub struct EthashParams { /// Reward per block in wei. #[serde(rename="blockReward")] pub block_reward: Option, + /// Block at which the block reward contract should start being used. + #[serde(rename="blockRewardContractTransition")] + pub block_reward_contract_transition: Option, + /// Block reward contract address (setting the block reward contract + /// overrides all other block reward parameters). + #[serde(rename="blockRewardContractAddress")] + pub block_reward_contract_address: Option
, + /// Block reward code. This overrides the block reward contract address. + #[serde(rename="blockRewardContractCode")] + pub block_reward_contract_code: Option, /// See main EthashParams docs. #[serde(rename="daoHardforkTransition")] From 6c039807bb0b83913bd1bf5eb770d2331252c59b Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 8 Aug 2018 19:42:26 +0800 Subject: [PATCH 05/10] Fix json crate compile --- json/src/spec/ethash.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index b3c9bff983e..a727abb2090 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -193,7 +193,7 @@ mod tests { let deserialized: Ethash = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, Ethash { - params: EthashParams{ + params: EthashParams { minimum_difficulty: Uint(U256::from(0x020000)), difficulty_bound_divisor: Uint(U256::from(0x0800)), difficulty_increment_divisor: None, @@ -201,6 +201,9 @@ mod tests { duration_limit: Some(Uint(U256::from(0x0d))), homestead_transition: Some(Uint(U256::from(0x42))), 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_beneficiary: Some(Address(H160::from("0xabcabcabcabcabcabcabcabcabcabcabcabcabca"))), dao_hardfork_accounts: Some(vec![ @@ -266,6 +269,9 @@ mod tests { duration_limit: None, homestead_transition: 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_beneficiary: None, dao_hardfork_accounts: None, From 5c379d410d5ebe7217ea8e6a0b8b9e22edc27a9b Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 8 Aug 2018 19:53:17 +0800 Subject: [PATCH 06/10] Be more graceful on uncle with depth calculation --- ethcore/src/engines/block_reward.rs | 6 +++--- ethcore/src/ethereum/ethash.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/src/engines/block_reward.rs index 0fce7d2d1a1..22e6b551abc 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -46,7 +46,7 @@ pub enum RewardKind { /// Reward attributed to the block uncle(s) without any reference of block difference. This is used by all except Ethash engine's reward contract. Uncle, /// Reward attributed to the block uncle(s) with given difference. This is used by Ethash engine's reward contract. - UncleWithDifference(u16), + UncleWithDepth(u8), } impl From for u16 { @@ -57,7 +57,7 @@ impl From for u16 { RewardKind::External => 3, RewardKind::Uncle => 1, - RewardKind::UncleWithDifference(diff) => 100 + diff, + RewardKind::UncleWithDepth(depth) => 100 + depth as u16, } } } @@ -66,7 +66,7 @@ impl Into for RewardKind { fn into(self) -> trace::RewardType { match self { RewardKind::Author => trace::RewardType::Block, - RewardKind::Uncle | RewardKind::UncleWithDifference(_) => + RewardKind::Uncle | RewardKind::UncleWithDepth(_) => trace::RewardType::Uncle, RewardKind::EmptyStep => trace::RewardType::EmptyStep, RewardKind::External => trace::RewardType::External, diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 3957b5f0d3b..c122a28ff5b 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -249,8 +249,8 @@ impl Engine for Arc { benefactors.push((author, RewardKind::Author)); for u in LiveBlock::uncles(&*block) { let uncle_author = u.author(); - let uncle_diff = (number - u.number()) as u16; - benefactors.push((*uncle_author, RewardKind::UncleWithDifference(uncle_diff))); + let uncle_diff = if number > u.number() && number - u.number() <= u8::max_value().into() { (number - u.number()) as u8 } else { 0 }; + benefactors.push((*uncle_author, RewardKind::UncleWithDepth(uncle_diff))); } let mut call = |to, data| { From 0ea2236129fefe0da8157862366e95a9a4a1afea Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Fri, 10 Aug 2018 17:22:14 +0800 Subject: [PATCH 07/10] benefactors -> beneficiaries --- ethcore/src/engines/authority_round/mod.rs | 10 +++++----- ethcore/src/engines/block_reward.rs | 16 ++++++++-------- ethcore/src/ethereum/ethash.rs | 8 ++++---- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 7278d28cca6..2c8454c8fcc 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -1048,7 +1048,7 @@ impl Engine for AuthorityRound { /// Apply the block reward on finalisation of the block. 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 { 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 @@ -1074,12 +1074,12 @@ impl Engine for AuthorityRound { for empty_step in empty_steps { let author = empty_step.author()?; - benefactors.push((author, RewardKind::EmptyStep)); + beneficiaries.push((author, RewardKind::EmptyStep)); } } let author = *block.header().author(); - benefactors.push((author, RewardKind::Author)); + beneficiaries.push((author, RewardKind::Author)); let rewards: Vec<_> = match self.block_reward_contract { Some(ref c) if block.header().number() >= self.block_reward_contract_transition => { @@ -1110,11 +1110,11 @@ impl Engine for AuthorityRound { 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() }, _ => { - 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() }, }; diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/src/engines/block_reward.rs index 22e6b551abc..87cea535dcb 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -102,20 +102,20 @@ impl BlockRewardContract { Self::new(SystemOrCodeCallKind::Code(code, code_hash)) } - /// Calls the block reward contract with the given benefactors list (and associated reward kind) + /// 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 /// called by the system address so the `caller` must ensure that (e.g. using /// `machine.execute_as_system`). pub fn reward( &self, - benefactors: &[(Address, RewardKind)], + beneficiaries: &[(Address, RewardKind)], caller: &mut SystemOrCodeCall, ) -> Result, Error> { let reward = self.block_reward_contract.functions().reward(); let input = reward.input( - benefactors.iter().map(|&(address, _)| H160::from(address)), - benefactors.iter().map(|&(_, ref reward_kind)| u16::from(*reward_kind)), + beneficiaries.iter().map(|&(address, _)| H160::from(address)), + beneficiaries.iter().map(|&(_, ref reward_kind)| u16::from(*reward_kind)), ); let output = caller(self.kind.clone(), input) @@ -151,7 +151,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. pub fn apply_block_rewards( rewards: &[(Address, RewardKind, U256)], @@ -212,17 +212,17 @@ mod test { 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()); // the contract rewards (1000 + kind) for each benefactor - let benefactors = vec![ + let beneficiaries = vec![ ("0000000000000000000000000000000000000033".into(), RewardKind::Author), ("0000000000000000000000000000000000000034".into(), RewardKind::Uncle), ("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![ ("0000000000000000000000000000000000000033".into(), U256::from(1000)), ("0000000000000000000000000000000000000034".into(), U256::from(1000 + 1)), diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index c122a28ff5b..bfba4a7e9c6 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -244,13 +244,13 @@ impl Engine for Arc { let rewards = match self.ethash_params.block_reward_contract { Some(ref c) if number >= self.ethash_params.block_reward_contract_transition => { - let mut benefactors = Vec::new(); + let mut beneficiaries = Vec::new(); - benefactors.push((author, RewardKind::Author)); + beneficiaries.push((author, RewardKind::Author)); for u in LiveBlock::uncles(&*block) { let uncle_author = u.author(); let uncle_diff = if number > u.number() && number - u.number() <= u8::max_value().into() { (number - u.number()) as u8 } else { 0 }; - benefactors.push((*uncle_author, RewardKind::UncleWithDepth(uncle_diff))); + beneficiaries.push((*uncle_author, RewardKind::UncleWithDepth(uncle_diff))); } let mut call = |to, data| { @@ -278,7 +278,7 @@ impl Engine for Arc { 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() }, _ => { From 56a409c808096085c8904d74454c4d2056301671 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 22 Aug 2018 18:21:43 +0800 Subject: [PATCH 08/10] Fix linting --- ethcore/src/machine.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index 6012fd738b0..89a8aa49258 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -33,7 +33,6 @@ use transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, use tx_filter::TransactionFilter; use ethereum_types::{U256, H256, Address}; -use bytes::BytesRef; use rlp::Rlp; use vm::{CallType, ActionParams, ActionValue, ParamsType}; use vm::{EnvInfo, Schedule, CreateContractAddress}; From 0dd53bfea9f0cf0ab1dac271f7bceb4598171ee8 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 22 Aug 2018 18:39:26 +0800 Subject: [PATCH 09/10] Refactor duplicate system_or_code_call --- ethcore/src/engines/authority_round/mod.rs | 27 +-------------------- ethcore/src/engines/mod.rs | 28 ++++++++++++++++++++++ ethcore/src/ethereum/ethash.rs | 26 +------------------- 3 files changed, 30 insertions(+), 51 deletions(-) diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 2c8454c8fcc..8c702e7bdf2 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -1083,32 +1083,7 @@ impl Engine for AuthorityRound { 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 = match to { - SystemOrCodeCallKind::Address(address) => { - self.machine.execute_as_system( - block, - address, - U256::max_value(), - Some(data), - ) - }, - SystemOrCodeCallKind::Code(code, code_hash) => { - self.machine.execute_code_as_system( - block, - None, - Some(code), - Some(code_hash), - U256::max_value(), - Some(data), - ) - }, - }; - - result.map_err(|e| format!("{}", e)) - }; + 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() diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index f5903470f29..167df0fd2b8 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -145,6 +145,34 @@ pub enum SystemOrCodeCallKind { Code(Arc>, 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) -> Result, 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. pub type Headers<'a, H> = Fn(H256) -> Option + 'a; diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 9d8b77952a3..dc78ea0275a 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -19,7 +19,6 @@ use std::cmp; use std::collections::BTreeMap; use std::sync::Arc; use hash::{KECCAK_EMPTY_LIST_RLP}; -use engines::SystemOrCodeCallKind; use engines::block_reward::{self, BlockRewardContract, RewardKind}; use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor}; use ethereum_types::{H256, H64, U256, Address}; @@ -253,30 +252,7 @@ impl Engine for Arc { beneficiaries.push((*uncle_author, RewardKind::UncleWithDepth(uncle_diff))); } - let mut call = |to, data| { - let result = match to { - SystemOrCodeCallKind::Address(address) => { - self.machine.execute_as_system( - block, - address, - U256::max_value(), - Some(data), - ) - }, - SystemOrCodeCallKind::Code(code, code_hash) => { - self.machine.execute_code_as_system( - block, - None, - Some(code), - Some(code_hash), - U256::max_value(), - Some(data), - ) - }, - }; - - result.map_err(|e| format!("{}", e)) - }; + 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() From c494b791676d4baede4a4d6fd1d2c3dfe4dc7f00 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 22 Aug 2018 19:02:02 +0800 Subject: [PATCH 10/10] Remove old RewardKind::Uncle --- ethcore/src/engines/authority_round/mod.rs | 3 +-- ethcore/src/engines/block_reward.rs | 23 ++++++++++++---------- ethcore/src/engines/null_engine.rs | 2 +- ethcore/src/ethereum/ethash.rs | 5 ++--- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 8c702e7bdf2..d810f5a9c84 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -35,7 +35,6 @@ use ethjson; use machine::{AuxiliaryData, Call, EthereumMachine}; use hash::keccak; use header::{Header, BlockNumber, ExtendedHeader}; -use super::SystemOrCodeCallKind; use super::signer::EngineSigner; use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; use self::finality::RollingFinality; @@ -1083,7 +1082,7 @@ impl Engine for AuthorityRound { let rewards: Vec<_> = match self.block_reward_contract { Some(ref c) if block.header().number() >= self.block_reward_contract_transition => { - let mut call = engines::default_system_or_code_call(&self.machine, block); + let mut call = super::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() diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/src/engines/block_reward.rs index 87cea535dcb..9488465e548 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -27,6 +27,7 @@ use error::Error; use machine::WithRewards; use parity_machine::{Machine, WithBalances}; use trace; +use types::BlockNumber; use super::{SystemOrCodeCall, SystemOrCodeCallKind}; use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward.json"); @@ -42,11 +43,15 @@ pub enum RewardKind { EmptyStep, /// Reward attributed by an external protocol (e.g. block reward contract). External, + /// Reward attributed to the block uncle(s) with given difference. + Uncle(u8), +} - /// Reward attributed to the block uncle(s) without any reference of block difference. This is used by all except Ethash engine's reward contract. - Uncle, - /// Reward attributed to the block uncle(s) with given difference. This is used by Ethash engine's reward contract. - UncleWithDepth(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 for u16 { @@ -56,8 +61,7 @@ impl From for u16 { RewardKind::EmptyStep => 2, RewardKind::External => 3, - RewardKind::Uncle => 1, - RewardKind::UncleWithDepth(depth) => 100 + depth as u16, + RewardKind::Uncle(depth) => 100 + depth as u16, } } } @@ -66,8 +70,7 @@ impl Into for RewardKind { fn into(self) -> trace::RewardType { match self { RewardKind::Author => trace::RewardType::Block, - RewardKind::Uncle | RewardKind::UncleWithDepth(_) => - trace::RewardType::Uncle, + RewardKind::Uncle(_) => trace::RewardType::Uncle, RewardKind::EmptyStep => trace::RewardType::EmptyStep, RewardKind::External => trace::RewardType::External, } @@ -218,14 +221,14 @@ mod test { // the contract rewards (1000 + kind) for each benefactor let beneficiaries = vec![ ("0000000000000000000000000000000000000033".into(), RewardKind::Author), - ("0000000000000000000000000000000000000034".into(), RewardKind::Uncle), + ("0000000000000000000000000000000000000034".into(), RewardKind::Uncle(1)), ("0000000000000000000000000000000000000035".into(), RewardKind::EmptyStep), ]; let rewards = block_reward_contract.reward(&beneficiaries, &mut call).unwrap(); let expected = vec![ ("0000000000000000000000000000000000000033".into(), U256::from(1000)), - ("0000000000000000000000000000000000000034".into(), U256::from(1000 + 1)), + ("0000000000000000000000000000000000000034".into(), U256::from(1000 + 101)), ("0000000000000000000000000000000000000035".into(), U256::from(1000 + 2)), ]; diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index f9e698307d5..af5aedaac37 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -89,7 +89,7 @@ impl Engine for NullEngine for u in LiveBlock::uncles(&*block) { let uncle_author = u.author(); 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) diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index dc78ea0275a..6afc65a1146 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -248,8 +248,7 @@ impl Engine for Arc { beneficiaries.push((author, RewardKind::Author)); for u in LiveBlock::uncles(&*block) { let uncle_author = u.author(); - let uncle_diff = if number > u.number() && number - u.number() <= u8::max_value().into() { (number - u.number()) as u8 } else { 0 }; - beneficiaries.push((*uncle_author, RewardKind::UncleWithDepth(uncle_diff))); + beneficiaries.push((*uncle_author, RewardKind::uncle(number, u.number()))); } let mut call = engines::default_system_or_code_call(&self.machine, block); @@ -301,7 +300,7 @@ impl Engine for Arc { reward.shr(5) }; - rewards.push((*uncle_author, RewardKind::Uncle, result_uncle_reward)); + rewards.push((*uncle_author, RewardKind::uncle(number, u.number()), result_uncle_reward)); } rewards