diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 0c4906be5..d810f5a9c 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -100,7 +100,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), + 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: 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)), @@ -1043,7 +1047,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 @@ -1069,32 +1073,22 @@ 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 => { - // 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), - ); - result.map_err(|e| format!("{}", e)) - }; + let mut call = super::default_system_or_code_call(&self.machine, block); - 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 7144ed78d..9488465e5 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/src/engines/block_reward.rs @@ -21,33 +21,48 @@ 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 types::BlockNumber; +use super::{SystemOrCodeCall, SystemOrCodeCallKind}; use_contract!(block_reward_contract, "BlockReward", "res/contracts/block_reward.json"); /// 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) 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 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(depth) => 100 + depth as u16, + } } } @@ -55,7 +70,7 @@ impl Into for RewardKind { fn into(self) -> trace::RewardType { match self { RewardKind::Author => trace::RewardType::Block, - RewardKind::Uncle => trace::RewardType::Uncle, + RewardKind::Uncle(_) => trace::RewardType::Uncle, RewardKind::EmptyStep => trace::RewardType::EmptyStep, RewardKind::External => trace::RewardType::External, } @@ -63,38 +78,50 @@ impl Into for RewardKind { } /// A client for the block reward contract. +#[derive(PartialEq, Debug)] 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(), } } - /// 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>) -> 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 /// 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)], - caller: &mut SystemCall, + 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.address, input) + let output = caller(self.kind.clone(), input) .map_err(Into::into) .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. pub fn apply_block_rewards( rewards: &[(Address, RewardKind, U256)], @@ -139,7 +166,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)] @@ -149,6 +176,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 +189,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,30 +200,35 @@ 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)) }; - // 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), + ("0000000000000000000000000000000000000034".into(), RewardKind::Uncle(1)), ("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)), + ("0000000000000000000000000000000000000034".into(), U256::from(1000 + 101)), ("0000000000000000000000000000000000000035".into(), U256::from(1000 + 2)), ]; diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 8f67a039f..167df0fd2 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -133,6 +133,46 @@ 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(PartialEq, Debug, Clone)] +pub enum SystemOrCodeCallKind { + /// On-chain address. + Address(Address), + /// Hard-coded code. + 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/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index f9e698307..af5aedaac 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 16069c327..6afc65a11 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -19,7 +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 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}; use unexpected::{OutOfBounds, Mismatch}; @@ -124,6 +124,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 +158,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,52 +241,72 @@ 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 beneficiaries = 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 + beneficiaries.push((author, RewardKind::Author)); + for u in LiveBlock::uncles(&*block) { + let uncle_author = u.author(); + beneficiaries.push((*uncle_author, RewardKind::uncle(number, u.number()))); + } + + 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) } @@ -512,6 +542,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/ethcore/src/machine.rs b/ethcore/src/machine.rs index fdeed4c8e..89a8aa492 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 rlp::Rlp; use vm::{CallType, ActionParams, ActionValue, ParamsType}; use vm::{EnvInfo, Schedule, CreateContractAddress}; @@ -122,6 +122,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(); @@ -130,31 +159,27 @@ 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, }; let schedule = self.schedule(env_info.number); let mut ex = Executive::new(&mut state, &env_info, self, &schedule); let mut substate = Substate::new(); - let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer); - let output = match res { - Ok(res) => res.return_data.to_vec(), - Err(e) => { - warn!("Encountered error on making system call: {}", e); - Vec::new() - } - }; + + let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).map_err(|e| ::engines::EngineError::FailedSystemCall(format!("{}", e)))?; + let output = res.return_data.to_vec(); Ok(output) } diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index e355c6fe9..b4fcf4d78 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -16,8 +16,9 @@ //! Authority params deserialization. -use ethereum_types::Address; +use hash::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, diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index fd6b9fca5..95c5eeff3 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. @@ -48,6 +49,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")] @@ -183,7 +194,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, @@ -191,6 +202,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![ @@ -256,6 +270,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,