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:
		
							parent
							
								
									1073d56245
								
							
						
					
					
						commit
						74ce0f738e
					
				@ -100,7 +100,11 @@ impl From<ethjson::spec::AuthorityRoundParams> 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<EthereumMachine> 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<EthereumMachine> 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()
 | 
			
		||||
			},
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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<RewardKind> 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<trace::RewardType> 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<trace::RewardType> 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<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
 | 
			
		||||
	/// 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<Vec<(Address, U256)>, 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<M: Machine + WithBalances + WithRewards>(
 | 
			
		||||
	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();
 | 
			
		||||
	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)),
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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<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.
 | 
			
		||||
pub type Headers<'a, H> = Fn(H256) -> Option<H> + 'a;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -89,7 +89,7 @@ impl<M: WithBalances + WithRewards> Engine<M> for NullEngine<M>
 | 
			
		||||
		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)
 | 
			
		||||
 | 
			
		||||
@ -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<BlockRewardContract>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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),
 | 
			
		||||
			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<EthereumMachine> for Arc<Ethash> {
 | 
			
		||||
		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,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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<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> {
 | 
			
		||||
		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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -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<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.
 | 
			
		||||
	#[serde(rename="maximumUncleCountTransition")]
 | 
			
		||||
	pub maximum_uncle_count_transition: Option<Uint>,
 | 
			
		||||
 | 
			
		||||
@ -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<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.
 | 
			
		||||
	#[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,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user