From 06df521efffb2b71c9408eab92f4e973b1e12c08 Mon Sep 17 00:00:00 2001 From: Artem Vorotnikov Date: Wed, 19 Feb 2020 16:42:52 +0300 Subject: [PATCH] Implement eth/64 (EIP-2364) and drop support for eth/62 (#11472) * sync: make code friendlier to future conditional parsing * Implement eth/64 (EIP-2364) and drop support for eth/62 --- Cargo.lock | 28 +- Cargo.toml | 1 - ethcore/spec/Cargo.toml | 1 + ethcore/spec/src/spec.rs | 73 ++++- ethcore/sync/Cargo.toml | 1 + ethcore/sync/src/api.rs | 16 +- ethcore/sync/src/chain/fork_filter.rs | 143 ++++++++++ ethcore/sync/src/chain/handler.rs | 43 ++- ethcore/sync/src/chain/mod.rs | 29 +- ethcore/sync/src/chain/propagator.rs | 13 +- ethcore/sync/src/tests/helpers.rs | 10 +- parity/modules.rs | 5 +- parity/run.rs | 2 + rpc/src/v1/tests/helpers/sync_provider.rs | 10 +- rpc/src/v1/tests/mocked/eth.rs | 2 +- rpc/src/v1/tests/mocked/parity.rs | 2 +- util/EIP-2124/Cargo.toml | 21 -- util/EIP-2124/src/lib.rs | 328 ---------------------- 18 files changed, 318 insertions(+), 410 deletions(-) create mode 100644 ethcore/sync/src/chain/fork_filter.rs delete mode 100644 util/EIP-2124/Cargo.toml delete mode 100644 util/EIP-2124/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index f92bc58bb..10a6e8510 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,18 +999,6 @@ dependencies = [ "rustc-hex 2.1.0", ] -[[package]] -name = "eip-2124" -version = "0.1.0" -dependencies = [ - "crc", - "ethereum-types", - "hex-literal", - "maplit", - "rlp", - "rlp-derive", -] - [[package]] name = "eip-712" version = "0.1.1" @@ -1631,6 +1619,7 @@ dependencies = [ "ethcore-network", "ethcore-network-devp2p", "ethcore-private-tx", + "ethereum-forkid", "ethereum-types", "fastmap", "futures", @@ -1654,6 +1643,20 @@ dependencies = [ "triehash-ethereum", ] +[[package]] +name = "ethereum-forkid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2f547713a9a1e69a55529f3981dbded2c59c69629c7df795bdc7deef3375f59" +dependencies = [ + "crc", + "ethereum-types", + "maplit", + "parity-util-mem", + "rlp", + "rlp-derive", +] + [[package]] name = "ethereum-types" version = "0.8.0" @@ -4798,6 +4801,7 @@ dependencies = [ "kvdb-memorydb", "log", "machine", + "maplit", "null-engine", "parity-bytes", "pod", diff --git a/Cargo.toml b/Cargo.toml index 69eed9c16..859cb3980 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,5 +128,4 @@ members = [ "chainspec", "ethcore/wasm/run", "evmbin", - "util/EIP-2124" ] diff --git a/ethcore/spec/Cargo.toml b/ethcore/spec/Cargo.toml index 8fa2c7387..ef8158245 100644 --- a/ethcore/spec/Cargo.toml +++ b/ethcore/spec/Cargo.toml @@ -28,6 +28,7 @@ keccak-hash = "0.4.0" kvdb-memorydb = "0.4.0" log = "0.4.8" machine = { path = "../machine" } +maplit = "1" null-engine = { path = "../engines/null-engine" } pod = { path = "../pod" } rlp = "0.4.2" diff --git a/ethcore/spec/src/spec.rs b/ethcore/spec/src/spec.rs index 1116fc626..da45c3b8c 100644 --- a/ethcore/spec/src/spec.rs +++ b/ethcore/spec/src/spec.rs @@ -17,7 +17,7 @@ //! Parameters for a block chain. use std::{ - collections::BTreeMap, + collections::{BTreeMap, BTreeSet}, convert::TryFrom, fmt, io::Read, @@ -47,6 +47,7 @@ use instant_seal::{InstantSeal, InstantSealParams}; use keccak_hash::{KECCAK_NULL_RLP, keccak}; use log::{trace, warn}; use machine::{executive::Executive, Machine, substate::Substate}; +use maplit::btreeset; use null_engine::NullEngine; use pod::PodState; use rlp::{Rlp, RlpStream}; @@ -218,6 +219,8 @@ pub struct Spec { pub seal_rlp: Bytes, /// Hardcoded synchronization. Allows the light client to immediately jump to a specific block. pub hardcoded_sync: Option, + /// List of hard forks in the network. + pub hard_forks: BTreeSet, /// Contract constructors to be executed on genesis. pub constructors: Vec<(Address, Bytes)>, /// May be pre-populated if we know this in advance. @@ -280,7 +283,7 @@ fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result Result, - ) -> Arc { + ) -> (Arc, BTreeSet) { + let mut hard_forks = btreeset![ + params.eip150_transition, + params.eip160_transition, + params.eip161abc_transition, + params.eip161d_transition, + params.eip98_transition, + params.eip658_transition, + params.eip155_transition, + params.validate_receipts_transition, + params.validate_chain_id_transition, + params.eip140_transition, + params.eip210_transition, + params.eip211_transition, + params.eip214_transition, + params.eip145_transition, + params.eip1052_transition, + params.eip1283_transition, + params.eip1283_disable_transition, + params.eip1283_reenable_transition, + params.eip1014_transition, + params.eip1706_transition, + params.eip1344_transition, + params.eip1884_transition, + params.eip2028_transition, + params.eip2200_advance_transition, + params.dust_protection_transition, + params.wasm_activation_transition, + params.kip4_transition, + params.kip6_transition, + params.max_code_size_transition, + params.transaction_permission_contract_transition, + ]; + // BUG: Rinkeby has homestead transition at block 1 but we can't reflect that in specs for non-Ethash networks + if params.network_id == 0x4 { + hard_forks.insert(1); + } + let machine = Self::machine(&engine_spec, params, builtins); - match engine_spec { + let engine: Arc = match engine_spec { ethjson::spec::Engine::Null(null) => Arc::new(NullEngine::new(null.params.into(), machine)), - ethjson::spec::Engine::Ethash(ethash) => Arc::new(Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)), + ethjson::spec::Engine::Ethash(ethash) => { + // Specific transitions for Ethash-based networks + for block in &[ethash.params.homestead_transition, ethash.params.dao_hardfork_transition] { + if let Some(block) = *block { + hard_forks.insert(block.into()); + } + } + + // Ethereum's difficulty bomb delay is a fork too + if let Some(delays) = ðash.params.difficulty_bomb_delays { + for delay in delays.keys().copied() { + hard_forks.insert(delay.into()); + } + } + Arc::new(Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)) + }, ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => Arc::new(InstantSeal::new(instant_seal.params.into(), machine)), ethjson::spec::Engine::InstantSeal(None) => Arc::new(InstantSeal::new(InstantSealParams::default(), machine)), ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)), @@ -360,7 +416,12 @@ impl Spec { .expect("Failed to start Clique consensus engine."), ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine) .expect("Failed to start AuthorityRound consensus engine."), - } + }; + + // Dummy value is a filler for non-existent transitions + hard_forks.remove(&BlockNumber::max_value()); + + (engine, hard_forks) } /// Get common blockchain parameters. diff --git a/ethcore/sync/Cargo.toml b/ethcore/sync/Cargo.toml index 40ced6ee3..36fe32ed0 100644 --- a/ethcore/sync/Cargo.toml +++ b/ethcore/sync/Cargo.toml @@ -16,6 +16,7 @@ devp2p = { package = "ethcore-network-devp2p", path = "../../util/network-devp2p enum_primitive = "0.1.1" ethcore-io = { path = "../../util/io" } ethcore-private-tx = { path = "../private-tx" } +ethereum-forkid = "0.1" ethereum-types = "0.8.0" fastmap = { path = "../../util/fastmap" } futures = "0.1" diff --git a/ethcore/sync/src/api.rs b/ethcore/sync/src/api.rs index ba00b5813..433393b34 100644 --- a/ethcore/sync/src/api.rs +++ b/ethcore/sync/src/api.rs @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use std::sync::{Arc, mpsc, atomic}; -use std::collections::{HashMap, BTreeMap}; +use std::collections::{BTreeSet, HashMap, BTreeMap}; use std::io; use std::ops::RangeInclusive; use std::time::Duration; @@ -27,10 +27,11 @@ use crate::sync_io::NetSyncIo; use crate::light_sync::{self, SyncInfo}; use crate::private_tx::PrivateTxHandler; use crate::chain::{ + fork_filter::ForkFilterApi, sync_packet::SyncPacket::{PrivateTransactionPacket, SignedPrivateTransactionPacket}, - ChainSyncApi, SyncState, SyncStatus as EthSyncStatus, ETH_PROTOCOL_VERSION_62, - ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, - PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4, + ChainSyncApi, SyncState, SyncStatus as EthSyncStatus, + ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, + PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4, }; use bytes::Bytes; @@ -268,6 +269,8 @@ pub struct Params { pub executor: Executor, /// Blockchain client. pub chain: Arc, + /// Forks. + pub forks: BTreeSet, /// Snapshot service. pub snapshot_service: Arc, /// Private tx service. @@ -348,10 +351,13 @@ impl EthSync { }) }; + let fork_filter = ForkFilterApi::new(&*params.chain, params.forks); + let (priority_tasks_tx, priority_tasks_rx) = mpsc::channel(); let sync = ChainSyncApi::new( params.config, &*params.chain, + fork_filter, params.private_tx_handler.as_ref().cloned(), priority_tasks_rx, ); @@ -605,7 +611,7 @@ impl ChainNotify for EthSync { _ => {}, } - self.network.register_protocol(self.eth_handler.clone(), self.subprotocol_name, &[ETH_PROTOCOL_VERSION_62, ETH_PROTOCOL_VERSION_63]) + self.network.register_protocol(self.eth_handler.clone(), self.subprotocol_name, &[ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64]) .unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e)); // register the warp sync subprotocol self.network.register_protocol(self.eth_handler.clone(), WARP_SYNC_PROTOCOL_ID, &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4]) diff --git a/ethcore/sync/src/chain/fork_filter.rs b/ethcore/sync/src/chain/fork_filter.rs new file mode 100644 index 000000000..4a4c706df --- /dev/null +++ b/ethcore/sync/src/chain/fork_filter.rs @@ -0,0 +1,143 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see . + +//! This module contains a wrapper that connects this codebase with `ethereum-forkid` crate which provides `FORK_ID` +//! to support Ethereum network protocol, version 64 and above. + +// Re-export ethereum-forkid crate contents here. +pub use ethereum_forkid::{BlockNumber, ForkId, RejectReason}; + +use client_traits::ChainInfo; +use ethereum_forkid::ForkFilter; +use parity_util_mem::MallocSizeOf; + +/// Wrapper around fork filter that provides integration with `ForkFilter`. +#[derive(MallocSizeOf)] +pub struct ForkFilterApi { + inner: ForkFilter, +} + +impl ForkFilterApi { + /// Create `ForkFilterApi` from `ChainInfo` and an `Iterator` over the hard forks. + pub fn new>(client: &C, forks: I) -> Self { + let chain_info = client.chain_info(); + Self { + inner: ForkFilter::new(chain_info.best_block_number, chain_info.genesis_hash, forks), + } + } + + #[cfg(test)] + /// Dummy version of ForkFilterApi with no forks. + pub fn new_dummy(client: &C) -> Self { + let chain_info = client.chain_info(); + Self { + inner: ForkFilter::new(chain_info.best_block_number, chain_info.genesis_hash, vec![]), + } + } + + fn update_head(&mut self, client: &C) { + self.inner.set_head(client.chain_info().best_block_number); + } + + /// Wrapper for `ForkFilter::current` + pub fn current(&mut self, client: &C) -> ForkId { + self.update_head(client); + self.inner.current() + } + + /// Wrapper for `ForkFilter::is_compatible` + pub fn is_compatible(&mut self, client: &C, fork_id: ForkId) -> Result<(), RejectReason> { + self.update_head(client); + self.inner.is_compatible(fork_id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use spec::Spec; + use ethcore::test_helpers::TestBlockChainClient; + + fn test_spec Spec>(spec_builder: F, forks: Vec) { + let spec = (spec_builder)(); + let genesis_hash = spec.genesis_header().hash(); + let spec_forks = spec.hard_forks.clone(); + let client = TestBlockChainClient::new_with_spec(spec); + + assert_eq!( + ForkFilterApi::new(&client, spec_forks).inner, + ForkFilter::new(0, genesis_hash, forks) + ); + } + + #[test] + fn ethereum_spec() { + test_spec( + || spec::new_foundation(&String::new()), + vec![ + 1_150_000, + 1_920_000, + 2_463_000, + 2_675_000, + 4_370_000, + 7_280_000, + 9_069_000, + 9_200_000, + ], + ) + } + + #[test] + fn ropsten_spec() { + test_spec( + || spec::new_ropsten(&String::new()), + vec![ + 10, + 1_700_000, + 4_230_000, + 4_939_394, + 6_485_846, + 7_117_117, + ], + ) + } + + #[test] + fn rinkeby_spec() { + test_spec( + || spec::new_rinkeby(&String::new()), + vec![ + 1, + 2, + 3, + 1_035_301, + 3_660_663, + 4_321_234, + 5_435_345, + ], + ) + } + + #[test] + fn goerli_spec() { + test_spec( + || spec::new_goerli(&String::new()), + vec![ + 1_561_651, + ], + ) + } +} diff --git a/ethcore/sync/src/chain/handler.rs b/ethcore/sync/src/chain/handler.rs index 9716db151..2067c2c9e 100644 --- a/ethcore/sync/src/chain/handler.rs +++ b/ethcore/sync/src/chain/handler.rs @@ -20,7 +20,7 @@ use std::{mem, cmp}; use crate::{ snapshot_sync::ChunkType, sync_io::SyncIo, - api::WARP_SYNC_PROTOCOL_ID, + api::{ETH_PROTOCOL, WARP_SYNC_PROTOCOL_ID}, block_sync::{BlockDownloaderImportError as DownloaderImportError, DownloadAction}, chain::{ sync_packet::{ @@ -32,7 +32,7 @@ use crate::{ } }, BlockSet, ChainSync, ForkConfirmation, PacketDecodeError, PeerAsking, PeerInfo, SyncRequester, - SyncState, ETH_PROTOCOL_VERSION_62, ETH_PROTOCOL_VERSION_63, MAX_NEW_BLOCK_AGE, MAX_NEW_HASHES, + SyncState, ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, MAX_NEW_BLOCK_AGE, MAX_NEW_HASHES, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4, } }; @@ -562,17 +562,34 @@ impl SyncHandler { /// Called by peer to report status fn on_peer_status(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { + let mut r = r.iter(); sync.handshaking_peers.remove(&peer_id); - let protocol_version: u8 = r.val_at(0)?; + let protocol_version: u8 = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + let eth_protocol_version = io.protocol_version(Ð_PROTOCOL, peer_id); let warp_protocol_version = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer_id); let warp_protocol = warp_protocol_version != 0; let private_tx_protocol = warp_protocol_version >= PAR_PROTOCOL_VERSION_3.0; + let network_id = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + let difficulty = Some(r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?); + let latest_hash = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + let genesis = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + let forkid_validation_error = { + if eth_protocol_version >= ETH_PROTOCOL_VERSION_64.0 { + let fork_id = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + sync.fork_filter.is_compatible(io.chain(), fork_id).err().map(|e| (fork_id, e)) + } else { + None + } + }; + let snapshot_hash = if warp_protocol { Some(r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?) } else { None }; + let snapshot_number = if warp_protocol { Some(r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?) } else { None }; + let private_tx_enabled = if private_tx_protocol { r.next().and_then(|v| v.as_val().ok()).unwrap_or(false) } else { false }; let peer = PeerInfo { protocol_version, - network_id: r.val_at(1)?, - difficulty: Some(r.val_at(2)?), - latest_hash: r.val_at(3)?, - genesis: r.val_at(4)?, + network_id, + difficulty, + latest_hash, + genesis, asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, @@ -583,10 +600,10 @@ impl SyncHandler { expired: false, confirmation: if sync.fork_block.is_none() { ForkConfirmation::Confirmed } else { ForkConfirmation::Unconfirmed }, asking_snapshot_data: None, - snapshot_hash: if warp_protocol { Some(r.val_at(5)?) } else { None }, - snapshot_number: if warp_protocol { Some(r.val_at(6)?) } else { None }, + snapshot_hash, + snapshot_number, block_set: None, - private_tx_enabled: if private_tx_protocol { r.val_at(7).unwrap_or(false) } else { false }, + private_tx_enabled, client_version: ClientVersion::from(io.peer_version(peer_id)), }; @@ -627,10 +644,14 @@ impl SyncHandler { trace!(target: "sync", "Peer {} network id mismatch (ours: {}, theirs: {})", peer_id, sync.network_id, peer.network_id); return Err(DownloaderImportError::Invalid); } + if let Some((fork_id, reason)) = forkid_validation_error { + trace!(target: "sync", "Peer {} incompatible fork id (fork id: {:#x}/{}, error: {:?})", peer_id, fork_id.hash.0, fork_id.next, reason); + return Err(DownloaderImportError::Invalid); + } if false || (warp_protocol && (peer.protocol_version < PAR_PROTOCOL_VERSION_1.0 || peer.protocol_version > PAR_PROTOCOL_VERSION_4.0)) - || (!warp_protocol && (peer.protocol_version < ETH_PROTOCOL_VERSION_62.0 || peer.protocol_version > ETH_PROTOCOL_VERSION_63.0)) + || (!warp_protocol && (peer.protocol_version < ETH_PROTOCOL_VERSION_63.0 || peer.protocol_version > ETH_PROTOCOL_VERSION_64.0)) { trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version); return Err(DownloaderImportError::Invalid); diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs index 2b506c04f..998a56ccd 100644 --- a/ethcore/sync/src/chain/mod.rs +++ b/ethcore/sync/src/chain/mod.rs @@ -92,6 +92,7 @@ mod propagator; mod requester; mod supplier; +pub mod fork_filter; pub mod sync_packet; use std::sync::{Arc, mpsc}; @@ -100,9 +101,10 @@ use std::cmp; use std::time::{Duration, Instant}; use crate::{ - EthProtocolInfo as PeerInfoDigest, PriorityTask, SyncConfig, WarpSync, WARP_SYNC_PROTOCOL_ID, + ETH_PROTOCOL, EthProtocolInfo as PeerInfoDigest, PriorityTask, SyncConfig, WarpSync, WARP_SYNC_PROTOCOL_ID, api::{Notification, PRIORITY_TIMER_INTERVAL}, block_sync::{BlockDownloader, DownloadAction}, + chain::fork_filter::ForkFilterApi, sync_io::SyncIo, snapshot_sync::Snapshot, transactions_stats::{TransactionsStats, Stats as TransactionStats}, @@ -147,10 +149,10 @@ malloc_size_of_is_0!(PeerInfo); pub type PacketDecodeError = DecoderError; -/// 63 version of Ethereum protocol. +/// Version 64 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count). +pub const ETH_PROTOCOL_VERSION_64: (u8, u8) = (64, 0x11); +/// Version 63 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count). pub const ETH_PROTOCOL_VERSION_63: (u8, u8) = (63, 0x11); -/// 62 version of Ethereum protocol. -pub const ETH_PROTOCOL_VERSION_62: (u8, u8) = (62, 0x11); /// 1 version of Parity protocol and the packet count. pub const PAR_PROTOCOL_VERSION_1: (u8, u8) = (1, 0x15); /// 2 version of Parity protocol (consensus messages added). @@ -427,11 +429,12 @@ impl ChainSyncApi { pub fn new( config: SyncConfig, chain: &dyn BlockChainClient, + fork_filter: ForkFilterApi, private_tx_handler: Option>, priority_tasks: mpsc::Receiver, ) -> Self { ChainSyncApi { - sync: RwLock::new(ChainSync::new(config, chain, private_tx_handler)), + sync: RwLock::new(ChainSync::new(config, chain, fork_filter, private_tx_handler)), priority_tasks: Mutex::new(priority_tasks), } } @@ -660,6 +663,8 @@ pub struct ChainSync { last_sent_block_number: BlockNumber, /// Network ID network_id: u64, + /// Fork filter + fork_filter: ForkFilterApi, /// Optional fork block to check fork_block: Option<(BlockNumber, H256)>, /// Snapshot downloader. @@ -688,6 +693,7 @@ impl ChainSync { pub fn new( config: SyncConfig, chain: &dyn BlockChainClient, + fork_filter: ForkFilterApi, private_tx_handler: Option>, ) -> Self { let chain_info = chain.chain_info(); @@ -705,6 +711,7 @@ impl ChainSync { old_blocks: None, last_sent_block_number: 0, network_id: config.network_id, + fork_filter, fork_block: config.fork_block, download_old_blocks: config.download_old_blocks, snapshot: Snapshot::new(), @@ -723,7 +730,7 @@ impl ChainSync { let last_imported_number = self.new_blocks.last_imported_block_number(); SyncStatus { state: self.state.clone(), - protocol_version: ETH_PROTOCOL_VERSION_63.0, + protocol_version: ETH_PROTOCOL_VERSION_64.0, network_id: self.network_id, start_block_number: self.starting_block, last_imported_block_number: Some(last_imported_number), @@ -1240,10 +1247,11 @@ impl ChainSync { /// Send Status message fn send_status(&mut self, io: &mut dyn SyncIo, peer: PeerId) -> Result<(), network::Error> { + let eth_protocol_version = io.protocol_version(Ð_PROTOCOL, peer); let warp_protocol_version = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer); let warp_protocol = warp_protocol_version != 0; let private_tx_protocol = warp_protocol_version >= PAR_PROTOCOL_VERSION_3.0; - let protocol = if warp_protocol { warp_protocol_version } else { ETH_PROTOCOL_VERSION_63.0 }; + let protocol = if warp_protocol { warp_protocol_version } else { eth_protocol_version }; trace!(target: "sync", "Sending status to {}, protocol version {}", peer, protocol); let mut packet = RlpStream::new(); packet.begin_unbounded_list(); @@ -1253,6 +1261,9 @@ impl ChainSync { packet.append(&chain.total_difficulty); packet.append(&chain.best_block_hash); packet.append(&chain.genesis_hash); + if eth_protocol_version >= ETH_PROTOCOL_VERSION_64.0 { + packet.append(&self.fork_filter.current(io.chain())); + } if warp_protocol { let manifest = io.snapshot_service().manifest(); let block_number = manifest.as_ref().map_or(0, |m| m.block_number); @@ -1499,6 +1510,7 @@ pub mod tests { use crate::{ api::SyncConfig, + chain::ForkFilterApi, tests::{helpers::TestIo, snapshot::TestSnapshotService}, }; @@ -1594,8 +1606,7 @@ pub mod tests { } pub fn dummy_sync_with_peer(peer_latest_hash: H256, client: &dyn BlockChainClient) -> ChainSync { - - let mut sync = ChainSync::new(SyncConfig::default(), client, None,); + let mut sync = ChainSync::new(SyncConfig::default(), client, ForkFilterApi::new_dummy(client), None,); insert_dummy_peer(&mut sync, 0, peer_latest_hash); sync } diff --git a/ethcore/sync/src/chain/propagator.rs b/ethcore/sync/src/chain/propagator.rs index 2066b7f65..1f5667aca 100644 --- a/ethcore/sync/src/chain/propagator.rs +++ b/ethcore/sync/src/chain/propagator.rs @@ -338,7 +338,10 @@ mod tests { use crate::{ api::SyncConfig, - chain::{ChainSync, ForkConfirmation, PeerAsking, PeerInfo}, + chain::{ + fork_filter::ForkFilterApi, + ChainSync, ForkConfirmation, PeerAsking, PeerInfo + }, tests::{helpers::TestIo, snapshot::TestSnapshotService}, }; @@ -423,7 +426,7 @@ mod tests { client.add_blocks(2, EachBlockWith::Uncle); let queue = RwLock::new(VecDeque::new()); let block = client.block(BlockId::Latest).unwrap().into_inner(); - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); + let mut sync = ChainSync::new(SyncConfig::default(), &client, ForkFilterApi::new_dummy(&client), None); sync.peers.insert(0, PeerInfo { // Messaging protocol @@ -514,7 +517,7 @@ mod tests { client.add_blocks(100, EachBlockWith::Uncle); client.insert_transaction_to_queue(); // Sync with no peers - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); + let mut sync = ChainSync::new(SyncConfig::default(), &client, ForkFilterApi::new_dummy(&client), None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); let mut io = TestIo::new(&mut client, &ss, &queue, None, None); @@ -584,7 +587,7 @@ mod tests { let mut client = TestBlockChainClient::new(); client.insert_transaction_with_gas_price_to_queue(U256::zero()); let block_hash = client.block_hash_delta_minus(1); - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); + let mut sync = ChainSync::new(SyncConfig::default(), &client, ForkFilterApi::new_dummy(&client), None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); let mut io = TestIo::new(&mut client, &ss, &queue, None, None); @@ -614,7 +617,7 @@ mod tests { let tx1_hash = client.insert_transaction_to_queue(); let tx2_hash = client.insert_transaction_with_gas_price_to_queue(U256::zero()); let block_hash = client.block_hash_delta_minus(1); - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); + let mut sync = ChainSync::new(SyncConfig::default(), &client, ForkFilterApi::new_dummy(&client), None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); let mut io = TestIo::new(&mut client, &ss, &queue, None, None); diff --git a/ethcore/sync/src/tests/helpers.rs b/ethcore/sync/src/tests/helpers.rs index 4ca63671d..f6afa3e0a 100644 --- a/ethcore/sync/src/tests/helpers.rs +++ b/ethcore/sync/src/tests/helpers.rs @@ -20,11 +20,12 @@ use std::sync::Arc; use crate::{ api::{SyncConfig, WARP_SYNC_PROTOCOL_ID}, chain::{ + fork_filter::ForkFilterApi, sync_packet::{ PacketInfo, SyncPacket::{self, PrivateTransactionPacket, SignedPrivateTransactionPacket} }, - ChainSync, SyncSupplier, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_4 + ChainSync, SyncSupplier, ETH_PROTOCOL_VERSION_64, PAR_PROTOCOL_VERSION_4 }, private_tx::SimplePrivateTxHandler, sync_io::SyncIo, @@ -156,7 +157,7 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { } fn protocol_version(&self, protocol: &ProtocolId, _peer_id: PeerId) -> u8 { - if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_4.0 } else { ETH_PROTOCOL_VERSION_63.0 } + if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_4.0 } else { ETH_PROTOCOL_VERSION_64.0 } } fn is_expired(&self) -> bool { @@ -380,7 +381,7 @@ impl TestNet> { let chain = TestBlockChainClient::new(); let ss = Arc::new(TestSnapshotService::new()); let private_tx_handler = Arc::new(SimplePrivateTxHandler::default()); - let sync = ChainSync::new(config.clone(), &chain, Some(private_tx_handler.clone())); + let sync = ChainSync::new(config.clone(), &chain, ForkFilterApi::new_dummy(&chain), Some(private_tx_handler.clone())); net.peers.push(Arc::new(EthPeer { sync: RwLock::new(sync), snapshot_service: ss, @@ -431,10 +432,11 @@ impl TestNet> { miner.clone(), channel.clone() ).unwrap(); + let fork_filter = ForkFilterApi::new(&*client, spec.hard_forks.clone()); let private_tx_handler = Arc::new(SimplePrivateTxHandler::default()); let ss = Arc::new(TestSnapshotService::new()); - let sync = ChainSync::new(config, &*client, Some(private_tx_handler.clone())); + let sync = ChainSync::new(config, &*client, fork_filter, Some(private_tx_handler.clone())); let peer = Arc::new(EthPeer { sync: RwLock::new(sync), snapshot_service: ss, diff --git a/parity/modules.rs b/parity/modules.rs index 264f79202..efd663e7d 100644 --- a/parity/modules.rs +++ b/parity/modules.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::sync::{Arc, mpsc}; +use std::{collections::BTreeSet, sync::{Arc, mpsc}}; use client_traits::{BlockChainClient, ChainNotify}; +use types::BlockNumber; use sync::{self, SyncConfig, NetworkConfiguration, Params, ConnectionFilter}; use snapshot::SnapshotService; use ethcore_private_tx::PrivateStateDB; @@ -38,6 +39,7 @@ pub fn sync( executor: Executor, network_config: NetworkConfiguration, chain: Arc, + forks: BTreeSet, snapshot_service: Arc, private_tx_handler: Option>, private_state: Option>, @@ -49,6 +51,7 @@ pub fn sync( config, executor, chain, + forks, provider, snapshot_service, private_tx_handler, diff --git a/parity/run.rs b/parity/run.rs index ad69e3df8..67b763c89 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -576,6 +576,7 @@ fn execute_impl( cmd.private_encryptor_conf, ).map_err(|e| format!("Client service error: {:?}", e))?; + let forks = spec.hard_forks.clone(); let connection_filter_address = spec.params().node_permission_contract; // drop the spec to free up genesis state. drop(spec); @@ -651,6 +652,7 @@ fn execute_impl( runtime.executor(), net_conf.clone().into(), client.clone(), + forks, snapshot_service.clone(), private_tx_sync, private_state, diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index 718e5d24d..a7984d2ae 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -46,7 +46,7 @@ impl TestSyncProvider { status: RwLock::new(SyncStatus { state: SyncState::Idle, network_id: config.network_id, - protocol_version: 63, + protocol_version: 64, start_block_number: 0, last_imported_block_number: None, highest_block_number: None, @@ -82,11 +82,11 @@ impl SyncProvider for TestSyncProvider { PeerInfo { id: Some("node1".to_owned()), client_version: ClientVersion::from("Parity-Ethereum/1/v2.4.0/linux/rustc"), - capabilities: vec!["eth/62".to_owned(), "eth/63".to_owned()], + capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()], remote_address: "127.0.0.1:7777".to_owned(), local_address: "127.0.0.1:8888".to_owned(), eth_info: Some(EthProtocolInfo { - version: 62, + version: 63, difficulty: Some(40.into()), head: H256::from_low_u64_be(50), }), @@ -95,11 +95,11 @@ impl SyncProvider for TestSyncProvider { PeerInfo { id: None, client_version: ClientVersion::from("Parity-Ethereum/2/v2.4.0/linux/rustc"), - capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()], + capabilities: vec!["eth/64".to_owned(), "eth/65".to_owned()], remote_address: "Handshake".to_owned(), local_address: "127.0.0.1:3333".to_owned(), eth_info: Some(EthProtocolInfo { - version: 64, + version: 65, difficulty: None, head: H256::from_low_u64_be(60), }), diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 828219019..54c69a4fe 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -125,7 +125,7 @@ impl EthTester { #[test] fn rpc_eth_protocol_version() { let request = r#"{"jsonrpc": "2.0", "method": "eth_protocolVersion", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"63","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"64","id":1}"#; assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 20a5d8595..0e906147d 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -255,7 +255,7 @@ fn rpc_parity_net_peers() { let io = deps.default_client(); let request = r#"{"jsonrpc": "2.0", "method": "parity_netPeers", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/62","eth/63"],"id":"node1","name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"1","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":62},"pip":null}},{"caps":["eth/63","eth/64"],"id":null,"name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"2","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":64},"pip":null}}]},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/63","eth/64"],"id":"node1","name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"1","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":63},"pip":null}},{"caps":["eth/64","eth/65"],"id":null,"name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"2","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":65},"pip":null}}]},"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/util/EIP-2124/Cargo.toml b/util/EIP-2124/Cargo.toml deleted file mode 100644 index 67b6ca65f..000000000 --- a/util/EIP-2124/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "eip-2124" -version = "0.1.0" -authors = ["Parity Technologies "] -repository = "https://github.com/paritytech/parity-ethereum" -documentation = "https://docs.rs/eip-2124" -readme = "README.md" -description = "EIP-2124 Fork ID implementation" -keywords = ["eip-2124", "eip"] -license = "GPL-3.0" -edition = "2018" - -[dependencies] -crc = "1" -ethereum-types = "0.8.0" -maplit = "1" -rlp = "0.4" -rlp-derive = "0.1" - -[dev-dependencies] -hex-literal = "0.2" diff --git a/util/EIP-2124/src/lib.rs b/util/EIP-2124/src/lib.rs deleted file mode 100644 index 612465814..000000000 --- a/util/EIP-2124/src/lib.rs +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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 Ethereum 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 Ethereum. If not, see . - -//! EIP-2124 implementation based on . - -#![deny(missing_docs)] - -#![warn( - clippy::all, - clippy::pedantic, - clippy::nursery, -)] - -use crc::crc32; -use ethereum_types::H256; -use maplit::btreemap; -use rlp::{DecoderError, Rlp, RlpStream}; -use rlp_derive::{RlpDecodable, RlpEncodable}; -use std::collections::{BTreeMap, BTreeSet}; - -/// Block number. -pub type BlockNumber = u64; - -/// `CRC32` hash of all previous forks starting from genesis block. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct ForkHash(pub u32); - -impl rlp::Encodable for ForkHash { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(&self.0.to_be_bytes()); - } -} - -impl rlp::Decodable for ForkHash { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder().decode_value(|b| { - if b.len() != 4 { - return Err(DecoderError::RlpInvalidLength); - } - - let mut blob = [0; 4]; - blob.copy_from_slice(&b[..]); - - Ok(Self(u32::from_be_bytes(blob))) - }) - } -} - -impl From for ForkHash { - fn from(genesis: H256) -> Self { - Self(crc32::checksum_ieee(&genesis[..])) - } -} - -impl std::ops::AddAssign for ForkHash { - fn add_assign(&mut self, height: BlockNumber) { - let blob = height.to_be_bytes(); - self.0 = crc32::update(self.0, &crc32::IEEE_TABLE, &blob) - } -} - -impl std::ops::Add for ForkHash { - type Output = Self; - fn add(mut self, height: BlockNumber) -> Self { - self += height; - self - } -} - -/// A fork identifier as defined by EIP-2124. -/// Serves as the chain compatibility identifier. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable)] -pub struct ForkId { - /// CRC32 checksum of the all fork blocks from genesis. - pub hash: ForkHash, - /// Next upcoming fork block number, 0 if not yet known. - pub next: BlockNumber -} - -/// Reason for rejecting provided `ForkId`. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum RejectReason { - /// Remote node is outdated and needs a software update. - RemoteStale, - /// Local node is on an incompatible chain or needs a sofwtare update. - LocalIncompatibleOrStale, -} - -/// Filter that describes the state of blockchain and can be used to check incoming `ForkId`s for compatibility. -#[derive(Clone, Debug)] -pub struct ForkFilter { - /// Blockchain head - pub head: BlockNumber, - past_forks: BTreeMap, - next_forks: BTreeSet, -} - -impl ForkFilter { - /// Create the filter from provided head, genesis block hash, past forks and expected future forks. - pub fn new(head: BlockNumber, genesis: H256, past_forks: PF, next_forks: NF) -> Self - where - PF: IntoIterator, - NF: IntoIterator, - { - let genesis_fork_hash = ForkHash::from(genesis); - Self { - head, - past_forks: past_forks.into_iter().fold((btreemap! { 0 => genesis_fork_hash }, genesis_fork_hash), |(mut acc, base_hash), block| { - let fork_hash = base_hash + block; - acc.insert(block, fork_hash); - (acc, fork_hash) - }).0, - next_forks: next_forks.into_iter().collect(), - } - } - - fn current_fork_hash(&self) -> ForkHash { - *self.past_forks.values().next_back().expect("there is always at least one - genesis - fork hash; qed") - } - - fn future_fork_hashes(&self) -> Vec { - self.next_forks.iter().fold((Vec::new(), self.current_fork_hash()), |(mut acc, hash), fork| { - let next = hash + *fork; - acc.push(next); - (acc, next) - }).0 - } - - /// Insert a new past fork - pub fn insert_past_fork(&mut self, height: BlockNumber) { - self.past_forks.insert(height, self.current_fork_hash() + height); - } - - /// Insert a new upcoming fork - pub fn insert_next_fork(&mut self, height: BlockNumber) { - self.next_forks.insert(height); - } - - /// Mark an upcoming fork as already happened and immutable. - /// Returns `false` if no such fork existed and the call was a no-op. - pub fn promote_next_fork(&mut self, height: BlockNumber) -> bool { - let promoted = self.next_forks.remove(&height); - if promoted { - self.insert_past_fork(height); - } - promoted - } - - /// Check whether the provided `ForkId` is compatible based on the validation rules in `EIP-2124`. - /// - /// # Errors - /// Returns a `RejectReason` if the `ForkId` is not compatible. - pub fn is_valid(&self, fork_id: ForkId) -> Result<(), RejectReason> { - // 1) If local and remote FORK_HASH matches... - if self.current_fork_hash() == fork_id.hash { - if fork_id.next == 0 { - // 1b) No remotely announced fork, connect. - return Ok(()) - } - - //... compare local head to FORK_NEXT. - if self.head >= fork_id.next { - // 1a) A remotely announced but remotely not passed block is already passed locally, disconnect, - // since the chains are incompatible. - return Err(RejectReason::LocalIncompatibleOrStale) - } else { - // 1b) Remotely announced fork not yet passed locally, connect. - return Ok(()) - } - } - - // 2) If the remote FORK_HASH is a subset of the local past forks... - let mut it = self.past_forks.iter(); - while let Some((_, hash)) = it.next() { - if *hash == fork_id.hash { - // ...and the remote FORK_NEXT matches with the locally following fork block number, connect. - if let Some((actual_fork_block, _)) = it.next() { - if *actual_fork_block == fork_id.next { - return Ok(()) - } else { - return Err(RejectReason::RemoteStale); - } - } - - break; - } - } - - // 3) If the remote FORK_HASH is a superset of the local past forks and can be completed with locally known future forks, connect. - for future_fork_hash in self.future_fork_hashes() { - if future_fork_hash == fork_id.hash { - return Ok(()) - } - } - - // 4) Reject in all other cases. - Err(RejectReason::LocalIncompatibleOrStale) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use hex_literal::hex; - - const GENESIS_HASH: &str = "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"; - const BYZANTIUM_FORK_HEIGHT: BlockNumber = 4370000; - const PETERSBURG_FORK_HEIGHT: BlockNumber = 7280000; - - // EIP test vectors. - - #[test] - fn test_forkhash() { - let mut fork_hash = ForkHash::from(GENESIS_HASH.parse::().unwrap()); - assert_eq!(fork_hash.0, 0xfc64ec04); - - fork_hash += 1150000; - assert_eq!(fork_hash.0, 0x97c2c34c); - - fork_hash += 1920000; - assert_eq!(fork_hash.0, 0x91d1f948); - } - - #[test] - fn test_compatibility_check() { - let spurious_filter = ForkFilter::new( - 4369999, - GENESIS_HASH.parse().unwrap(), - vec![1150000, 1920000, 2463000, 2675000], - vec![BYZANTIUM_FORK_HEIGHT] - ); - let mut byzantium_filter = spurious_filter.clone(); - byzantium_filter.promote_next_fork(BYZANTIUM_FORK_HEIGHT); - byzantium_filter.insert_next_fork(PETERSBURG_FORK_HEIGHT); - byzantium_filter.head = 7279999; - - let mut petersburg_filter = byzantium_filter.clone(); - petersburg_filter.promote_next_fork(PETERSBURG_FORK_HEIGHT); - petersburg_filter.head = 7987396; - - // Local is mainnet Petersburg, remote announces the same. No future fork is announced. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0x668db0af), next: 0 }), Ok(())); - - // Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork - // at block 0xffffffff, but that is uncertain. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0x668db0af), next: BlockNumber::max_value() }), Ok(())); - - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg),remote announces - // also Byzantium, but it's not yet aware of Petersburg (e.g. non updated node before the fork). - // In this case we don't know if Petersburg passed yet or not. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: 0 }), Ok(())); - - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, and it's also aware of Petersburg (e.g. updated node before the fork). We - // don't know if Petersburg passed yet (will pass) or not. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: PETERSBURG_FORK_HEIGHT }), Ok(())); - - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, and it's also aware of some random fork (e.g. misconfigured Petersburg). As - // neither forks passed at neither nodes, they may mismatch, but we still connect for now. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: BlockNumber::max_value() }), Ok(())); - - // Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote is simply out of sync, accept. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: PETERSBURG_FORK_HEIGHT }), Ok(())); - - // Local is mainnet Petersburg, remote announces Spurious + knowledge about Byzantium. Remote - // is definitely out of sync. It may or may not need the Petersburg update, we don't know yet. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0x3edd5b10), next: 4370000 }), Ok(())); - - // Local is mainnet Byzantium, remote announces Petersburg. Local is out of sync, accept. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0x668db0af), next: 0 }), Ok(())); - - // Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. Local - // out of sync. Local also knows about a future fork, but that is uncertain yet. - assert_eq!(spurious_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: 0 }), Ok(())); - - // Local is mainnet Petersburg. remote announces Byzantium but is not aware of further forks. - // Remote needs software update. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: 0 }), Err(RejectReason::RemoteStale)); - - // Local is mainnet Petersburg, and isn't aware of more forks. Remote announces Petersburg + - // 0xffffffff. Local needs software update, reject. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0x5cddc0e1), next: 0 }), Err(RejectReason::LocalIncompatibleOrStale)); - - // Local is mainnet Byzantium, and is aware of Petersburg. Remote announces Petersburg + - // 0xffffffff. Local needs software update, reject. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0x5cddc0e1), next: 0 }), Err(RejectReason::LocalIncompatibleOrStale)); - - // Local is mainnet Petersburg, remote is Rinkeby Petersburg. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0xafec6b27), next: 0 }), Err(RejectReason::LocalIncompatibleOrStale)); - - // Local is mainnet Petersburg, far in the future. Remote announces Gopherium (non existing fork) - // at some future block 88888888, for itself, but past block for local. Local is incompatible. - // - // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). - let mut far_away_petersburg = petersburg_filter.clone(); - far_away_petersburg.head = 88888888; - assert_eq!(far_away_petersburg.is_valid(ForkId { hash: ForkHash(0x668db0af), next: 88888888 }), Err(RejectReason::LocalIncompatibleOrStale)); - - // Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing - // fork) at block 7279999, before Petersburg. Local is incompatible. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: 7279999 }), Err(RejectReason::LocalIncompatibleOrStale)); - } - - #[test] - fn test_forkid_serialization() { - assert_eq!(rlp::encode(&ForkId { hash: ForkHash(0), next: 0 }), hex!("c6840000000080")); - assert_eq!(rlp::encode(&ForkId { hash: ForkHash(0xdeadbeef), next: 0xBADDCAFE }), hex!("ca84deadbeef84baddcafe")); - assert_eq!(rlp::encode(&ForkId { hash: ForkHash(u32::max_value()), next: u64::max_value() }), hex!("ce84ffffffff88ffffffffffffffff")); - - assert_eq!(rlp::decode::(&hex!("c6840000000080")).unwrap(), ForkId { hash: ForkHash(0), next: 0 }); - assert_eq!(rlp::decode::(&hex!("ca84deadbeef84baddcafe")).unwrap(), ForkId { hash: ForkHash(0xdeadbeef), next: 0xBADDCAFE }); - assert_eq!(rlp::decode::(&hex!("ce84ffffffff88ffffffffffffffff")).unwrap(), ForkId { hash: ForkHash(u32::max_value()), next: u64::max_value() }); - } -}