openethereum/ethcore/src/ethereum/ethash.rs
André Silva db3a98903b [stable] Backport constantinople changes (#9723)
* ethash: implement EIP-1234 (#9187)

* Implement EIP-1052 (EXTCODEHASH) and fix several issues in state account cache (#9234)

* Implement EIP-1052 and fix several issues related to account cache

* Fix jsontests

* Merge two matches together

* Avoid making unnecessary Arc<Vec>

* Address grumbles

* Comply EIP-86 with the new definition (#9140)

* Comply EIP-86 with the new CREATE2 opcode

* Fix rpc compile

* Fix interpreter CREATE/CREATE2 stack pop difference

* Add unreachable! to fix compile

* Fix instruction_info

* Fix gas check due to new stack item

* Add new tests in executive

* Fix have_create2 comment

* Remove all unused references of eip86_transition and block_number

* Implement KIP4: create2 for wasm (#9277)

* Basic implementation for kip4

* Add KIP-4 config flags

* typo: docs fix

* Fix args offset

* Add tests for create2

* tests: evm

* Update wasm-tests and fix all gas costs

* Update wasm-tests

* Update wasm-tests and fix gas costs

* `gasleft` extern implemented for WASM runtime (kip-6) (#9357)

* Wasm gasleft extern added

* wasm_gasleft_activation_transition -> kip4_transition

* use kip-6 switch

* gasleft_panic -> gasleft_fail rename

* call_msg_gasleft test added and gas_left agustments because this https://github.com/paritytech/wasm-tests/pull/52

* change .. to _

* fix comment for the have_gasleft param

* update tests (0edbf860ff)

* Add EIP-1014 transition config flag (#9268)

* Add EIP-1014 transition config flag

* Remove EIP-86 configs

* Change CREATE2 opcode index to 0xf5

* Move salt to the last item in the stack

* Change sendersaltandaddress scheme to comply with current EIP-1014

* Fix json configs

* Fix create2 test

* Fix deprecated comments

* EIP 1283: Net gas metering for SSTORE without dirty maps (#9319)

* Implement last_checkpoint_storage_at

* Add reverted_storage_at for externalities

* sstore_clears_count -> sstore_clears_refund

* Implement eip1283 for evm

* Add eip1283Transition params

* evm: fix tests

* jsontests: fix test

* Return checkpoint index when creating

* Comply with spec Version II

* Fix docs

* Fix jsontests feature compile

* Address grumbles

* Fix no-checkpoint-entry case

* Remove unnecessary expect

* Add test for State::checkpoint_storage_at

* Add executive level test for eip1283

* Hard-code transaction_checkpoint_index to 0

* Fix jsontests

* Add tests for checkpoint discard/revert

* Require checkpoint to be empty for kill_account and commit

* Get code coverage

* Use saturating_add/saturating_sub

* Fix issues in insert_cache

* Clear the state again

* Fix original_storage_at

* Early return for empty RLP trie storage

* Update comments

* Fix borrow_mut issue

* Simplify checkpoint_storage_at if branches

* Better commenting for gas handling code

* Address naming grumbles

* More tests

* Fix an issue in overwrite_with

* Add another test

* Fix comment

* Remove unnecessary bracket

* Move orig to inner if

* Remove test coverage for this PR

* Add tests for executive original value

* Add warn! for an unreachable cause

* Update state tests execution model (#9440)

* Update & fix JSON state tests.

* Update tests to be able to run ethtest at
021fe3d410773024cd5f0387e62db6e6ec800f32.

- Touch user in state
- Adjust transaction tests to new json format

* Switch to same commit for submodule ethereum/test as geth (next includes constantinople changes).
Added test `json_tests::trie::generic::TrieTests_trieanyorder` and a few
difficulty tests.

* Remove trietestnextprev as it would require to parse differently and
implement it.

* Support new (shitty) format of transaction tests.

* Ignore junk in ethereum/tests repo.

* Ignore incorrect test.

* Update to a later commit

* Move block number to a constant.

* Fix ZK2 test - touched account should also be cleared.

* Fix conflict resolution

* Fix checkpointing when creating contract failed (#9514)

* In create memory calculation is the same for create2 because the additional parameter was popped before. (#9522)

* Enable all Constantinople hard fork changes in constantinople_test.json (#9505)

* Enable all Constantinople hard fork changes in constantinople_test.json

* Address grumbles

* Remove EIP-210 activation

* 8m -> 5m

* Temporarily add back eip210 transition so we can get test passed

* Add eip210_test and remove eip210 transition from const_test

* Add constantinople conf to EvmTestClient. (#9570)

* Add constantinople conf to EvmTestClient.

* Skip some test to update submodule etheureum/tests submodule to latest.

* Put skipping 'under issue' test behind a feature.

* Change blockReward for const-test to pass ethereum/tests

* Update tests to new constantinple definition (change of reward at block
5).
Switch 'reference' to string, that way we can include issues from others
repo (more flexible)Update tests to new constantinple definition (change
of reward at block 5).
Switch 'reference' to string, that way we can include issues from others
repo (more flexible).

* Fix modexp and bn128_mul gas prices in chain config

* Changes `run_test_path` method to append its directory results (without
that it stop testing at the first file failure).
Add some missing tests.
Add skip for those (block create2 is one hundred percent false but on
hive we can see that geth and aleth got similar issue for this item).

* retab current.json

* Update reference to parity issue for failing tests.

* Hardfork the testnets (#9562)

* ethcore: propose hardfork block number 4230000 for ropsten

* ethcore: propose hardfork block number 9000000 for kovan

* ethcore: enable kip-4 and kip-6 on kovan

* etcore: bump kovan hardfork to block 9.2M

* ethcore: fix ropsten constantinople block number to 4.2M

* ethcore: disable difficulty_test_ropsten until ethereum/tests are updated upstream

* Don't hash the init_code of CREATE. (#9688)

* Implement CREATE2 gas changes and fix some potential overflowing (#9694)

* Implement CREATE2 gas changes and fix some potential overflowing

* Ignore create2 state tests

* Split CREATE and CREATE2 in gasometer

* Generalize rounding (x + 31) / 32 to to_word_size

* ethcore: delay ropsten hardfork (#9704)

* Add hardcoded headers (#9730)

* add foundation hardcoded header #6486017

* add ropsten hardcoded headers #4202497

* add kovan hardcoded headers #9023489

* gitlab ci: releasable_branches: change variables condition to schedule (#9729)

* HF in POA Core (2018-10-22) (#9724)

https://github.com/poanetwork/poa-chain-spec/pull/87
2018-10-10 18:55:52 +02:00

923 lines
34 KiB
Rust

// Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::path::Path;
use std::cmp;
use std::collections::BTreeMap;
use std::sync::Arc;
use hash::{KECCAK_EMPTY_LIST_RLP};
use engines::block_reward::{self, RewardKind};
use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor};
use ethereum_types::{H256, H64, U256, Address};
use unexpected::{OutOfBounds, Mismatch};
use block::*;
use error::{BlockError, Error};
use header::{Header, BlockNumber, ExtendedHeader};
use engines::{self, Engine};
use ethjson;
use rlp::Rlp;
use machine::EthereumMachine;
/// Number of blocks in an ethash snapshot.
// make dependent on difficulty incrment divisor?
const SNAPSHOT_BLOCKS: u64 = 5000;
/// Maximum number of blocks allowed in an ethash snapshot.
const MAX_SNAPSHOT_BLOCKS: u64 = 30000;
/// Default number of blocks the difficulty bomb is delayed in EIP-{649,1234}
const DEFAULT_EIP649_DELAY: u64 = 3_000_000;
const DEFAULT_EIP1234_DELAY: u64 = 2_000_000;
/// Ethash specific seal
#[derive(Debug, PartialEq)]
pub struct Seal {
/// Ethash seal mix_hash
pub mix_hash: H256,
/// Ethash seal nonce
pub nonce: H64,
}
impl Seal {
/// Tries to parse rlp as ethash seal.
pub fn parse_seal<T: AsRef<[u8]>>(seal: &[T]) -> Result<Self, Error> {
if seal.len() != 2 {
return Err(BlockError::InvalidSealArity(
Mismatch {
expected: 2,
found: seal.len()
}
).into());
}
let mix_hash = Rlp::new(seal[0].as_ref()).as_val::<H256>()?;
let nonce = Rlp::new(seal[1].as_ref()).as_val::<H64>()?;
let seal = Seal {
mix_hash,
nonce,
};
Ok(seal)
}
}
/// Ethash params.
#[derive(Debug, PartialEq)]
pub struct EthashParams {
/// Minimum difficulty.
pub minimum_difficulty: U256,
/// Difficulty bound divisor.
pub difficulty_bound_divisor: U256,
/// Difficulty increment divisor.
pub difficulty_increment_divisor: u64,
/// Metropolis difficulty increment divisor.
pub metropolis_difficulty_increment_divisor: u64,
/// Block duration.
pub duration_limit: u64,
/// Homestead transition block number.
pub homestead_transition: u64,
/// Transition block for a change of difficulty params (currently just bound_divisor).
pub difficulty_hardfork_transition: u64,
/// Difficulty param after the difficulty transition.
pub difficulty_hardfork_bound_divisor: U256,
/// Block on which there is no additional difficulty from the exponential bomb.
pub bomb_defuse_transition: u64,
/// Number of first block where EIP-100 rules begin.
pub eip100b_transition: u64,
/// Number of first block where ECIP-1010 begins.
pub ecip1010_pause_transition: u64,
/// Number of first block where ECIP-1010 ends.
pub ecip1010_continue_transition: u64,
/// Total block number for one ECIP-1017 era.
pub ecip1017_era_rounds: u64,
/// Number of first block where MCIP-3 begins.
pub mcip3_transition: u64,
/// MCIP-3 Block reward coin-base for miners.
pub mcip3_miner_reward: U256,
/// MCIP-3 Block reward ubi-base for basic income.
pub mcip3_ubi_reward: U256,
/// MCIP-3 contract address for universal basic income.
pub mcip3_ubi_contract: Address,
/// MCIP-3 Block reward dev-base for dev funds.
pub mcip3_dev_reward: U256,
/// MCIP-3 contract address for the developer funds.
pub mcip3_dev_contract: Address,
/// Block reward in base units.
pub block_reward: U256,
/// EIP-649 transition block.
pub eip649_transition: u64,
/// EIP-649 bomb delay.
pub eip649_delay: u64,
/// EIP-649 base reward.
pub eip649_reward: Option<U256>,
/// EIP-1234 transition block.
pub eip1234_transition: u64,
/// EIP-1234 bomb delay.
pub eip1234_delay: u64,
/// EIP-1234 base reward.
pub eip1234_reward: Option<U256>,
/// EXPIP-2 block height
pub expip2_transition: u64,
/// EXPIP-2 duration limit
pub expip2_duration_limit: u64,
}
impl From<ethjson::spec::EthashParams> for EthashParams {
fn from(p: ethjson::spec::EthashParams) -> Self {
EthashParams {
minimum_difficulty: p.minimum_difficulty.into(),
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
difficulty_increment_divisor: p.difficulty_increment_divisor.map_or(10, Into::into),
metropolis_difficulty_increment_divisor: p.metropolis_difficulty_increment_divisor.map_or(9, Into::into),
duration_limit: p.duration_limit.map_or(0, Into::into),
homestead_transition: p.homestead_transition.map_or(0, Into::into),
difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(u64::max_value(), Into::into),
difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into),
bomb_defuse_transition: p.bomb_defuse_transition.map_or(u64::max_value(), Into::into),
eip100b_transition: p.eip100b_transition.map_or(u64::max_value(), Into::into),
ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into),
ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into),
ecip1017_era_rounds: p.ecip1017_era_rounds.map_or(u64::max_value(), Into::into),
mcip3_transition: p.mcip3_transition.map_or(u64::max_value(), Into::into),
mcip3_miner_reward: p.mcip3_miner_reward.map_or_else(Default::default, Into::into),
mcip3_ubi_reward: p.mcip3_ubi_reward.map_or(U256::from(0), Into::into),
mcip3_ubi_contract: p.mcip3_ubi_contract.map_or_else(Address::new, Into::into),
mcip3_dev_reward: p.mcip3_dev_reward.map_or(U256::from(0), Into::into),
mcip3_dev_contract: p.mcip3_dev_contract.map_or_else(Address::new, Into::into),
block_reward: p.block_reward.map_or_else(Default::default, Into::into),
eip649_transition: p.eip649_transition.map_or(u64::max_value(), Into::into),
eip649_delay: p.eip649_delay.map_or(DEFAULT_EIP649_DELAY, Into::into),
eip649_reward: p.eip649_reward.map(Into::into),
eip1234_transition: p.eip1234_transition.map_or(u64::max_value(), Into::into),
eip1234_delay: p.eip1234_delay.map_or(DEFAULT_EIP1234_DELAY, Into::into),
eip1234_reward: p.eip1234_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),
}
}
}
/// Engine using Ethash proof-of-work consensus algorithm, suitable for Ethereum
/// mainnet chains in the Olympic, Frontier and Homestead eras.
pub struct Ethash {
ethash_params: EthashParams,
pow: EthashManager,
machine: EthereumMachine,
}
impl Ethash {
/// Create a new instance of Ethash engine
pub fn new<T: Into<Option<OptimizeFor>>>(
cache_dir: &Path,
ethash_params: EthashParams,
machine: EthereumMachine,
optimize_for: T,
) -> Arc<Self> {
Arc::new(Ethash {
ethash_params,
machine,
pow: EthashManager::new(cache_dir.as_ref(), optimize_for.into()),
})
}
}
// TODO [rphmeier]
//
// for now, this is different than Ethash's own epochs, and signal
// "consensus epochs".
// in this sense, `Ethash` is epochless: the same `EpochVerifier` can be used
// for any block in the chain.
// in the future, we might move the Ethash epoch
// caching onto this mechanism as well.
impl engines::EpochVerifier<EthereumMachine> for Arc<Ethash> {
fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) }
fn verify_heavy(&self, header: &Header) -> Result<(), Error> {
self.verify_block_unordered(header)
}
}
impl Engine<EthereumMachine> for Arc<Ethash> {
fn name(&self) -> &str { "Ethash" }
fn machine(&self) -> &EthereumMachine { &self.machine }
// Two fields - nonce and mix.
fn seal_fields(&self, _header: &Header) -> usize { 2 }
/// Additional engine-specific information for the user/developer concerning `header`.
fn extra_info(&self, header: &Header) -> BTreeMap<String, String> {
match Seal::parse_seal(header.seal()) {
Ok(seal) => map![
"nonce".to_owned() => format!("0x{:x}", seal.nonce),
"mixHash".to_owned() => format!("0x{:x}", seal.mix_hash)
],
_ => BTreeMap::default()
}
}
fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 }
fn populate_from_parent(&self, header: &mut Header, parent: &Header) {
let difficulty = self.calculate_difficulty(header, parent);
header.set_difficulty(difficulty);
}
/// Apply the block reward on finalisation of the block.
/// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current).
fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> {
use std::ops::Shr;
use parity_machine::LiveBlock;
let author = *LiveBlock::header(&*block).author();
let number = LiveBlock::header(&*block).number();
let mut rewards = Vec::new();
// Applies EIP-{649,1234} reward, defaults to block_reward.
let reward = if number >= self.ethash_params.eip1234_transition {
self.ethash_params.eip1234_reward.unwrap_or(self.ethash_params.block_reward)
} else 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, result_uncle_reward));
}
block_reward::apply_block_rewards(&rewards, block, &self.machine)
}
#[cfg(not(feature = "miner-debug"))]
fn verify_local_seal(&self, header: &Header) -> Result<(), Error> {
self.verify_block_basic(header)
.and_then(|_| self.verify_block_unordered(header))
}
#[cfg(feature = "miner-debug")]
fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> {
warn!("Skipping seal verification, running in miner testing mode.");
Ok(())
}
fn verify_block_basic(&self, header: &Header) -> Result<(), Error> {
// check the seal fields.
let seal = Seal::parse_seal(header.seal())?;
// TODO: consider removing these lines.
let min_difficulty = self.ethash_params.minimum_difficulty;
if header.difficulty() < &min_difficulty {
return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() })))
}
let difficulty = ethash::boundary_to_difficulty(&H256(quick_get_difficulty(
&header.bare_hash().0,
seal.nonce.low_u64(),
&seal.mix_hash.0
)));
if &difficulty < header.difficulty() {
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty })));
}
if header.gas_limit() > &0x7fffffffffffffffu64.into() {
return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(0x7fffffffffffffffu64.into()), found: header.gas_limit().clone() })));
}
Ok(())
}
fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> {
let seal = Seal::parse_seal(header.seal())?;
let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, seal.nonce.low_u64());
let mix = H256(result.mix_hash);
let difficulty = ethash::boundary_to_difficulty(&H256(result.value));
trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}",
num = header.number() as u64,
seed = H256(slow_hash_block_number(header.number() as u64)),
h = header.bare_hash(),
non = seal.nonce.low_u64(),
mix = H256(result.mix_hash),
res = H256(result.value));
if mix != seal.mix_hash {
return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: seal.mix_hash })));
}
if &difficulty < header.difficulty() {
return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty })));
}
Ok(())
}
fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> {
// we should not calculate difficulty for genesis blocks
if header.number() == 0 {
return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() })));
}
// Check difficulty is correct given the two timestamps.
let expected_difficulty = self.calculate_difficulty(header, parent);
if header.difficulty() != &expected_difficulty {
return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() })))
}
Ok(())
}
fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> engines::ConstructedVerifier<'a, EthereumMachine> {
engines::ConstructedVerifier::Trusted(Box::new(self.clone()))
}
fn snapshot_components(&self) -> Option<Box<::snapshot::SnapshotComponents>> {
Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS)))
}
fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> engines::ForkChoice {
engines::total_difficulty_fork_choice(new, current)
}
}
impl Ethash {
fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 {
const EXP_DIFF_PERIOD: u64 = 100_000;
if header.number() == 0 {
panic!("Can't calculate genesis block difficulty");
}
let parent_has_uncles = parent.uncles_hash() != &KECCAK_EMPTY_LIST_RLP;
let min_difficulty = self.ethash_params.minimum_difficulty;
let difficulty_hardfork = header.number() >= self.ethash_params.difficulty_hardfork_transition;
let difficulty_bound_divisor = if difficulty_hardfork {
self.ethash_params.difficulty_hardfork_bound_divisor
} else {
self.ethash_params.difficulty_bound_divisor
};
let expip2_hardfork = header.number() >= self.ethash_params.expip2_transition;
let duration_limit = if expip2_hardfork {
self.ethash_params.expip2_duration_limit
} else {
self.ethash_params.duration_limit
};
let frontier_limit = self.ethash_params.homestead_transition;
let mut target = if header.number() < frontier_limit {
if header.timestamp() >= parent.timestamp() + duration_limit {
*parent.difficulty() - (*parent.difficulty() / difficulty_bound_divisor)
} else {
*parent.difficulty() + (*parent.difficulty() / difficulty_bound_divisor)
}
} else {
trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty(), header.timestamp(), parent.timestamp());
//block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)
let (increment_divisor, threshold) = if header.number() < self.ethash_params.eip100b_transition {
(self.ethash_params.difficulty_increment_divisor, 1)
} else if parent_has_uncles {
(self.ethash_params.metropolis_difficulty_increment_divisor, 2)
} else {
(self.ethash_params.metropolis_difficulty_increment_divisor, 1)
};
let diff_inc = (header.timestamp() - parent.timestamp()) / increment_divisor;
if diff_inc <= threshold {
*parent.difficulty() + *parent.difficulty() / difficulty_bound_divisor * U256::from(threshold - diff_inc)
} else {
let multiplier: U256 = cmp::min(diff_inc - threshold, 99).into();
parent.difficulty().saturating_sub(
*parent.difficulty() / difficulty_bound_divisor * multiplier
)
}
};
target = cmp::max(min_difficulty, target);
if header.number() < self.ethash_params.bomb_defuse_transition {
if header.number() < self.ethash_params.ecip1010_pause_transition {
let mut number = header.number();
if number >= self.ethash_params.eip1234_transition {
number = number.saturating_sub(self.ethash_params.eip1234_delay);
}
if number >= self.ethash_params.eip649_transition {
number = number.saturating_sub(self.ethash_params.eip649_delay);
}
let period = (number / EXP_DIFF_PERIOD) as usize;
if period > 1 {
target = cmp::max(min_difficulty, target + (U256::from(1) << (period - 2)));
}
}
else if header.number() < self.ethash_params.ecip1010_continue_transition {
let fixed_difficulty = ((self.ethash_params.ecip1010_pause_transition / EXP_DIFF_PERIOD) - 2) as usize;
target = cmp::max(min_difficulty, target + (U256::from(1) << fixed_difficulty));
}
else {
let period = ((parent.number() + 1) / EXP_DIFF_PERIOD) as usize;
let delay = ((self.ethash_params.ecip1010_continue_transition - self.ethash_params.ecip1010_pause_transition) / EXP_DIFF_PERIOD) as usize;
target = cmp::max(min_difficulty, target + (U256::from(1) << (period - delay - 2)));
}
}
target
}
}
fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u64) -> (u64, U256) {
let eras = if block_number != 0 && block_number % era_rounds == 0 {
block_number / era_rounds - 1
} else {
block_number / era_rounds
};
let mut divi = U256::from(1);
for _ in 0..eras {
reward = reward * U256::from(4);
divi = divi * U256::from(5);
}
reward = reward / divi;
(eras, reward)
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use std::sync::Arc;
use ethereum_types::{H64, H256, U256, Address};
use block::*;
use test_helpers::get_temp_state_db;
use error::{BlockError, Error, ErrorKind};
use header::Header;
use spec::Spec;
use engines::Engine;
use super::super::{new_morden, new_mcip3_test, new_homestead_test_machine};
use super::{Ethash, EthashParams, ecip1017_eras_block_reward};
use rlp;
use tempdir::TempDir;
fn test_spec() -> Spec {
let tempdir = TempDir::new("").unwrap();
new_morden(&tempdir.path())
}
fn get_default_ethash_params() -> EthashParams {
EthashParams {
minimum_difficulty: U256::from(131072),
difficulty_bound_divisor: U256::from(2048),
difficulty_increment_divisor: 10,
metropolis_difficulty_increment_divisor: 9,
homestead_transition: 1150000,
duration_limit: 13,
block_reward: 0.into(),
difficulty_hardfork_transition: u64::max_value(),
difficulty_hardfork_bound_divisor: U256::from(0),
bomb_defuse_transition: u64::max_value(),
eip100b_transition: u64::max_value(),
ecip1010_pause_transition: u64::max_value(),
ecip1010_continue_transition: u64::max_value(),
ecip1017_era_rounds: u64::max_value(),
mcip3_transition: u64::max_value(),
mcip3_miner_reward: 0.into(),
mcip3_ubi_reward: 0.into(),
mcip3_ubi_contract: "0000000000000000000000000000000000000001".into(),
mcip3_dev_reward: 0.into(),
mcip3_dev_contract: "0000000000000000000000000000000000000001".into(),
eip649_transition: u64::max_value(),
eip649_delay: 3_000_000,
eip649_reward: None,
eip1234_transition: u64::max_value(),
eip1234_delay: 2_000_000,
eip1234_reward: None,
expip2_transition: u64::max_value(),
expip2_duration_limit: 30,
}
}
#[test]
fn on_close_block() {
let spec = test_spec();
let engine = &*spec.engine;
let genesis_header = spec.genesis_header();
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()).unwrap(), U256::from_str("4563918244f40000").unwrap());
}
#[test]
fn has_valid_ecip1017_eras_block_reward() {
let eras_rounds = 5000000;
let start_reward: U256 = "4563918244F40000".parse().unwrap();
let block_number = 0;
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number);
assert_eq!(0, eras);
assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward);
let block_number = 5000000;
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number);
assert_eq!(0, eras);
assert_eq!(U256::from_str("4563918244F40000").unwrap(), reward);
let block_number = 10000000;
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number);
assert_eq!(1, eras);
assert_eq!(U256::from_str("3782DACE9D900000").unwrap(), reward);
let block_number = 20000000;
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number);
assert_eq!(3, eras);
assert_eq!(U256::from_str("2386F26FC1000000").unwrap(), reward);
let block_number = 80000000;
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number);
assert_eq!(15, eras);
assert_eq!(U256::from_str("271000000000000").unwrap(), reward);
let block_number = 250000000;
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number);
assert_eq!(49, eras);
assert_eq!(U256::from_str("51212FFBAF0A").unwrap(), reward);
}
#[test]
fn on_close_block_with_uncle() {
let spec = test_spec();
let engine = &*spec.engine;
let genesis_header = spec.genesis_header();
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
let mut uncle = Header::new();
let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into();
uncle.set_author(uncle_author);
b.push_uncle(uncle).unwrap();
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()).unwrap(), "478eae0e571ba000".into());
assert_eq!(b.state().balance(&uncle_author).unwrap(), "3cb71f51fc558000".into());
}
#[test]
fn has_valid_mcip3_era_block_rewards() {
let spec = new_mcip3_test();
let engine = &*spec.engine;
let genesis_header = spec.genesis_header();
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]);
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false, &mut Vec::new().into_iter()).unwrap();
let b = b.close();
let ubi_contract: Address = "00efdd5883ec628983e9063c7d969fe268bbf310".into();
let dev_contract: Address = "00756cf8159095948496617f5fb17ed95059f536".into();
assert_eq!(b.state().balance(&Address::zero()).unwrap(), U256::from_str("d8d726b7177a80000").unwrap());
assert_eq!(b.state().balance(&ubi_contract).unwrap(), U256::from_str("2b5e3af16b1880000").unwrap());
assert_eq!(b.state().balance(&dev_contract).unwrap(), U256::from_str("c249fdd327780000").unwrap());
}
#[test]
fn has_valid_metadata() {
let engine = test_spec().engine;
assert!(!engine.name().is_empty());
}
#[test]
fn can_return_schedule() {
let engine = test_spec().engine;
let schedule = engine.schedule(10000000);
assert!(schedule.stack_limit > 0);
let schedule = engine.schedule(100);
assert!(!schedule.have_delegate_call);
}
#[test]
fn can_do_seal_verification_fail() {
let engine = test_spec().engine;
let header: Header = Header::default();
let verify_result = engine.verify_block_basic(&header);
match verify_result {
Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {},
Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_difficulty_verification_fail() {
let engine = test_spec().engine;
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]);
let verify_result = engine.verify_block_basic(&header);
match verify_result {
Err(Error(ErrorKind::Block(BlockError::DifficultyOutOfBounds(_)), _)) => {},
Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_proof_of_work_verification_fail() {
let engine = test_spec().engine;
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]);
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
let verify_result = engine.verify_block_basic(&header);
match verify_result {
Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {},
Err(_) => { panic!("should be invalid proof of work error (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_seal_unordered_verification_fail() {
let engine = test_spec().engine;
let header = Header::default();
let verify_result = engine.verify_block_unordered(&header);
match verify_result {
Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {},
Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_seal_unordered_verification_fail2() {
let engine = test_spec().engine;
let mut header = Header::default();
header.set_seal(vec![vec![], vec![]]);
let verify_result = engine.verify_block_unordered(&header);
// rlp error, shouldn't panic
assert!(verify_result.is_err());
}
#[test]
fn can_do_seal256_verification_fail() {
let engine = test_spec().engine;
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::zero()).into_vec(), rlp::encode(&H64::zero()).into_vec()]);
let verify_result = engine.verify_block_unordered(&header);
match verify_result {
Err(Error(ErrorKind::Block(BlockError::MismatchedH256SealElement(_)), _)) => {},
Err(_) => { panic!("should be invalid 256-bit seal fail (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_do_proof_of_work_unordered_verification_fail() {
let engine = test_spec().engine;
let mut header: Header = Header::default();
header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).into_vec(), rlp::encode(&H64::zero()).into_vec()]);
header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap());
let verify_result = engine.verify_block_unordered(&header);
match verify_result {
Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {},
Err(_) => { panic!("should be invalid proof-of-work fail (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_verify_block_family_genesis_fail() {
let engine = test_spec().engine;
let header: Header = Header::default();
let parent_header: Header = Header::default();
let verify_result = engine.verify_block_family(&header, &parent_header);
match verify_result {
Err(Error(ErrorKind::Block(BlockError::RidiculousNumber(_)), _)) => {},
Err(_) => { panic!("should be invalid block number fail (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn can_verify_block_family_difficulty_fail() {
let engine = test_spec().engine;
let mut header: Header = Header::default();
header.set_number(2);
let mut parent_header: Header = Header::default();
parent_header.set_number(1);
let verify_result = engine.verify_block_family(&header, &parent_header);
match verify_result {
Err(Error(ErrorKind::Block(BlockError::InvalidDifficulty(_)), _)) => {},
Err(_) => { panic!("should be invalid difficulty fail (got {:?})", verify_result); },
_ => { panic!("Should be error, got Ok"); },
}
}
#[test]
fn difficulty_frontier() {
let machine = new_homestead_test_machine();
let ethparams = get_default_ethash_params();
let tempdir = TempDir::new("").unwrap();
let ethash = Ethash::new(tempdir.path(), ethparams, machine, None);
let mut parent_header = Header::default();
parent_header.set_number(1000000);
parent_header.set_difficulty(U256::from_str("b69de81a22b").unwrap());
parent_header.set_timestamp(1455404053);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(1455404058);
let difficulty = ethash.calculate_difficulty(&header, &parent_header);
assert_eq!(U256::from_str("b6b4bbd735f").unwrap(), difficulty);
}
#[test]
fn difficulty_homestead() {
let machine = new_homestead_test_machine();
let ethparams = get_default_ethash_params();
let tempdir = TempDir::new("").unwrap();
let ethash = Ethash::new(tempdir.path(), ethparams, machine, None);
let mut parent_header = Header::default();
parent_header.set_number(1500000);
parent_header.set_difficulty(U256::from_str("1fd0fd70792b").unwrap());
parent_header.set_timestamp(1463003133);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(1463003177);
let difficulty = ethash.calculate_difficulty(&header, &parent_header);
assert_eq!(U256::from_str("1fc50f118efe").unwrap(), difficulty);
}
#[test]
fn difficulty_classic_bomb_delay() {
let machine = new_homestead_test_machine();
let ethparams = EthashParams {
ecip1010_pause_transition: 3000000,
..get_default_ethash_params()
};
let tempdir = TempDir::new("").unwrap();
let ethash = Ethash::new(tempdir.path(), ethparams, machine, None);
let mut parent_header = Header::default();
parent_header.set_number(3500000);
parent_header.set_difficulty(U256::from_str("6F62EAF8D3C").unwrap());
parent_header.set_timestamp(1452838500);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 20);
assert_eq!(
U256::from_str("6F55FE9B74B").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
header.set_timestamp(parent_header.timestamp() + 5);
assert_eq!(
U256::from_str("6F71D75632D").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
header.set_timestamp(parent_header.timestamp() + 80);
assert_eq!(
U256::from_str("6F02746B3A5").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
}
#[test]
fn test_difficulty_bomb_continue() {
let machine = new_homestead_test_machine();
let ethparams = EthashParams {
ecip1010_pause_transition: 3000000,
ecip1010_continue_transition: 5000000,
..get_default_ethash_params()
};
let tempdir = TempDir::new("").unwrap();
let ethash = Ethash::new(tempdir.path(), ethparams, machine, None);
let mut parent_header = Header::default();
parent_header.set_number(5000102);
parent_header.set_difficulty(U256::from_str("14944397EE8B").unwrap());
parent_header.set_timestamp(1513175023);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 6);
assert_eq!(
U256::from_str("1496E6206188").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
parent_header.set_number(5100123);
parent_header.set_difficulty(U256::from_str("14D24B39C7CF").unwrap());
parent_header.set_timestamp(1514609324);
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 41);
assert_eq!(
U256::from_str("14CA9C5D9227").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
parent_header.set_number(6150001);
parent_header.set_difficulty(U256::from_str("305367B57227").unwrap());
parent_header.set_timestamp(1529664575);
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 105);
assert_eq!(
U256::from_str("309D09E0C609").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
parent_header.set_number(8000000);
parent_header.set_difficulty(U256::from_str("1180B36D4CE5B6A").unwrap());
parent_header.set_timestamp(1535431724);
header.set_number(parent_header.number() + 1);
header.set_timestamp(parent_header.timestamp() + 420);
assert_eq!(
U256::from_str("5126FFD5BCBB9E7").unwrap(),
ethash.calculate_difficulty(&header, &parent_header)
);
}
#[test]
fn difficulty_max_timestamp() {
let machine = new_homestead_test_machine();
let ethparams = get_default_ethash_params();
let tempdir = TempDir::new("").unwrap();
let ethash = Ethash::new(tempdir.path(), ethparams, machine, None);
let mut parent_header = Header::default();
parent_header.set_number(1000000);
parent_header.set_difficulty(U256::from_str("b69de81a22b").unwrap());
parent_header.set_timestamp(1455404053);
let mut header = Header::default();
header.set_number(parent_header.number() + 1);
header.set_timestamp(u64::max_value());
let difficulty = ethash.calculate_difficulty(&header, &parent_header);
assert_eq!(U256::from(12543204905719u64), difficulty);
}
#[test]
fn test_extra_info() {
let machine = new_homestead_test_machine();
let ethparams = get_default_ethash_params();
let tempdir = TempDir::new("").unwrap();
let ethash = Ethash::new(tempdir.path(), ethparams, machine, None);
let mut header = Header::default();
header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")).into_vec(), rlp::encode(&H64::zero()).into_vec()]);
let info = ethash.extra_info(&header);
assert_eq!(info["nonce"], "0x0000000000000000");
assert_eq!(info["mixHash"], "0xb251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d");
}
}