diff --git a/Cargo.lock b/Cargo.lock index 4dd27c688..dc310201f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -839,15 +839,22 @@ name = "darwinia-eth-relay" version = "0.2.0" dependencies = [ "ethash 0.4.0 (git+https://github.com/hammeWang/ethash-rs.git?rev=70a4f078)", + "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "keccak-hasher 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", "merkle-patricia-trie 0.1.0", "parity-scale-codec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.4.4 (git+https://github.com/darwinia-network/parity-common.git)", + "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sr-eth-primitives 0.2.0", + "sr-io 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "sr-primitives 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "sr-std 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "srml-support 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "srml-system 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", + "srml-transaction-payment 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", + "substrate-primitives 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", + "triehash 0.8.1 (git+https://github.com/darwinia-network/parity-common.git)", ] [[package]] @@ -2691,6 +2698,7 @@ dependencies = [ "keccak-hash 0.4.1 (git+https://github.com/darwinia-network/parity-common.git)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.4.4 (git+https://github.com/darwinia-network/parity-common.git)", + "sr-std 2.0.0 (git+https://github.com/darwinia-network/substrate.git?branch=darwinia-develop)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index 86d720d56..3a8114e1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "srml/balances", "srml/eth-relay", +# "srml/eth-backing", "srml/kton", "srml/staking", "srml/support" diff --git a/core/merkle-mountain-range/src/lib.rs b/core/merkle-mountain-range/src/lib.rs index bb358893e..ac21d0d57 100644 --- a/core/merkle-mountain-range/src/lib.rs +++ b/core/merkle-mountain-range/src/lib.rs @@ -5,13 +5,13 @@ extern crate test; mod common; -mod merkle_mountain_range; mod merkle_proof; +mod mmr; #[allow(unused)] #[cfg(all(feature = "std", test))] mod tests; pub use common::*; -pub use merkle_mountain_range::MerkleMountainRange; pub use merkle_proof::MerkleProof; +pub use mmr::MerkleMountainRange; diff --git a/core/merkle-mountain-range/src/merkle_mountain_range.rs b/core/merkle-mountain-range/src/mmr.rs similarity index 100% rename from core/merkle-mountain-range/src/merkle_mountain_range.rs rename to core/merkle-mountain-range/src/mmr.rs diff --git a/core/merkle-patricia-trie/Cargo.toml b/core/merkle-patricia-trie/Cargo.toml index 65a74a46c..12f3e679a 100644 --- a/core/merkle-patricia-trie/Cargo.toml +++ b/core/merkle-patricia-trie/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" rlp = { git = "https://github.com/darwinia-network/parity-common.git", default-features = false } hash = { package = "keccak-hash", git = "https://github.com/darwinia-network/parity-common.git", default-features = false } hashbrown = { version = "0.6.0" } +rstd = { package = "sr-std", git = "https://github.com/darwinia-network/substrate.git", branch = "darwinia-develop", default-features = false } [dev-dependencies] rand = "0.6.3" diff --git a/core/merkle-patricia-trie/src/db.rs b/core/merkle-patricia-trie/src/db.rs index 00289a732..5122b60e4 100644 --- a/core/merkle-patricia-trie/src/db.rs +++ b/core/merkle-patricia-trie/src/db.rs @@ -1,5 +1,5 @@ -use crate::std::*; use hashbrown::HashMap; +use rstd::{cell::RefCell, vec::Vec}; #[derive(Debug)] pub struct MemoryDB { diff --git a/core/merkle-patricia-trie/src/error.rs b/core/merkle-patricia-trie/src/error.rs index 6c0047f81..9ebb98a96 100644 --- a/core/merkle-patricia-trie/src/error.rs +++ b/core/merkle-patricia-trie/src/error.rs @@ -1,5 +1,13 @@ -use crate::std::*; use rlp::DecoderError; +use rstd::{borrow::ToOwned, fmt}; + +#[cfg(not(feature = "std"))] +extern crate alloc; + +#[cfg(not(feature = "std"))] +use alloc::format; +#[cfg(not(feature = "std"))] +use alloc::string::String; #[derive(Debug)] pub enum TrieError { diff --git a/core/merkle-patricia-trie/src/lib.rs b/core/merkle-patricia-trie/src/lib.rs index 52d96e639..991b42ca8 100644 --- a/core/merkle-patricia-trie/src/lib.rs +++ b/core/merkle-patricia-trie/src/lib.rs @@ -1,30 +1,7 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(not(feature = "std"))] -extern crate alloc; -#[cfg(not(feature = "std"))] -extern crate core; - -#[cfg(not(feature = "std"))] -mod std { - pub use alloc::borrow::ToOwned; - pub use alloc::format; - pub use alloc::rc::Rc; - pub use alloc::string::String; - pub use alloc::vec; - pub use alloc::vec::Vec; - - pub use core::cell::RefCell; - pub use core::fmt; -} - -#[cfg(feature = "std")] -mod std { - pub use std::cell::RefCell; - pub use std::fmt; - pub use std::rc::Rc; -} +use rstd::rc::Rc; mod db; mod error; @@ -65,7 +42,7 @@ where A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, { - let memdb = std::Rc::new(MemoryDB::new()); + let memdb = Rc::new(MemoryDB::new()); let mut trie = MerklePatriciaTrie::new(memdb.clone()); data.into_iter().for_each(|(key, value)| { // TODO the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `core::ops::Try`) diff --git a/core/merkle-patricia-trie/src/nibbles.rs b/core/merkle-patricia-trie/src/nibbles.rs index 14f4c44ad..69784314a 100644 --- a/core/merkle-patricia-trie/src/nibbles.rs +++ b/core/merkle-patricia-trie/src/nibbles.rs @@ -1,5 +1,5 @@ -use crate::std::*; use core::cmp::min; +use rstd::{vec, vec::Vec}; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Nibbles { diff --git a/core/merkle-patricia-trie/src/node.rs b/core/merkle-patricia-trie/src/node.rs index b9dc8b1dc..165d5df40 100644 --- a/core/merkle-patricia-trie/src/node.rs +++ b/core/merkle-patricia-trie/src/node.rs @@ -1,5 +1,5 @@ use crate::nibbles::Nibbles; -use crate::std::*; +use rstd::{cell::RefCell, rc::Rc, vec::Vec}; #[derive(Debug, Clone)] pub enum Node { diff --git a/core/merkle-patricia-trie/src/proof.rs b/core/merkle-patricia-trie/src/proof.rs index 400b40c40..1d0e6758b 100644 --- a/core/merkle-patricia-trie/src/proof.rs +++ b/core/merkle-patricia-trie/src/proof.rs @@ -1,5 +1,5 @@ -use crate::std::*; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; +use rstd::vec::Vec; #[derive(Clone)] #[cfg_attr(feature = "std", derive(Debug, PartialEq))] diff --git a/core/merkle-patricia-trie/src/trie.rs b/core/merkle-patricia-trie/src/trie.rs index a1ba47ecf..24147a8a7 100644 --- a/core/merkle-patricia-trie/src/trie.rs +++ b/core/merkle-patricia-trie/src/trie.rs @@ -1,7 +1,7 @@ -use crate::std::*; use hash::keccak; use hashbrown::{HashMap, HashSet}; use rlp::{Prototype, Rlp, RlpStream}; +use rstd::{cell::RefCell, rc::Rc, vec, vec::Vec}; use crate::db::MemoryDB; use crate::error::TrieError; diff --git a/core/sr-eth-primitives/src/header.rs b/core/sr-eth-primitives/src/header.rs index 36a14802a..348a8f8ff 100644 --- a/core/sr-eth-primitives/src/header.rs +++ b/core/sr-eth-primitives/src/header.rs @@ -15,21 +15,21 @@ enum Seal { #[derive(Default, PartialEq, Eq, Clone, Encode, Decode, RlpEncodable, RlpDecodable, RuntimeDebug)] 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 parent_hash: H256, + pub timestamp: u64, + pub number: BlockNumber, + pub author: Address, + pub transactions_root: H256, + pub uncles_hash: H256, + pub extra_data: Bytes, + pub state_root: H256, + pub receipts_root: H256, + pub log_bloom: Bloom, + pub gas_used: U256, + pub gas_limit: U256, + pub difficulty: U256, + pub seal: Vec, + pub hash: Option, } /// Alter value of given field, reset memoised hash if changed. @@ -325,7 +325,7 @@ mod tests { fn can_calculate_difficulty_ropsten() { let (header1, header2) = ropsten_sequential_header(); let expected = U256::from_str("f3c49f25").unwrap(); - let mut ethash_params = EthashPartial::ropsten_test(); + let mut ethash_params = EthashPartial::ropsten_testnet(); // 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/pow.rs b/core/sr-eth-primitives/src/pow.rs index 502b2befd..b5acb72c4 100644 --- a/core/sr-eth-primitives/src/pow.rs +++ b/core/sr-eth-primitives/src/pow.rs @@ -5,11 +5,10 @@ use codec::{Decode, Encode}; use core::cmp; use core::convert::{From, Into, TryFrom}; use error::{BlockError, Mismatch, OutOfBounds}; -use ethbloom::Bloom; use ethereum_types::BigEndianHash; use header::EthHeader; use keccak_hash::KECCAK_EMPTY_LIST_RLP; -use primitive_types::{H160, H256, U128, U256, U512}; +use primitive_types::{H256, U256, U512}; use rlp::*; use rstd::{collections::btree_map::BTreeMap, mem, result}; use sr_primitives::RuntimeDebug; @@ -86,9 +85,7 @@ impl EthashPartial { } } - /// TODO: to find out the exact ropsten params, only for testing. - #[cfg(feature = "std")] - pub fn ropsten_test() -> Self { + pub fn ropsten_testnet() -> Self { EthashPartial { minimum_difficulty: U256::from(0x20000), difficulty_bound_divisor: U256::from(0x0800), @@ -279,7 +276,7 @@ fn difficulty_to_boundary_aux>(difficulty: T) -> ethereum_types::U } } -fn quick_get_difficulty(header_hash: &[u8; 32], nonce: u64, mix_hash: &[u8; 32], progpow: bool) -> [u8; 32] { +fn quick_get_difficulty(header_hash: &[u8; 32], nonce: u64, mix_hash: &[u8; 32], _progpow: bool) -> [u8; 32] { let mut first_buf = [0u8; 40]; let mut buf = [0u8; 64 + 32]; diff --git a/node/runtime/src/constants.rs b/node/runtime/src/constants.rs index 42adfd12f..83a0e22f1 100644 --- a/node/runtime/src/constants.rs +++ b/node/runtime/src/constants.rs @@ -61,7 +61,7 @@ pub mod time { pub const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10 * MINUTES; pub const EPOCH_DURATION_IN_SLOTS: u64 = { - const SLOT_FILL_RATE: f64 = MILLISECS_PER_BLOCK as f64 / SLOT_DURATION as f64; + // const SLOT_FILL_RATE: f64 = MILLISECS_PER_BLOCK as f64 / SLOT_DURATION as f64; // Develop 2 diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 3fc3be52c..f7d2da38a 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -379,8 +379,14 @@ impl staking::Trait for Runtime { type SessionInterface = Self; } +parameter_types! { + pub const ETH_MAINET: u64 = 0; + pub const ETH_ROPSTEN: u64 = 1; +} + impl eth_relay::Trait for Runtime { type Event = Event; + type EthNetwork = ETH_ROPSTEN; } construct_runtime!( diff --git a/srml/balances/src/mock.rs b/srml/balances/src/mock.rs index 717be7a65..145e6cbfc 100644 --- a/srml/balances/src/mock.rs +++ b/srml/balances/src/mock.rs @@ -15,9 +15,6 @@ // along with Substrate. If not, see . //! Test utilities - -#![cfg(test)] - use crate::{GenesisConfig, Module, Trait}; use primitives::H256; use runtime_io; diff --git a/srml/balances/src/tests.rs b/srml/balances/src/tests.rs index 5aafa42d0..08808420c 100644 --- a/srml/balances/src/tests.rs +++ b/srml/balances/src/tests.rs @@ -15,9 +15,6 @@ // along with Substrate. If not, see . //! Tests for the module. - -#![cfg(test)] - use sr_primitives::traits::SignedExtension; use support::{ assert_err, assert_noop, assert_ok, diff --git a/srml/ethereum-migrate/Cargo.toml b/srml/eth-backing/Cargo.toml similarity index 97% rename from srml/ethereum-migrate/Cargo.toml rename to srml/eth-backing/Cargo.toml index 12cf71407..885bfb898 100644 --- a/srml/ethereum-migrate/Cargo.toml +++ b/srml/eth-backing/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "darwinia-ethereum-migrate" +name = "darwinia-eth-backing" version = "0.1.0" authors = ["Darwinia Network "] edition = "2018" diff --git a/srml/ethereum-migrate/src/lib.rs b/srml/eth-backing/src/lib.rs similarity index 92% rename from srml/ethereum-migrate/src/lib.rs rename to srml/eth-backing/src/lib.rs index d2f9d57ac..df1420ea0 100644 --- a/srml/ethereum-migrate/src/lib.rs +++ b/srml/eth-backing/src/lib.rs @@ -20,7 +20,7 @@ pub trait Trait: system::Trait { // config() require `serde = { version = "1.0.101", optional = true }` // tracking issue: https://github.com/rust-lang/rust/issues/27812 decl_storage! { - trait Store for Module as EthMigrate { + trait Store for Module as EthBacking { pub DepositPool get(deposit_pool) config(): RingBalanceOf; pub DepositValue get(deposit_value): RingBalanceOf; @@ -69,13 +69,6 @@ impl Module { unimplemented!() } - /// 1. if exists? - /// 2. verify (difficulty + prev_hash + nonce) - /// 3. challenge - fn verify(_: &Header) -> Result { - unimplemented!() - } - fn _punish(_who: &T::AccountId) -> Result { unimplemented!() } diff --git a/srml/eth-relay/Cargo.toml b/srml/eth-relay/Cargo.toml index 5a3888434..d77768323 100644 --- a/srml/eth-relay/Cargo.toml +++ b/srml/eth-relay/Cargo.toml @@ -20,6 +20,15 @@ rlp = { package = "rlp", git = "https://github.com/darwinia-network/parity-commo ethash = { git = "https://github.com/hammeWang/ethash-rs.git", rev = "70a4f078", default-features = false} merkle-patricia-trie = { path = "../../core/merkle-patricia-trie", default-features = false} +[dev-dependencies] +runtime-io = { package = "sr-io", git = "https://github.com/darwinia-network/substrate.git", branch = "darwinia-develop"} +primitives = { package = "substrate-primitives", git = "https://github.com/darwinia-network/substrate.git", branch = "darwinia-develop" } +transaction-payment = { package = "srml-transaction-payment", git = "https://github.com/darwinia-network/substrate.git", branch = "darwinia-develop" } +rustc-hex = "2.0" +keccak-hasher = "0.15.2" +triehash = { package = "triehash", git = "https://github.com/darwinia-network/parity-common.git" } +hex-literal = "0.2.1" + [features] default = ["std"] std = [ diff --git a/srml/eth-relay/src/lib.rs b/srml/eth-relay/src/lib.rs index 9e3deefc8..fb0c0300e 100644 --- a/srml/eth-relay/src/lib.rs +++ b/srml/eth-relay/src/lib.rs @@ -7,26 +7,30 @@ use codec::{Decode, Encode}; use rstd::vec::Vec; use sr_eth_primitives::{ - header::EthHeader, pow::EthashPartial, pow::EthashSeal, receipt::Receipt, BlockNumber as EthBlockNumber, H160, - H256, H64, U128, U256, U512, + header::EthHeader, pow::EthashPartial, pow::EthashSeal, receipt::Receipt, BlockNumber as EthBlockNumber, H256, U256, }; use ethash::{EthereumPatch, LightDAG}; -use support::{decl_event, decl_module, decl_storage, dispatch::Result, ensure, traits::Currency}; +use support::{decl_event, decl_module, decl_storage, dispatch::Result, ensure, traits::Get}; use system::ensure_signed; use sr_primitives::RuntimeDebug; -use rlp::{decode, encode}; - use merkle_patricia_trie::{trie::Trie, MerklePatriciaTrie, Proof}; type DAG = LightDAG; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + pub trait Trait: system::Trait { type Event: From> + Into<::Event>; + + type EthNetwork: Get; } /// Familial details concerning a block @@ -78,12 +82,14 @@ decl_storage! { } add_extra_genesis { config(header): Option>; + config(genesis_difficulty): u64; build(|config| { if let Some(h) = &config.header { let header: EthHeader = rlp::decode(&h).expect("can't deserialize the header"); + >::genesis_header(&header,config.genesis_difficulty); + // TODO: initilize other parameters. - >::genesis_header(&header); } }); } @@ -96,8 +102,12 @@ decl_module! { { fn deposit_event() = default; - pub fn test_relay_header(origin, header: EthHeader) { + pub fn reset_genesis_header(origin, header: EthHeader, genesis_difficulty: u64) { let _relayer = ensure_signed(origin)?; + // TODO: Check authority + + // TODO: Just for easy testing. + Self::genesis_header(&header, genesis_difficulty); >::deposit_event(RawEvent::NewHeader(header)); } @@ -109,69 +119,25 @@ decl_module! { Self::verify_header(&header)?; - let header_hash = header.hash(); - let block_number = header.number(); - - HeaderOf::insert(header_hash, &header); - - let prev_total_difficulty = Self::header_details_of(header.parent_hash()).unwrap().total_difficulty; - - HeaderDetailsOf::insert(header_hash, BlockDetails { - height: block_number, - hash: header_hash, - total_difficulty: prev_total_difficulty + header.difficulty() - }); - - let best_header_hash = Self::best_header_hash(); - let best_header = Self::header_of(best_header_hash).ok_or("Can not find best header."); - let best_header_details = Self::header_details_of(best_header_hash).unwrap(); - - // TODO: Check total difficulty and reorg if necessary. - if prev_total_difficulty + header.difficulty() > best_header_details.total_difficulty { - BestHeaderHash::mutate(|hash| { - *hash = header_hash; - }); - } + Self::store_header(&header)?; >::deposit_event(RawEvent::NewHeader(header)); } - pub fn test_check_receipt(origin, receipt: Receipt, proof_record: ActionRecord) { + pub fn check_receipt(origin, proof_record: ActionRecord) { let _relayer = ensure_signed(origin)?; - >::deposit_event(RawEvent::RelayProof(proof_record)); - } - - pub fn check_receipt(origin, receipt: Receipt, proof_record: ActionRecord) { - let _relayer = ensure_signed(origin)?; - - let header_hash = proof_record.header_hash; - if !HeaderOf::exists(header_hash) { - return Err("This block header does not exist.") - } - - let header = HeaderOf::get(header_hash).unwrap(); - - let proof: Proof = rlp::decode(&proof_record.proof).unwrap(); - let key = rlp::encode(&proof_record.index); + let verified_receipt = Self::verify_receipt(&proof_record); - let value = MerklePatriciaTrie::verify_proof(header.receipts_root().0.to_vec(), &key, proof) - .unwrap(); - assert!(value.is_some()); + ensure!(verified_receipt.is_some(), "Receipt proof verification failed."); - let receipt_encoded = rlp::encode(&receipt); - - assert_eq!(value.unwrap(), receipt_encoded); - // confirm that the block hash is right - // get the receipt MPT trie root from the block header - // Using receipt MPT trie root to verify the proof and index etc. - - >::deposit_event(RawEvent::RelayProof(proof_record)); + >::deposit_event(RawEvent::RelayProof(verified_receipt.unwrap(), proof_record)); } // Assuming that there are at least one honest worker submiting headers // This method may be merged together with relay_header - pub fn challenge_header(origin, header: EthHeader) { + pub fn challenge_header(origin, _header: EthHeader) { + let _relayer = ensure_signed(origin)?; // if header confirmed then return // if header in unverified header then challenge } @@ -184,20 +150,28 @@ decl_event! { ::AccountId { NewHeader(EthHeader), - RelayProof(ActionRecord), + RelayProof(Receipt, ActionRecord), TODO(AccountId), } } impl Module { // TOOD: what is the total difficulty for genesis/begin header - pub fn genesis_header(header: &EthHeader) { + pub fn genesis_header(header: &EthHeader, genesis_difficulty: u64) { let header_hash = header.hash(); - // let block_number = header.number(); + let block_number = header.number(); - HeaderOf::insert(header_hash, header); + HeaderOf::insert(&header_hash, header); - // TODO: initialize the header details, including total difficulty. + // initialize the header details, including total difficulty. + HeaderDetailsOf::insert( + &header_hash, + BlockDetails { + height: block_number, + hash: header_hash, + total_difficulty: genesis_difficulty.into(), + }, + ); // Initialize the the best hash. BestHeaderHash::mutate(|hash| { @@ -208,6 +182,30 @@ impl Module { BeginHeader::put(header.clone()); } + fn verify_receipt(proof_record: &ActionRecord) -> Option { + let header_hash = proof_record.header_hash; + if !HeaderOf::exists(header_hash) { + return None; //Err("This block header does not exist."); + } + + let header = HeaderOf::get(header_hash).unwrap(); + + let proof: Proof = rlp::decode(&proof_record.proof).unwrap(); + let key = rlp::encode(&proof_record.index); + + let value = MerklePatriciaTrie::verify_proof(header.receipts_root().0.to_vec(), &key, proof).unwrap(); + if !value.is_some() { + return None; + } + + let proof_receipt: Receipt = rlp::decode(&value.unwrap()).expect("can't deserialize the receipt"); + + Some(proof_receipt) + // confirm that the block hash is right + // get the receipt MPT trie root from the block header + // Using receipt MPT trie root to verify the proof and index etc. + } + /// 1. proof of difficulty /// 2. proof of pow (mixhash) /// 3. challenge @@ -226,27 +224,21 @@ impl Module { ensure!((prev_header.number() + 1) == number, "Block number does not match."); // check difficulty - let ethash_params = EthashPartial::production(); - // ethash_params.set_difficulty_bomb_delays(0xc3500, 5000000); - let result = ethash_params.verify_block_basic(header); - match result { - Ok(_) => (), - Err(e) => { - return Err("Block difficulty verification failed."); - } + let ethash_params = match T::EthNetwork::get() { + 0 => EthashPartial::production(), + 1 => EthashPartial::ropsten_testnet(), + _ => EthashPartial::production(), // others }; + ethash_params + .verify_block_basic(header) + .expect("Block difficulty verification failed."); // verify difficulty let difficulty = ethash_params.calculate_difficulty(header, &prev_header); ensure!(difficulty == *header.difficulty(), "difficulty verification failed"); // verify mixhash - let seal = match EthashSeal::parse_seal(header.seal()) { - Err(e) => { - return Err("Seal parse error."); - } - Ok(x) => x, - }; + let seal = EthashSeal::parse_seal(header.seal()).unwrap(); let light_dag = DAG::new(number.into()); let partial_header_hash = header.bare_hash(); @@ -262,6 +254,37 @@ impl Module { Ok(()) } + fn store_header(header: &EthHeader) -> Result { + let header_hash = header.hash(); + let block_number = header.number(); + + HeaderOf::insert(header_hash, header); + + let prev_total_difficulty = Self::header_details_of(header.parent_hash()).unwrap().total_difficulty; + + HeaderDetailsOf::insert( + header_hash, + BlockDetails { + height: block_number, + hash: header_hash, + total_difficulty: prev_total_difficulty + header.difficulty(), + }, + ); + + let best_header_hash = Self::best_header_hash(); + // let best_header = Self::header_of(best_header_hash).ok_or("Can not find best header."); + let best_header_details = Self::header_details_of(best_header_hash).unwrap(); + + // TODO: Check total difficulty and reorg if necessary. + if prev_total_difficulty + header.difficulty() > best_header_details.total_difficulty { + BestHeaderHash::mutate(|hash| { + *hash = header_hash; + }); + } + + Ok(()) + } + fn _punish(_who: &T::AccountId) -> Result { unimplemented!() } diff --git a/srml/eth-relay/src/mock.rs b/srml/eth-relay/src/mock.rs new file mode 100644 index 000000000..c747c7ebd --- /dev/null +++ b/srml/eth-relay/src/mock.rs @@ -0,0 +1,162 @@ +//! Test utilities + +use crate::{Module, Trait}; +use primitives::H256; +use runtime_io; +use sr_primitives::{ + testing::Header, + traits::IdentityLookup, + // weights::{DispatchInfo, Weight}, + Perbill, +}; +use std::cell::RefCell; +//use support::traits::Get; +use support::{impl_outer_origin, parameter_types}; + +impl_outer_origin! { + pub enum Origin for Runtime {} +} + +thread_local! { + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); + static TRANSFER_FEE: RefCell = RefCell::new(0); + static CREATION_FEE: RefCell = RefCell::new(0); +} + +//pub struct ExistentialDeposit; +//impl Get for ExistentialDeposit { +// fn get() -> u64 { +// EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) +// } +//} +// +//pub struct TransferFee; +//impl Get for TransferFee { +// fn get() -> u64 { +// TRANSFER_FEE.with(|v| *v.borrow()) +// } +//} +// +//pub struct CreationFee; +//impl Get for CreationFee { +// fn get() -> u64 { +// CREATION_FEE.with(|v| *v.borrow()) +// } +//} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Runtime; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} +impl system::Trait for Runtime { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Call = (); + type Hash = H256; + type Hashing = ::sr_primitives::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); +} +parameter_types! { + pub const TransactionBaseFee: u64 = 0; + pub const TransactionByteFee: u64 = 1; +} + +parameter_types! { + pub const ETH_MAINET: u64 = 0; + pub const ETH_ROPSTEN: u64 = 1; +} + +impl Trait for Runtime { + type Event = (); + type EthNetwork = ETH_ROPSTEN; +} + +parameter_types! { + pub const MinimumPeriod: u64 = 5; +} + +pub struct ExtBuilder { + existential_deposit: u64, + transfer_fee: u64, + creation_fee: u64, + monied: bool, + vesting: bool, +} +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: 0, + transfer_fee: 0, + creation_fee: 0, + monied: false, + vesting: false, + } + } +} +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + #[allow(dead_code)] + pub fn transfer_fee(mut self, transfer_fee: u64) -> Self { + self.transfer_fee = transfer_fee; + self + } + #[allow(dead_code)] + pub fn creation_fee(mut self, creation_fee: u64) -> Self { + self.creation_fee = creation_fee; + self + } + #[allow(dead_code)] + pub fn monied(mut self, monied: bool) -> Self { + self.monied = monied; + if self.existential_deposit == 0 { + self.existential_deposit = 1; + } + self + } + #[allow(dead_code)] + pub fn vesting(mut self, vesting: bool) -> Self { + self.vesting = vesting; + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + TRANSFER_FEE.with(|v| *v.borrow_mut() = self.transfer_fee); + CREATION_FEE.with(|v| *v.borrow_mut() = self.creation_fee); + } + pub fn build(self) -> runtime_io::TestExternalities { + self.set_associated_consts(); + let t = system::GenesisConfig::default().build_storage::().unwrap(); + + t.into() + } +} + +pub type System = system::Module; +pub type EthRelay = Module; + +//pub const CALL: &::Call = &(); + +// create a transaction info struct from weight. Handy to avoid building the whole struct. +//pub fn info_from_weight(w: Weight) -> DispatchInfo { +// DispatchInfo { +// weight: w, +// ..Default::default() +// } +//} diff --git a/srml/eth-relay/src/tests.rs b/srml/eth-relay/src/tests.rs new file mode 100644 index 000000000..e86b9c573 --- /dev/null +++ b/srml/eth-relay/src/tests.rs @@ -0,0 +1,167 @@ +//! Tests for the module. +use super::*; +//use sr_primitives::traits::SignedExtension; +//use support::{ +// assert_err, assert_noop, assert_ok, +// traits::{Currency, ExistenceRequirement::AllowDeath, LockableCurrency, ReservableCurrency}, +//}; +//use system::RawOrigin; + +use mock::{EthRelay, ExtBuilder, System}; +//use mock::{info_from_weight, EthRelay, ExtBuilder, Runtime, System, CALL}; + +//use rstd::prelude::*; +use sr_eth_primitives::{ + receipt::{LogEntry, TransactionOutcome}, + Address, Bloom, H64, U128, +}; + +use hex_literal::hex; +use rustc_hex::FromHex; +use std::str::FromStr; + +#[test] +fn verify_receipt_proof() { + ExtBuilder::default() + .existential_deposit(256 * 1) + .monied(true) + .build() + .execute_with(|| { + System::inc_account_nonce(&2); + + let log_entries = vec![LogEntry { + address: Address::from_str("ad52e0f67b6f44cd5b9a6f4fbc7c0f78f37e094b").unwrap(), + topics: vec![ + H256::from(hex!("6775ce244ff81f0a82f87d6fd2cf885affb38416e3a04355f713c6f008dd126a")), + H256::from(hex!("0000000000000000000000000000000000000000000000000000000000000006")), + H256::from(hex!("0000000000000000000000000000000000000000000000000000000000000000")), + ], + data: "00000000000000000000000074241db5f3ebaeecf9506e4ae9881860933416048eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000000000000000000000000000000000000000000000000002386f26fc10000".from_hex().unwrap(), + }]; + + let receipt = Receipt::new( + TransactionOutcome::StatusCode(1), +// TransactionOutcome::StateRoot(H256::from(hex!("a21cdf375ebef58f606c298d6211f4edee58f2dd6430edbdd0ed3cd886a16863"))), + U256::from(U128::from(1123401)), + log_entries + ); + + + let proof_record = ActionRecord { + index: 25, + proof: "f904c4f904c1b8b3f8b1a0c75e4fe93609c5f088e180e294577ba0f991fcad25e6163523adba4bfc65cfa8a008d8d33daaf581590c70f28317e5a48c33786ee092d7d9a9b4faae64fd05339ba0562b932c3332c149c7449d68be351f41c947c5f4b6d336906970f361dc905c67a0da77a1e9b271dcaaf156d5528be7e6c586930feab5d0e644208c0b8e54eed21780808080a0e58215be848c1293dd381210359d84485553000a82b67410406d183b42adbbdd8080808080808080b90214f90211a08fd1196d29f53e148b7cd38b1143b132d8f9bd4a9c5a2ad51244de514b5b5f19a0a6d91f439a4b87ec5861732d4900baa7df91c8b2f0f02eb9c0e640269adcae3da00cbe602772266b03258721442dd7327eb996fb2eef54b4fbe77c9b57053dd3f5a0e412c05734ae17fa87154402c9737bfd800f44aa3df0ef32fe56092214868b87a0a60ac628f42d20e1dee3d479c192b74ceacbb7d571a93750132c536328b031a6a03518806a81c734f33fe971a22721c12f2f3cc60d7f9b3bc89403d7cfdb5d0895a0d130ed44f0def9f86a53d3e3720615cec6f6f0aedecd4fc0cb2649c766ca1a17a0d421bfc8d9f46e123e432b8582c49629a969547a8ef40b231659b8385c7c1b81a09a62e4ae73121a710ba5353172874f248df38f39ceaef351522c4a9b1cffb1c3a09f4604347f9ba2c30703cce323c9f9705e0edecf5c1061e634a792de9a854e00a015421788d874414ca073e71d99c5fab4acd350b46551a48aa29891d322651071a0a1f624aded3a70996b4117dc609e5fbdd1bbdc819935be31a395904a1f85982aa0a69eb11de6f2d70d0ab095da5ba88f38cd9a60569839ecf35103360603d9aa2da02564a45d7661a773b13f984a47c63017fcea8599b39f42df99d1132d9cf2c159a0ff8b9f7b23ffe706af9188e74da6ad7ead36ba7d75c47ef915541689cc025194a094974e354978838330aeefefe0e29fa2e86cab1f4503b1b895f889514f48aa0e80b901f2f901ef20b901ebf901e80183112449b9010000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000820000000000000020000000000000000000800000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000200000000020000000000000000000000000000080000000000000800000000000000000000000f8def8dc94ad52e0f67b6f44cd5b9a6f4fbc7c0f78f37e094bf863a06775ce244ff81f0a82f87d6fd2cf885affb38416e3a04355f713c6f008dd126aa00000000000000000000000000000000000000000000000000000000000000006a00000000000000000000000000000000000000000000000000000000000000000b86000000000000000000000000074241db5f3ebaeecf9506e4ae9881860933416048eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000000000000000000000000000000000000000000000000002386f26fc10000".from_hex().unwrap(), + header_hash: H256::from(hex!("f1a5bc27877e219b859b0bb1f2f440134553019f9bb5a2eca7a4703263e736c9")) + }; + +// let proof: Proof = rlp::decode(&proof_record.proof).unwrap(); + + let mixh = H256::from(hex!("5a85e328a8bb041a386ffb25db029b7f0df4665a8a55b331b30a576761404fa6")); + let nonce = H64::from(hex!("650ea83006bb108d")); + + let header = EthHeader { + parent_hash: H256::from(hex!("91553997d11a1d978f2ea363f230f5f525aee914a726d01e1deb4ea51de315cd")), + timestamp: 1573560715, + number: 6760579, + author: Address::from(hex!("d7a15baeb7ea05c9660cbe03fb7999c2c2e57625")), + transactions_root: H256::from(hex!("c2b9e612bdac9d73d53ab38cafa959e5703dc078a9d5b184c65ee38bc471b5bf")), + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "41746c616e7469632043727970746f".from_hex().unwrap(), + state_root: H256::from(hex!("a21cdf375ebef58f606c298d6211f4edee58f2dd6430edbdd0ed3cd886a16863")), + receipts_root: H256::from(hex!("4c573edd96da310fefc3ced2d70831173e4684728c963330d990cf360aed8550")), + log_bloom: Bloom::from_str("040000411080018200400100100020100808080020130000004000000a80040000001000000400004010800004811000000000800604002004000000002300820008181000000a820142010c0000010418030040080010080010280018200408000020800208120100000000001828000000000200000800000080511508c0008004100482000800040080000411409000000d20400000056000000802400006420002801000108140202100000804109008000150800140000020290028404000040102800000002000020000811004020080008000100411300100422420060210100100110124080000800084022021000200808005500000000000012000").unwrap(), + gas_used: 0x220d13.into(), + gas_limit: 0x7a121d.into(), + difficulty: 0x269921540_u64.into(), + seal: vec![rlp::encode(&mixh), rlp::encode(&nonce)], + hash: Some(H256::from(hex!("f1a5bc27877e219b859b0bb1f2f440134553019f9bb5a2eca7a4703263e736c9"))), + }; + + EthRelay::genesis_header(&header, 0x624c22d93f8e59_u64); + + assert_eq!(EthRelay::verify_receipt(&proof_record), Some(receipt)); + }); +} + +#[test] +fn relay_header() { + ExtBuilder::default().monied(true).build().execute_with(|| { + // 6760579 + let mixh1 = H256::from(hex!("5a85e328a8bb041a386ffb25db029b7f0df4665a8a55b331b30a576761404fa6")); + let nonce1 = H64::from(hex!("650ea83006bb108d")); + + let header1 = EthHeader { + parent_hash: H256::from(hex!("91553997d11a1d978f2ea363f230f5f525aee914a726d01e1deb4ea51de315cd")), + timestamp: 1573560715, + number: 6760579, + author: Address::from(hex!("d7a15baeb7ea05c9660cbe03fb7999c2c2e57625")), + transactions_root: H256::from(hex!("c2b9e612bdac9d73d53ab38cafa959e5703dc078a9d5b184c65ee38bc471b5bf")), + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "41746c616e7469632043727970746f".from_hex().unwrap(), + state_root: H256::from(hex!("a21cdf375ebef58f606c298d6211f4edee58f2dd6430edbdd0ed3cd886a16863")), + receipts_root: H256::from(hex!("4c573edd96da310fefc3ced2d70831173e4684728c963330d990cf360aed8550")), + log_bloom: Bloom::from_str("040000411080018200400100100020100808080020130000004000000a80040000001000000400004010800004811000000000800604002004000000002300820008181000000a820142010c0000010418030040080010080010280018200408000020800208120100000000001828000000000200000800000080511508c0008004100482000800040080000411409000000d20400000056000000802400006420002801000108140202100000804109008000150800140000020290028404000040102800000002000020000811004020080008000100411300100422420060210100100110124080000800084022021000200808005500000000000012000").unwrap(), + gas_used: 0x220d13.into(), + gas_limit: 0x7a121d.into(), + difficulty: 0x269921540_u64.into(), + seal: vec![rlp::encode(&mixh1), rlp::encode(&nonce1)], + hash: Some(H256::from(hex!("f1a5bc27877e219b859b0bb1f2f440134553019f9bb5a2eca7a4703263e736c9"))), + }; + + + // 6760580 + let mixh2 = H256::from(hex!("e06f0c107dcc91e9e82de0b42d0e22d5c2cfae5209422fda88cff4f810f4bffb")); + let nonce2 = H64::from(hex!("9348d06003756cff")); + + let header2 = EthHeader { + parent_hash: H256::from(hex!("f1a5bc27877e219b859b0bb1f2f440134553019f9bb5a2eca7a4703263e736c9")), + timestamp: 0x5dcaa1a3, + number: 6760580, + author: Address::from(hex!("4ccfb3039b78d3938588157564c9ad559bafab94")), + transactions_root: H256::from(hex!("bd4f8075fcdf01d3be2b8ae4a0a7195107429f34361e278e8760cc0f08e35d7a")), + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "d983010906846765746889676f312e31312e3133856c696e7578".from_hex().unwrap(), + state_root: H256::from(hex!("694af9f7dc9866ec99dd83ef846778552cb60659e9cbd6e77e800816da83c3c9")), + receipts_root: H256::from(hex!("729394331d204a175e4c1938ae19cc905107d8fd5562ee5283c323cde6b82e23")), + log_bloom: Bloom::from_str("0400000000000100001000100000040000000100000000000000000002040080002004000000000200000000000210000080000002000080000000040014000000000000040020000000000800020040080110000004008800000000000000000100000002000000000000000000080040000000000004000010801101000000000000000000000000000000020060000000001000020000200002000000100000000000000000001000010000000000000001000080000000011000002040401000001280000000000021000800000800000000000010000000000040006000000400200000000000000000000000000000000000c000100000400000800100").unwrap(), + gas_used: 0x17231e.into(), + gas_limit: 0x7a1200.into(), + difficulty: 0x2694562fe_u64.into(), + seal: vec![rlp::encode(&mixh2), rlp::encode(&nonce2)], + hash: Some(H256::from(hex!("12734378d3e4ad7050f7baf629d6eda161e911865d77c10e44c1f7e8e31fd7a7"))), + }; + + + EthRelay::genesis_header(&header1, 0x624c22d93f8e59_u64); + + EthRelay::verify_header(&header2).expect("Verify Failed."); + + EthRelay::store_header(&header2).expect("Store Failed."); + + + // 6760581 + let mixh3 = H256::from(hex!("019b6a52120a8769d34fe6348bdfa400ab4886576287f5ef11d9105875280c7e")); + let nonce3 = H64::from(hex!("f43d6b58a23b7065")); + + let header3 = EthHeader { + parent_hash: H256::from(hex!("12734378d3e4ad7050f7baf629d6eda161e911865d77c10e44c1f7e8e31fd7a7")), + timestamp: 0x5dcaa1ae, + number: 6760581, + author: Address::from(hex!("d7a15baeb7ea05c9660cbe03fb7999c2c2e57625")), + transactions_root: H256::from(hex!("aaccb1d4b2dc847eefa50681d3096522a41f7c27031ead7a0ad51b50632218dc")), + uncles_hash: H256::from(hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + extra_data: "41746c616e7469632043727970746f".from_hex().unwrap(), + state_root: H256::from(hex!("8106951604cc1305eedb3b7df1c2cf9c2d0ba9e792f645386d3a2fdffd2e9d96")), + receipts_root: H256::from(hex!("e39a6c035914d6544db6d3653101740625e7608c747ea87b9784261e5d94a7ea")), + log_bloom: Bloom::from_str("00000000000001000000000000000000000000000000000000000000000000000000000000000020000000000000000000200020400000000000000000000000000000000000000000000008000000000000080000000000000000000200000000000000000000000000000000008100000000000000000000000010010000000000020000000000000000000000040000000010040000002000204000000000000000000000000000000100000000000000000000000050002000000000000000800002800000000400000000000000000040000000100000000200000000080000000400002000000000000000000000002000000000000000000002020000").unwrap(), + gas_used: 0x3ea15.into(), + gas_limit: 0x7a121d.into(), + difficulty: 0x26945e2fe_u64.into(), + seal: vec![rlp::encode(&mixh3), rlp::encode(&nonce3)], + hash: Some(H256::from(hex!("c86b090d12fa61c34f075530618e40a89654d8d85ac6aaa26149fb56b596a15a"))), + }; + + EthRelay::verify_header(&header3).expect("Verify Failed."); + + EthRelay::store_header(&header3).expect("Store Failed."); + }); +}