diff --git a/crates/ethcore/src/engines/authority_round/mod.rs b/crates/ethcore/src/engines/authority_round/mod.rs index 076263d99..dee1a03c0 100644 --- a/crates/ethcore/src/engines/authority_round/mod.rs +++ b/crates/ethcore/src/engines/authority_round/mod.rs @@ -43,7 +43,7 @@ use engines::{ }; use error::{BlockError, Error, ErrorKind}; use ethereum_types::{Address, H256, H520, U128, U256}; -use ethjson; +use ethjson::{self, uint::Uint}; use ethkey::{self, Signature}; use hash::keccak; use io::{IoContext, IoHandler, IoService, TimerToken}; @@ -80,7 +80,7 @@ pub struct AuthorityRoundParams { /// Immediate transitions. pub immediate_transitions: bool, /// Block reward in base units. - pub block_reward: U256, + pub block_reward: BTreeMap, /// Block reward contract transition block. pub block_reward_contract_transition: u64, /// Block reward contract. @@ -113,7 +113,33 @@ impl From for AuthorityRoundParams { validate_score_transition: p.validate_score_transition.map_or(0, Into::into), validate_step_transition: p.validate_step_transition.map_or(0, Into::into), 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( + || { + let mut ret = BTreeMap::new(); + ret.insert(0, U256::zero()); + ret + }, + |reward| match reward { + ethjson::spec::BlockReward::Single(reward) => { + let mut ret = BTreeMap::new(); + ret.insert(0, reward.into()); + ret + } + ethjson::spec::BlockReward::Multi(mut multi) => { + if multi.is_empty() { + panic!("No block rewards are found in config"); + } + // add block reward from genesis and put reward to zero. + multi + .entry(Uint(U256::from(0))) + .or_insert(Uint(U256::from(0))); + multi + .into_iter() + .map(|(block, reward)| (block.into(), reward.into())) + .collect() + } + }, + ), block_reward_contract_transition: p .block_reward_contract_transition .map_or(0, Into::into), @@ -457,7 +483,7 @@ pub struct AuthorityRound { empty_steps: Mutex>, epoch_manager: Mutex, immediate_transitions: bool, - block_reward: U256, + block_reward: BTreeMap, block_reward_contract_transition: u64, block_reward_contract: Option, maximum_uncle_count_transition: u64, @@ -1374,10 +1400,11 @@ impl Engine for AuthorityRound { } let author = *block.header.author(); + let number = block.header.number(); 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 => { + Some(ref c) if number >= self.block_reward_contract_transition => { let mut call = super::default_system_or_code_call(&self.machine, block); let rewards = c.reward(&beneficiaries, &mut call)?; @@ -1386,10 +1413,18 @@ impl Engine for AuthorityRound { .map(|(author, amount)| (author, RewardKind::External, amount)) .collect() } - _ => beneficiaries - .into_iter() - .map(|(author, reward_kind)| (author, reward_kind, self.block_reward)) - .collect(), + _ => { + let (_, reward) = self.block_reward.iter() + .rev() + .find(|&(block, _)| *block <= number) + .expect("Current block's reward is not found; this indicates a chain config error; qed"); + let reward = *reward; + + beneficiaries + .into_iter() + .map(|(author, reward_kind)| (author, reward_kind, reward)) + .collect() + } }; block_reward::apply_block_rewards(&rewards, block, &self.machine) diff --git a/crates/ethcore/src/ethereum/ethash.rs b/crates/ethcore/src/ethereum/ethash.rs index f1d0853ea..be7520f2d 100644 --- a/crates/ethcore/src/ethereum/ethash.rs +++ b/crates/ethcore/src/ethereum/ethash.rs @@ -17,7 +17,7 @@ use std::{cmp, collections::BTreeMap, path::Path, sync::Arc}; use ethereum_types::{H256, H64, U256}; -use ethjson; +use ethjson::{self, uint::Uint}; use hash::KECCAK_EMPTY_LIST_RLP; use rlp::Rlp; use types::{ @@ -145,10 +145,19 @@ impl From for EthashParams { ret.insert(0, reward.into()); ret } - ethjson::spec::BlockReward::Multi(multi) => multi - .into_iter() - .map(|(block, reward)| (block.into(), reward.into())) - .collect(), + ethjson::spec::BlockReward::Multi(mut multi) => { + if multi.is_empty() { + panic!("No block rewards are found in config"); + } + // add block reward from genesis and put reward to zero. + multi + .entry(Uint(U256::from(0))) + .or_insert(Uint(U256::from(0))); + multi + .into_iter() + .map(|(block, reward)| (block.into(), reward.into())) + .collect() + } }, ), expip2_transition: p.expip2_transition.map_or(u64::max_value(), Into::into), diff --git a/crates/ethjson/src/spec/authority_round.rs b/crates/ethjson/src/spec/authority_round.rs index 15699c33a..6a76855d1 100644 --- a/crates/ethjson/src/spec/authority_round.rs +++ b/crates/ethjson/src/spec/authority_round.rs @@ -16,7 +16,7 @@ //! Authority params deserialization. -use super::ValidatorSet; +use super::{BlockReward, ValidatorSet}; use bytes::Bytes; use hash::Address; use uint::Uint; @@ -40,7 +40,7 @@ pub struct AuthorityRoundParams { /// Whether transitions should be immediate. pub immediate_transitions: Option, /// Reward per block in wei. - pub block_reward: Option, + pub block_reward: Option, /// Block at which the block reward contract should start being used. pub block_reward_contract_transition: Option, /// Block reward contract address (setting the block reward contract @@ -70,6 +70,9 @@ pub struct AuthorityRound { #[cfg(test)] mod tests { + use std::collections::BTreeMap; + + use super::BlockReward; use ethereum_types::{H160, U256}; use hash::Address; use serde_json; @@ -110,5 +113,41 @@ mod tests { deserialized.params.maximum_uncle_count, Some(Uint(5.into())) ); + assert_eq!( + deserialized.params.block_reward, + Some(BlockReward::Single(Uint(5000000.into()))) + ) + } + + #[test] + fn authority_round_deserialization_multi_block() { + let s = r#"{ + "params": { + "stepDuration": "0x02", + "validators": { + "contract" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + }, + "blockReward": { + "0": 5000000, + "100": 150 + } + } + }"#; + + let deserialized: AuthorityRound = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized.params.step_duration, Uint(U256::from(0x02))); + assert_eq!( + deserialized.params.validators, + ValidatorSet::Contract(Address(H160::from( + "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + ))) + ); + let mut rewards: BTreeMap = BTreeMap::new(); + rewards.insert(Uint(U256::from(0)), Uint(U256::from(5000000))); + rewards.insert(Uint(U256::from(100)), Uint(U256::from(150))); + assert_eq!( + deserialized.params.block_reward, + Some(BlockReward::Multi(rewards)) + ); } }