diff --git a/core/sr-eth-primitives/src/header.rs b/core/sr-eth-primitives/src/header.rs new file mode 100644 index 000000000..e236f2cba --- /dev/null +++ b/core/sr-eth-primitives/src/header.rs @@ -0,0 +1,282 @@ +use super::*; +use ethbloom::Bloom; +use pow::EthashSeal; +use rlp::RlpStream; + +#[derive(PartialEq, Eq, Clone, Encode, Decode, Copy)] +enum Seal { + /// The seal/signature is included. + With, + /// The seal/signature is not included. + Without, +} + +#[derive(Default, PartialEq, Eq, Clone, Encode, Decode)] +pub struct EthHeader { + parent_hash: H256, + timestamp: u64, + number: BlockNumber, + author: Address, + transactions_root: H256, + uncles_hash: H256, + extra_data: Bytes, + state_root: H256, + receipts_root: H256, + log_bloom: Bloom, + gas_used: U256, + gas_limit: U256, + difficulty: U256, + seal: Vec, + hash: Option, +} + +/// Alter value of given field, reset memoised hash if changed. +fn change_field(hash: &mut Option, field: &mut T, value: T) +where + T: PartialEq, +{ + if field != &value { + *field = value; + *hash = None; + } +} + +impl EthHeader { + /// Create a new, default-valued, header. + pub fn new() -> Self { + Self::default() + } + + /// Get the parent_hash field of the header. + pub fn parent_hash(&self) -> &H256 { + &self.parent_hash + } + + /// Get the timestamp field of the header. + pub fn timestamp(&self) -> u64 { + self.timestamp + } + + /// Get the number field of the header. + pub fn number(&self) -> BlockNumber { + self.number + } + + /// Get the author field of the header. + pub fn author(&self) -> &Address { + &self.author + } + + /// Get the extra data field of the header. + pub fn extra_data(&self) -> &Bytes { + &self.extra_data + } + + /// Get the state root field of the header. + pub fn state_root(&self) -> &H256 { + &self.state_root + } + + /// Get the receipts root field of the header. + pub fn receipts_root(&self) -> &H256 { + &self.receipts_root + } + + /// Get the log bloom field of the header. + pub fn log_bloom(&self) -> &Bloom { + &self.log_bloom + } + + /// Get the transactions root field of the header. + pub fn transactions_root(&self) -> &H256 { + &self.transactions_root + } + + /// Get the uncles hash field of the header. + pub fn uncles_hash(&self) -> &H256 { + &self.uncles_hash + } + + /// Get the gas used field of the header. + pub fn gas_used(&self) -> &U256 { + &self.gas_used + } + + /// Get the gas limit field of the header. + pub fn gas_limit(&self) -> &U256 { + &self.gas_limit + } + + /// Get the difficulty field of the header. + pub fn difficulty(&self) -> &U256 { + &self.difficulty + } + + /// Get the seal field of the header. + pub fn seal(&self) -> &[Bytes] { + &self.seal + } + + /// Set the seal field of the header. + pub fn set_seal(&mut self, a: Vec) { + change_field(&mut self.hash, &mut self.seal, a) + } + + /// Set the difficulty field of the header. + pub fn set_difficulty(&mut self, a: U256) { + change_field(&mut self.hash, &mut self.difficulty, a); + } + + /// Get & memoize the hash of this header (keccak of the RLP with seal). + pub fn compute_hash(&mut self) -> H256 { + let hash = self.hash(); + self.hash = Some(hash); + hash + } + + /// Get the hash of this header (keccak of the RLP with seal). + pub fn hash(&self) -> H256 { + self.hash.unwrap_or_else(|| keccak_hash::keccak(self.rlp(Seal::With))) + } + + /// Get the hash of the header excluding the seal + pub fn bare_hash(&self) -> H256 { + keccak_hash::keccak(self.rlp(Seal::Without)) + } + + /// Encode the header, getting a type-safe wrapper around the RLP. + pub fn encoded(&self) -> encoded::Header { + encoded::Header::new(self.rlp(Seal::With)) + } + + /// Get the RLP representation of this Header. + fn rlp(&self, with_seal: Seal) -> Bytes { + let mut s = RlpStream::new(); + self.stream_rlp(&mut s, with_seal); + s.out() + } + + /// Place this header into an RLP stream `s`, optionally `with_seal`. + fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { + if let Seal::With = with_seal { + s.begin_list(13 + self.seal.len()); + } else { + s.begin_list(13); + } + + s.append(&self.parent_hash); + s.append(&self.uncles_hash); + s.append(&self.author); + s.append(&self.state_root); + s.append(&self.transactions_root); + s.append(&self.receipts_root); + s.append(&self.log_bloom); + s.append(&self.difficulty); + s.append(&self.number); + s.append(&self.gas_limit); + s.append(&self.gas_used); + s.append(&self.timestamp); + s.append(&self.extra_data); + + if let Seal::With = with_seal { + for b in &self.seal { + s.append_raw(b, 1); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use error::BlockError; + use hex_literal::*; + use pow::EthashPartial; + use rustc_hex::FromHex; + use std::str::FromStr; + + #[inline] + fn sequential_header() -> (EthHeader, EthHeader) { + let mixh1 = H256::from(hex!("543bc0769f7d5df30e7633f4a01552c2cee7baace8a6da37fddaa19e49e81209")); + let nonce1 = H64::from(hex!("a5d3d0ccc8bb8a29")); + // #8996777 + let header1 = EthHeader { + parent_hash: H256::from(hex!("0b2d720b8d3b6601e4207ef926b0c228735aa1d58301a23d58f9cb51ac2288d8")), + timestamp: 0x5ddb67a0, + number: 0x8947a9, + author: Address::from(hex!("4c549990a7ef3fea8784406c1eecc98bf4211fa5")), + transactions_root: H256::from(hex!("07d44fadb4aca78c81698710211c5399c1408bb3f0aa3a687d091d230fcaddc6")), + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "5050594520686976656f6e2d6574682d6672".from_hex().unwrap(), + state_root: H256::from(hex!("4ba0fb3e6f4c1af32a799df667d304bcdb7f8154e6f86831f92f5a354c2baf70")), + receipts_root: H256::from(hex!("5968afe6026e673df3b9745d925a5648282d2195a46c22771fec48210daf8e23")), + log_bloom: Bloom::from_str("0c7b091bc8ec02401ad12491004e3014e8806390031950181c118580ac61c9a00409022c418162002710a991108a11ca5383d4921d1da46346edc3eb8068481118b005c0b20700414c13916c54011a0922904aa6e255406a33494c84a1426410541819070e04852042410b30030d4c88a5103082284c7d9bd42090322ae883e004224e18db4d858a0805d043e44a855400945311cb253001412002ea041a08e30394fc601440310920af2192dc4194a03302191cf2290ac0c12000815324eb96a08000aad914034c1c8eb0cb39422e272808b7a4911989c306381502868820b4b95076fc004b14dd48a0411024218051204d902b80d004c36510400ccb123084").unwrap(), + gas_used: 0x986d77.into(), + gas_limit: 0x989631.into(), + difficulty: 0x92ac28cbc4930_u64.into(), + seal: vec![rlp::encode(&mixh1), rlp::encode(&nonce1)], + hash: Some(H256::from(hex!("b80bf91d6f459227a9c617c5d9823ff0b07f1098ea16788676f0b804ecd42f3b"))), + }; + + // # 8996778 + let mixh2 = H256::from(hex!("0ea8027f96c18f474e9bc74ff71d29aacd3f485d5825be0a8dde529eb82a47ed")); + let nonce2 = H64::from(hex!("55859dc00728f99a")); + let header2 = EthHeader { + parent_hash: H256::from(hex!("b80bf91d6f459227a9c617c5d9823ff0b07f1098ea16788676f0b804ecd42f3b")), + timestamp: 0x5ddb67a3, + number: 0x8947aa, + author: Address::from(hex!("d224ca0c819e8e97ba0136b3b95ceff503b79f53")), + transactions_root: H256::from(hex!("efebac0e71cc2de04cf2f509bb038a82bbe92a659e010061b49b5387323b5ea6")), + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "7575706f6f6c2e636e2d3163613037623939".from_hex().unwrap(), + state_root: H256::from(hex!("5dfc6357dda61a7f927292509afacd51453ff158342eb9628ccb419fbe91c638")), + receipts_root: H256::from(hex!("3fbd99e253ff45045eec1e0011ac1b45fa0bccd641a356727defee3b166dd3bf")), + log_bloom: Bloom::from_str("0c0110a00144a0082057622381231d842b8977a98d1029841000a1c21641d91946594605e902a5432000159ad24a0300428d8212bf4d1c81c0f8478402a4a818010011437c07a112080e9a4a14822311a6840436f26585c84cc0d50693c148bf9830cf3e0a08970788a4424824b009080d52372056460dec808041b68ea04050bf116c041f25a3329d281068740ca911c0d4cd7541a1539005521694951c286567942d0024852080268d29850000954188f25151d80e4900002122c01ad53b7396acd34209c24110b81b9278642024603cd45387812b0696d93992829090619cf0b065a201082280812020000430601100cb08a3808204571c0e564d828648fb").unwrap(), + gas_used: 0x98254e.into(), + gas_limit: 0x98700d.into(), + difficulty: 0x92c07e50de0b9_u64.into(), + seal: vec![rlp::encode(&mixh2), rlp::encode(&nonce2)], + hash: Some(H256::from(hex!("b972df738904edb8adff9734eebdcb1d3b58fdfc68a48918720a4a247170f15e"))), + }; + + (header1, header2) + } + + #[test] + fn can_do_proof_of_work_verification_fail() { + let mut header: EthHeader = EthHeader::default(); + header.set_seal(vec![rlp::encode(&H256::zero()), rlp::encode(&H64::zero())]); + header.set_difficulty( + U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap(), + ); + + let ethash_params = EthashPartial::expanse(); + let verify_result = ethash_params.verify_block_basic(&header); + + match verify_result { + Err(BlockError::InvalidProofOfWork(_)) => {} + Err(_) => { + panic!("should be invalid proof of work error (got {:?})", verify_result); + } + _ => { + panic!("Should be error, got Ok"); + } + } + } + + #[test] + fn can_verify_basic_difficulty() { + let header = sequential_header().0; + let ethash_params = EthashPartial::expanse(); + assert_eq!(ethash_params.verify_block_basic(&header), Ok(())); + } + + #[test] + fn can_calculate_difficulty() { + let (header1, header2) = sequential_header(); + let expected = U256::from_str("92c07e50de0b9").unwrap(); + let mut ethash_params = EthashPartial::expanse(); + ethash_params.set_difficulty_bomb_delays(0xc3500, 5000000); + assert_eq!(ethash_params.calculate_difficulty(&header2, &header1), expected); + } +} diff --git a/core/sr-eth-primitives/src/lib.rs b/core/sr-eth-primitives/src/lib.rs index f215f00ca..86bf36390 100644 --- a/core/sr-eth-primitives/src/lib.rs +++ b/core/sr-eth-primitives/src/lib.rs @@ -9,6 +9,7 @@ use rstd::vec::Vec; pub mod encoded; pub mod error; //pub mod keccak; +pub mod header; pub mod pow; pub mod receipt; //pub mod transaction; @@ -27,7 +28,7 @@ pub type BlockNumber = u64; #[derive(Default, Clone, Copy, Eq, PartialEq, Encode, Decode)] pub struct BestBlock { - height: u64, // enough for ethereum poa network (kovan) + height: BlockNumber, // enough for ethereum poa network (kovan) hash: H256, total_difficulty: U256, } diff --git a/core/sr-eth-primitives/src/pow.rs b/core/sr-eth-primitives/src/pow.rs index 21ea79fe4..89df58120 100644 --- a/core/sr-eth-primitives/src/pow.rs +++ b/core/sr-eth-primitives/src/pow.rs @@ -1,82 +1,181 @@ /// A simplified prototype for light verification for pow. use super::*; //use crate::keccak::{keccak_256, keccak_512, H256 as BH256}; +use codec::{Decode, Encode}; use core::cmp; use core::convert::{From, Into, TryFrom}; use error::{BlockError, Mismatch, OutOfBounds}; use ethbloom::Bloom; -use keccak_hash::KECCAK_EMPTY_LIST_RLP; -use rstd::collections::btree_map::BTreeMap; -use rstd::mem; -use rstd::result; - -use codec::{Decode, Encode}; - use ethereum_types::BigEndianHash; +use header::EthHeader; +use keccak_hash::KECCAK_EMPTY_LIST_RLP; use primitive_types::{H160, H256, U128, U256, U512}; - use rlp::*; +use rstd::{collections::btree_map::BTreeMap, mem, result}; //use substrate_primitives::RuntimeDebug; -pub const MINIMUM_DIFFICULTY: u128 = 131072; -// TODO: please keep an eye on this. -// it might change due to ethereum's upgrade -pub const PROGPOW_TRANSITION: u64 = u64::max_value(); -//pub const DIFFICULTY_HARDFORK_TRANSITION: u64 = 0x59d9; -pub const DIFFICULTY_HARDFORK_BOUND_DIVISOR: u128 = 0x0200; -pub const DIFFICULTY_BOUND_DIVISOR: u128 = 0x0800; -pub const EXPIP2_TRANSITION: u64 = 0xc3500; -pub const EXPIP2_DURATION_LIMIT: u64 = 0x1e; -pub const DURATION_LIMIT: u64 = 0x3C; -pub const HOMESTEAD_TRANSITION: u64 = 0x30d40; -pub const EIP100B_TRANSITION: u64 = 0xC3500; -pub const DIFFICULTY_INCREMENT_DIVISOR: u64 = 0x3C; -pub const METROPOLIS_DIFFICULTY_INCREMENT_DIVISOR: u64 = 0x1E; - -pub const BOMB_DEFUSE_TRANSITION: u64 = 0x30d40; -// 3,000,000 -pub const ECIP1010_PAUSE_TRANSITION: u64 = 0x2dc6c0; -// 5,000,000 -pub const ECIP1010_CONTINUE_TRANSITION: u64 = 0x4c4b40; - -pub const DIFFICULTY_HARDFORK_TRANSITION: u64 = u64::max_value(); - #[derive(Default, PartialEq, Eq, Clone, Encode, Decode)] -pub struct EthHeader { - parent_hash: H256, - timestamp: u64, - number: BlockNumber, - author: Address, - transactions_root: H256, - uncles_hash: H256, - extra_data: Bytes, - state_root: H256, - receipts_root: H256, - log_bloom: Bloom, - gas_used: U256, - gas_limit: U256, - difficulty: U256, - seal: Vec, - hash: Option, +pub struct EthashPartial { + pub minimum_difficulty: U256, + pub difficulty_bound_divisor: U256, + pub difficulty_increment_divisor: u64, + pub metropolis_difficulty_increment_divisor: u64, + pub duration_limit: u64, + pub homestead_transition: u64, + pub difficulty_hardfork_transition: u64, + pub difficulty_hardfork_bound_divisor: U256, + pub bomb_defuse_transition: u64, + pub eip100b_transition: u64, + pub ecip1010_pause_transition: u64, + pub ecip1010_continue_transition: u64, + pub difficulty_bomb_delays: BTreeMap, + pub expip2_transition: u64, + pub expip2_duration_limit: u64, + pub progpow_transition: u64, } -/// Alter value of given field, reset memoised hash if changed. -fn change_field(hash: &mut Option, field: &mut T, value: T) -where - T: PartialEq, -{ - if field != &value { - *field = value; - *hash = None; + +impl EthashPartial { + pub fn set_difficulty_bomb_delays(&mut self, key: BlockNumber, value: BlockNumber) { + self.difficulty_bomb_delays.insert(key, value); + } + + pub fn expanse() -> Self { + EthashPartial { + minimum_difficulty: U256::from(131072_u128), + difficulty_bound_divisor: U256::from(0x0800), + difficulty_increment_divisor: 0x3C, + metropolis_difficulty_increment_divisor: 0x1E, + duration_limit: 0x3C, + homestead_transition: 0x30d40, + difficulty_hardfork_transition: 0x59d9, + difficulty_hardfork_bound_divisor: U256::from(0x0200), + bomb_defuse_transition: 0x30d40, + eip100b_transition: 0xC3500, + ecip1010_pause_transition: 0x2dc6c0, + ecip1010_continue_transition: 0x4c4b40, + difficulty_bomb_delays: BTreeMap::::default(), + expip2_transition: 0xc3500, + expip2_duration_limit: 0x1e, + progpow_transition: u64::max_value(), + } } } -#[derive(PartialEq, Eq, Clone, Encode, Decode, Copy)] -enum Seal { - /// The seal/signature is included. - With, - /// The seal/signature is not included. - Without, +impl EthashPartial { + pub fn verify_block_basic(&self, header: &EthHeader) -> result::Result<(), error::BlockError> { + // check the seal fields. + let seal = EthashSeal::parse_seal(header.seal())?; + + // TODO: consider removing these lines. + let min_difficulty = self.minimum_difficulty; + if header.difficulty() < &min_difficulty { + return Err(BlockError::DifficultyOutOfBounds(OutOfBounds { + min: Some(min_difficulty), + max: None, + found: header.difficulty().clone(), + })); + } + + let difficulty = boundary_to_difficulty(&H256(quick_get_difficulty( + &header.bare_hash().0, + seal.nonce.to_low_u64_be(), + &seal.mix_hash.0, + header.number() >= self.progpow_transition, + ))); + + if &difficulty < header.difficulty() { + return Err(BlockError::InvalidProofOfWork(OutOfBounds { + min: Some(header.difficulty().clone()), + max: None, + found: difficulty, + })); + } + + Ok(()) + } + + pub fn calculate_difficulty(&self, header: &EthHeader, parent: &EthHeader) -> 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.minimum_difficulty; + + let difficulty_hardfork = header.number() >= self.difficulty_hardfork_transition; + let difficulty_bound_divisor = if difficulty_hardfork { + self.difficulty_hardfork_bound_divisor + } else { + self.difficulty_bound_divisor + }; + + let expip2_hardfork = header.number() >= self.expip2_transition; + let duration_limit = if expip2_hardfork { + self.expip2_duration_limit + } else { + self.duration_limit + }; + + let frontier_limit = self.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.eip100b_transition { + (self.difficulty_increment_divisor, 1) + } else if parent_has_uncles { + (self.metropolis_difficulty_increment_divisor, 2) + } else { + (self.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.bomb_defuse_transition { + if header.number() < self.ecip1010_pause_transition { + let mut number = header.number(); + let original_number = number; + for (block, delay) in &self.difficulty_bomb_delays { + if original_number >= *block { + number = number.saturating_sub(*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.ecip1010_continue_transition { + let fixed_difficulty = ((self.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.ecip1010_continue_transition - self.ecip1010_pause_transition) / EXP_DIFF_PERIOD) as usize; + target = cmp::max(min_difficulty, target + (U256::from(1) << (period - delay - 2))); + } + } + target + } } #[derive(PartialEq, Eq, Clone, Encode, Decode)] @@ -110,183 +209,6 @@ impl EthashSeal { } } -impl EthHeader { - /// Create a new, default-valued, header. - pub fn new() -> Self { - Self::default() - } - - /// Get the parent_hash field of the header. - pub fn parent_hash(&self) -> &H256 { - &self.parent_hash - } - - /// Get the timestamp field of the header. - pub fn timestamp(&self) -> u64 { - self.timestamp - } - - /// Get the number field of the header. - pub fn number(&self) -> BlockNumber { - self.number - } - - /// Get the author field of the header. - pub fn author(&self) -> &Address { - &self.author - } - - /// Get the extra data field of the header. - pub fn extra_data(&self) -> &Bytes { - &self.extra_data - } - - /// Get the state root field of the header. - pub fn state_root(&self) -> &H256 { - &self.state_root - } - - /// Get the receipts root field of the header. - pub fn receipts_root(&self) -> &H256 { - &self.receipts_root - } - - /// Get the log bloom field of the header. - pub fn log_bloom(&self) -> &Bloom { - &self.log_bloom - } - - /// Get the transactions root field of the header. - pub fn transactions_root(&self) -> &H256 { - &self.transactions_root - } - - /// Get the uncles hash field of the header. - pub fn uncles_hash(&self) -> &H256 { - &self.uncles_hash - } - - /// Get the gas used field of the header. - pub fn gas_used(&self) -> &U256 { - &self.gas_used - } - - /// Get the gas limit field of the header. - pub fn gas_limit(&self) -> &U256 { - &self.gas_limit - } - - /// Get the difficulty field of the header. - pub fn difficulty(&self) -> &U256 { - &self.difficulty - } - - /// Get the seal field of the header. - pub fn seal(&self) -> &[Bytes] { - &self.seal - } - - /// Set the seal field of the header. - pub fn set_seal(&mut self, a: Vec) { - change_field(&mut self.hash, &mut self.seal, a) - } - - /// Set the difficulty field of the header. - pub fn set_difficulty(&mut self, a: U256) { - change_field(&mut self.hash, &mut self.difficulty, a); - } - - /// Get & memoize the hash of this header (keccak of the RLP with seal). - pub fn compute_hash(&mut self) -> H256 { - let hash = self.hash(); - self.hash = Some(hash); - hash - } - - /// Get the hash of this header (keccak of the RLP with seal). - pub fn hash(&self) -> H256 { - self.hash.unwrap_or_else(|| keccak_hash::keccak(self.rlp(Seal::With))) - } - - /// Get the hash of the header excluding the seal - pub fn bare_hash(&self) -> H256 { - keccak_hash::keccak(self.rlp(Seal::Without)) - } - - /// Encode the header, getting a type-safe wrapper around the RLP. - pub fn encoded(&self) -> encoded::Header { - encoded::Header::new(self.rlp(Seal::With)) - } - - /// Get the RLP representation of this Header. - fn rlp(&self, with_seal: Seal) -> Bytes { - let mut s = RlpStream::new(); - self.stream_rlp(&mut s, with_seal); - s.out() - } - - /// Place this header into an RLP stream `s`, optionally `with_seal`. - fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { - if let Seal::With = with_seal { - s.begin_list(13 + self.seal.len()); - } else { - s.begin_list(13); - } - - s.append(&self.parent_hash); - s.append(&self.uncles_hash); - s.append(&self.author); - s.append(&self.state_root); - s.append(&self.transactions_root); - s.append(&self.receipts_root); - s.append(&self.log_bloom); - s.append(&self.difficulty); - s.append(&self.number); - s.append(&self.gas_limit); - s.append(&self.gas_used); - s.append(&self.timestamp); - s.append(&self.extra_data); - - if let Seal::With = with_seal { - for b in &self.seal { - s.append_raw(b, 1); - } - } - } -} - -pub fn verify_block_basic(header: &EthHeader) -> result::Result<(), error::BlockError> { - // check the seal fields. - let seal = EthashSeal::parse_seal(header.seal())?; - - // TODO: consider removing these lines. - let min_difficulty = MINIMUM_DIFFICULTY.into(); - if header.difficulty() < &min_difficulty { - return Err(BlockError::DifficultyOutOfBounds(OutOfBounds { - min: Some(min_difficulty), - max: None, - found: header.difficulty().clone(), - })); - } - - let difficulty = boundary_to_difficulty(&H256(quick_get_difficulty( - &header.bare_hash().0, - seal.nonce.to_low_u64_be(), - &seal.mix_hash.0, - header.number() >= PROGPOW_TRANSITION, - ))); - - if &difficulty < header.difficulty() { - return Err(BlockError::InvalidProofOfWork(OutOfBounds { - min: Some(header.difficulty().clone()), - max: None, - found: difficulty, - })); - } - - Ok(()) -} - pub fn boundary_to_difficulty(boundary: ðereum_types::H256) -> U256 { difficulty_to_boundary_aux(&boundary.into_uint()) } @@ -337,175 +259,3 @@ fn quick_get_difficulty(header_hash: &[u8; 32], nonce: u64, mix_hash: &[u8; 32], // hash // } } - -fn calculate_difficulty(header: &EthHeader, parent: &EthHeader) -> U256 { - const EXP_DIFF_PERIOD: u64 = 100_000; - - let mut difficulty_bomb_delays = BTreeMap::::new(); - difficulty_bomb_delays.insert(0xC3500, 3000000); - 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 = U256::from(MINIMUM_DIFFICULTY); - - let difficulty_hardfork = header.number() >= DIFFICULTY_HARDFORK_TRANSITION; - let difficulty_bound_divisor = U256::from(if difficulty_hardfork { - DIFFICULTY_HARDFORK_BOUND_DIVISOR - } else { - DIFFICULTY_BOUND_DIVISOR - }); - - let expip2_hardfork = header.number() >= EXPIP2_TRANSITION; - let duration_limit = if expip2_hardfork { - EXPIP2_DURATION_LIMIT - } else { - DURATION_LIMIT - }; - - let frontier_limit = 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() < EIP100B_TRANSITION { - (DIFFICULTY_INCREMENT_DIVISOR, 1) - } else if parent_has_uncles { - (METROPOLIS_DIFFICULTY_INCREMENT_DIVISOR, 2) - } else { - (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() < BOMB_DEFUSE_TRANSITION { - if header.number() < ECIP1010_PAUSE_TRANSITION { - let mut number = header.number(); - let original_number = number; - for (block, delay) in &difficulty_bomb_delays { - if original_number >= *block { - number = number.saturating_sub(*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() < ECIP1010_CONTINUE_TRANSITION { - let fixed_difficulty = ((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 = ((ECIP1010_CONTINUE_TRANSITION - ECIP1010_PAUSE_TRANSITION) / EXP_DIFF_PERIOD) as usize; - target = cmp::max(min_difficulty, target + (U256::from(1) << (period - delay - 2))); - } - } - target -} - -#[cfg(test)] -mod tests { - use super::*; - use error::BlockError; - use hex_literal::*; - use rustc_hex::FromHex; - use std::str::FromStr; - - #[inline] - fn sequential_header() -> (EthHeader, EthHeader) { - let mixh1 = H256::from(hex!("543bc0769f7d5df30e7633f4a01552c2cee7baace8a6da37fddaa19e49e81209")); - let nonce1 = H64::from(hex!("a5d3d0ccc8bb8a29")); - // #8996777 - let header1 = EthHeader { - parent_hash: H256::from(hex!("0b2d720b8d3b6601e4207ef926b0c228735aa1d58301a23d58f9cb51ac2288d8")), - timestamp: 0x5ddb67a0, - number: 0x8947a9, - author: Address::from(hex!("4c549990a7ef3fea8784406c1eecc98bf4211fa5")), - transactions_root: H256::from(hex!("07d44fadb4aca78c81698710211c5399c1408bb3f0aa3a687d091d230fcaddc6")), - uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), - extra_data: "5050594520686976656f6e2d6574682d6672".from_hex().unwrap(), - state_root: H256::from(hex!("4ba0fb3e6f4c1af32a799df667d304bcdb7f8154e6f86831f92f5a354c2baf70")), - receipts_root: H256::from(hex!("5968afe6026e673df3b9745d925a5648282d2195a46c22771fec48210daf8e23")), - log_bloom: Bloom::from_str("0c7b091bc8ec02401ad12491004e3014e8806390031950181c118580ac61c9a00409022c418162002710a991108a11ca5383d4921d1da46346edc3eb8068481118b005c0b20700414c13916c54011a0922904aa6e255406a33494c84a1426410541819070e04852042410b30030d4c88a5103082284c7d9bd42090322ae883e004224e18db4d858a0805d043e44a855400945311cb253001412002ea041a08e30394fc601440310920af2192dc4194a03302191cf2290ac0c12000815324eb96a08000aad914034c1c8eb0cb39422e272808b7a4911989c306381502868820b4b95076fc004b14dd48a0411024218051204d902b80d004c36510400ccb123084").unwrap(), - gas_used: 0x986d77.into(), - gas_limit: 0x989631.into(), - difficulty: 0x92ac28cbc4930_u64.into(), - seal: vec![rlp::encode(&mixh1), rlp::encode(&nonce1)], - hash: Some(H256::from(hex!("b80bf91d6f459227a9c617c5d9823ff0b07f1098ea16788676f0b804ecd42f3b"))), - }; - - // # 8996778 - let mixh2 = H256::from(hex!("0ea8027f96c18f474e9bc74ff71d29aacd3f485d5825be0a8dde529eb82a47ed")); - let nonce2 = H64::from(hex!("55859dc00728f99a")); - let header2 = EthHeader { - parent_hash: H256::from(hex!("b80bf91d6f459227a9c617c5d9823ff0b07f1098ea16788676f0b804ecd42f3b")), - timestamp: 0x5ddb67a3, - number: 0x8947aa, - author: Address::from(hex!("d224ca0c819e8e97ba0136b3b95ceff503b79f53")), - transactions_root: H256::from(hex!("efebac0e71cc2de04cf2f509bb038a82bbe92a659e010061b49b5387323b5ea6")), - uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), - extra_data: "7575706f6f6c2e636e2d3163613037623939".from_hex().unwrap(), - state_root: H256::from(hex!("5dfc6357dda61a7f927292509afacd51453ff158342eb9628ccb419fbe91c638")), - receipts_root: H256::from(hex!("3fbd99e253ff45045eec1e0011ac1b45fa0bccd641a356727defee3b166dd3bf")), - log_bloom: Bloom::from_str("0c0110a00144a0082057622381231d842b8977a98d1029841000a1c21641d91946594605e902a5432000159ad24a0300428d8212bf4d1c81c0f8478402a4a818010011437c07a112080e9a4a14822311a6840436f26585c84cc0d50693c148bf9830cf3e0a08970788a4424824b009080d52372056460dec808041b68ea04050bf116c041f25a3329d281068740ca911c0d4cd7541a1539005521694951c286567942d0024852080268d29850000954188f25151d80e4900002122c01ad53b7396acd34209c24110b81b9278642024603cd45387812b0696d93992829090619cf0b065a201082280812020000430601100cb08a3808204571c0e564d828648fb").unwrap(), - gas_used: 0x98254e.into(), - gas_limit: 0x98700d.into(), - difficulty: 0x92c07e50de0b9_u64.into(), - seal: vec![rlp::encode(&mixh2), rlp::encode(&nonce2)], - hash: Some(H256::from(hex!("b972df738904edb8adff9734eebdcb1d3b58fdfc68a48918720a4a247170f15e"))), - }; - - (header1, header2) - } - - #[test] - fn can_do_proof_of_work_verification_fail() { - let mut header: EthHeader = EthHeader::default(); - header.set_seal(vec![rlp::encode(&H256::zero()), rlp::encode(&H64::zero())]); - header.set_difficulty( - U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap(), - ); - - let verify_result = verify_block_basic(&header); - - match verify_result { - Err(BlockError::InvalidProofOfWork(_)) => {} - Err(_) => { - panic!("should be invalid proof of work error (got {:?})", verify_result); - } - _ => { - panic!("Should be error, got Ok"); - } - } - } - - #[test] - fn can_verify_basic_difficulty() { - let header = sequential_header().0; - assert_eq!(verify_block_basic(&header), Ok(())); - } - - #[test] - fn can_calculate_difficulty() { - let (header1, header2) = sequential_header(); - let expected = U256::from_str("92c07e50de0b9").unwrap(); - assert_eq!(calculate_difficulty(&header2, &header1), expected); - } -} diff --git a/srml/ethereum-bridge/src/lib.rs b/srml/ethereum-bridge/src/lib.rs index 274357c2a..9ca74d648 100644 --- a/srml/ethereum-bridge/src/lib.rs +++ b/srml/ethereum-bridge/src/lib.rs @@ -6,10 +6,11 @@ // use blake2::Blake2b; use codec::{Decode, Encode}; use rstd::vec::Vec; -use sr_eth_primitives::pow::EthHeader; +use sr_eth_primitives::{ + header::EthHeader, BestBlock, BlockNumber as EthBlockNumber, H160, H256, H64, U128, U256, U512, +}; use support::{decl_event, decl_module, decl_storage, dispatch::Result, traits::Currency}; use system::ensure_signed; -//use sr_eth_primitives::{pow::EthHeader, H160, H256, H64, U128, U256, U512}; //use sr_primitives::RuntimeDebug; @@ -40,18 +41,17 @@ pub trait Trait: system::Trait { decl_storage! { trait Store for Module as EthBridge { - pub BeginNumber get(begin_number): u64; - } -} + /// Anchor block that works as genesis block + pub BeginHeader get(begin_header): Option; + /// Info of the best block header for now + pub BestHeader get(best_header): BestBlock; + /// + pub BlockList get(block_list): map EthBlockNumber => EthHeader; -// pub BeginHeader get(begin_header): Option; - -// pub BestHeader get(best_header): BestBlock; // pub HeaderOf get(header_of): map H256 => Option; // pub BestHashOf get(best_hash_of): map u64 => Option; - // pub HashsOf get(hashs_of): map u64 => Vec; // Block delay for verify transaction @@ -63,7 +63,7 @@ decl_storage! { // add_extra_genesis { // config(header): Option>; -// config(number): u64; +// config(number): u64;z // build(|config| { // if let Some(h) = &config.header { // BeginNumber::put(header.number); @@ -74,6 +74,8 @@ decl_storage! { // } // }); // } + } +} decl_module! { pub struct Module for enum Call @@ -115,9 +117,10 @@ impl Module { /// 1. if exists? /// 2. verify (difficulty + prev_hash + nonce + re-org) /// 3. challenge - // fn verify(_: &EthHeader) -> Result { - // unimplemented!() - // } + fn verify(header: &EthHeader) -> Result { + let number = header.number(); + Ok(()) + } fn _punish(_who: &T::AccountId) -> Result { unimplemented!()