From 958e290d1600584dcad4951acdc0f65ed89873c7 Mon Sep 17 00:00:00 2001 From: Fredrik Date: Fri, 2 Mar 2018 18:58:17 +0100 Subject: [PATCH 01/11] Echo back the message hash of a ping in the pong request --- util/network/src/discovery.rs | 32 +++++++++++++++++++++++++------- util/network/src/node_table.rs | 1 + 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/util/network/src/discovery.rs b/util/network/src/discovery.rs index 88468a375a0..286cf3461f8 100644 --- a/util/network/src/discovery.rs +++ b/util/network/src/discovery.rs @@ -179,6 +179,7 @@ impl Discovery { } } + /// Removes the timeout of a given NodeId if it can be found in one of the discovery buckets fn clear_ping(&mut self, id: &NodeId) { let bucket = &mut self.node_buckets[Discovery::distance(&self.id_hash, &keccak(id)) as usize]; if let Some(node) = bucket.nodes.iter_mut().find(|n| &n.address.id == id) { @@ -186,6 +187,7 @@ impl Discovery { } } + /// Starts the discovery process at round 0 fn start(&mut self) { trace!(target: "discovery", "Starting discovery"); self.discovery_round = 0; @@ -379,7 +381,7 @@ impl Discovery { let packet_id = signed[0]; let rlp = UntrustedRlp::new(&signed[1..]); match packet_id { - PACKET_PING => self.on_ping(&rlp, &node_id, &from), + PACKET_PING => self.on_ping(&rlp, &node_id, &from, &hash_signed), PACKET_PONG => self.on_pong(&rlp, &node_id, &from), PACKET_FIND_NODE => self.on_find_node(&rlp, &node_id, &from), PACKET_NEIGHBOURS => self.on_neighbours(&rlp, &node_id, &from), @@ -390,6 +392,7 @@ impl Discovery { } } + /// Validate that given timestamp is in within one second of now or in the future fn check_timestamp(&self, timestamp: u64) -> Result<(), Error> { if self.check_timestamps && timestamp < time::get_time().sec as u64{ debug!(target: "discovery", "Expired packet"); @@ -402,7 +405,7 @@ impl Discovery { entry.endpoint.is_allowed(&self.ip_filter) && entry.id != self.id } - fn on_ping(&mut self, rlp: &UntrustedRlp, node: &NodeId, from: &SocketAddr) -> Result, Error> { + fn on_ping(&mut self, rlp: &UntrustedRlp, node: &NodeId, from: &SocketAddr, echo_hash: &[u8]) -> Result, Error> { trace!(target: "discovery", "Got Ping from {:?}", &from); let source = NodeEndpoint::from_rlp(&rlp.at(1)?)?; let dest = NodeEndpoint::from_rlp(&rlp.at(2)?)?; @@ -418,10 +421,9 @@ impl Discovery { self.update_node(entry.clone()); added_map.insert(node.clone(), entry); } - let hash = keccak(rlp.as_raw()); let mut response = RlpStream::new_list(2); dest.to_rlp_list(&mut response); - response.append(&hash); + response.append(&echo_hash); self.send_packet(PACKET_PONG, from, &response.drain()); Ok(Some(TableUpdates { added: added_map, removed: HashSet::new() })) @@ -429,7 +431,7 @@ impl Discovery { fn on_pong(&mut self, rlp: &UntrustedRlp, node: &NodeId, from: &SocketAddr) -> Result, Error> { trace!(target: "discovery", "Got Pong from {:?}", &from); - // TODO: validate pong packet + // TODO: validate pong packet in rlp.val_at(1) let dest = NodeEndpoint::from_rlp(&rlp.at(0)?)?; let timestamp: u64 = rlp.val_at(2)?; self.check_timestamp(timestamp)?; @@ -439,8 +441,6 @@ impl Discovery { entry.endpoint.address = from.clone(); } self.clear_ping(node); - let mut added_map = HashMap::new(); - added_map.insert(node.clone(), entry); Ok(None) } @@ -707,4 +707,22 @@ mod tests { assert!(discovery.on_packet(&packet, from.clone()).is_ok()); } + #[test] + fn test_ping() { + let key1 = Random.generate().unwrap(); + let key2 = Random.generate().unwrap(); + let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40444").unwrap(), udp_port: 40444 }; + let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40445").unwrap(), udp_port: 40445 }; + let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0, IpFilter::default()); + let mut discovery2 = Discovery::new(&key2, ep2.address.clone(), ep2.clone(), 0, IpFilter::default()); + + discovery1.ping(&ep2); + let ping_data = discovery1.send_queue.pop_front().unwrap(); + discovery2.on_packet(&ping_data.payload, ep1.address.clone()).ok(); + let pong_data = discovery2.send_queue.pop_front().unwrap(); + let data = &pong_data.payload[(32 + 65)..]; + let rlp = UntrustedRlp::new(&data[1..]); + assert_eq!(ping_data.payload[0..32], rlp.val_at::>(1).unwrap()[..]) + } + } diff --git a/util/network/src/node_table.rs b/util/network/src/node_table.rs index 4b1a935618c..ebf6003640f 100644 --- a/util/network/src/node_table.rs +++ b/util/network/src/node_table.rs @@ -102,6 +102,7 @@ impl NodeEndpoint { self.to_rlp(rlp); } + /// Validates that the port is not 0 and address IP is specified pub fn is_valid(&self) -> bool { self.udp_port != 0 && self.address.port() != 0 && match self.address { From dfbd8427c880fc1c3c34e8c3e068d501ddd7ea0f Mon Sep 17 00:00:00 2001 From: Shane Vitarana Date: Fri, 2 Mar 2018 11:21:35 -0500 Subject: [PATCH 02/11] Fixed broken link in README (#8012) * Fixed broken link in README * Updated wiki link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 666ffc34629..a5fd7d4a8a5 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Or join our community on Matrix: Official website: https://parity.io -Be sure to check out [our wiki](https://paritytech.github.io/wiki/) and the [internal documentation](https://paritytech.github.io/parity/ethcore/index.html) for more information. +Be sure to check out [our wiki](https://wiki.parity.io) for more information. ---- From 06c7a09505cd215866647091f523ee9814226067 Mon Sep 17 00:00:00 2001 From: Niklas Adolfsson Date: Fri, 2 Mar 2018 18:30:25 +0100 Subject: [PATCH 03/11] [hardware wallet] sleeping -> pollling (#8018) * Use polling, enable missing doc warnings & docs * make try_connect_polling() a free function --- hw/src/ledger.rs | 34 ++++++++++++++++++++++------------ hw/src/lib.rs | 9 +++++++++ hw/src/trezor.rs | 35 ++++++++++++++++++++++------------- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/hw/src/ledger.rs b/hw/src/ledger.rs index 130149c4738..e7d616d4f9f 100644 --- a/hw/src/ledger.rs +++ b/hw/src/ledger.rs @@ -21,8 +21,7 @@ use std::cmp::min; use std::fmt; use std::str::FromStr; use std::sync::{Arc, Weak}; -use std::time::Duration; -use std::thread; +use std::time::{Duration, Instant}; use ethereum_types::{H256, Address}; use ethkey::Signature; @@ -353,17 +352,30 @@ impl Manager { Err(Error::InvalidDevice) } } +} +// Try to connect to the device using polling in at most the time specified by the `timeout` +fn try_connect_polling(ledger: Arc, timeout: Duration) -> bool { + let start_time = Instant::now(); + while start_time.elapsed() <= timeout { + if let Ok(_) = ledger.update_devices() { + return true + } + } + false } /// Ledger event handler -/// A seperate thread is handling incoming events +/// A seperate thread is hanedling incoming events +/// +/// Note, that this run to completion and race-conditions can't occur but this can +/// therefore starve other events for being process with a spinlock or similar pub struct EventHandler { ledger: Weak, } impl EventHandler { - /// Ledger event handler constructor + /// Ledger event handler constructor pub fn new(ledger: Weak) -> Self { Self { ledger: ledger } } @@ -371,21 +383,19 @@ impl EventHandler { impl libusb::Hotplug for EventHandler { fn device_arrived(&mut self, device: libusb::Device) { + debug!(target: "hw", "Ledger arrived"); if let (Some(ledger), Ok(_)) = (self.ledger.upgrade(), Manager::is_valid_ledger(&device)) { - debug!(target: "hw", "Ledger arrived"); - // Wait for the device to boot up - thread::sleep(Duration::from_millis(1000)); - if let Err(e) = ledger.update_devices() { - debug!(target: "hw", "Ledger connect error: {:?}", e); + if try_connect_polling(ledger, Duration::from_millis(500)) != true { + debug!(target: "hw", "Ledger connect timeout"); } } } fn device_left(&mut self, device: libusb::Device) { + debug!(target: "hw", "Ledger left"); if let (Some(ledger), Ok(_)) = (self.ledger.upgrade(), Manager::is_valid_ledger(&device)) { - debug!(target: "hw", "Ledger left"); - if let Err(e) = ledger.update_devices() { - debug!(target: "hw", "Ledger disconnect error: {:?}", e); + if try_connect_polling(ledger, Duration::from_millis(500)) != true { + debug!(target: "hw", "Ledger disconnect timeout"); } } } diff --git a/hw/src/lib.rs b/hw/src/lib.rs index 060aead3a14..9bfec83410b 100644 --- a/hw/src/lib.rs +++ b/hw/src/lib.rs @@ -16,6 +16,8 @@ //! Hardware wallet management. +#![warn(missing_docs)] + extern crate ethereum_types; extern crate ethkey; extern crate hidapi; @@ -61,12 +63,19 @@ pub enum Error { /// or less a duplicate of ethcore::transaction::Transaction, but we can't /// import ethcore here as that would be a circular dependency. pub struct TransactionInfo { + /// Nonce pub nonce: U256, + /// Gas price pub gas_price: U256, + /// Gas limit pub gas_limit: U256, + /// Receiver pub to: Option
, + /// Value pub value: U256, + /// Data pub data: Vec, + /// Chain ID pub chain_id: Option, } diff --git a/hw/src/trezor.rs b/hw/src/trezor.rs index 4105bb7b552..7db226718bc 100644 --- a/hw/src/trezor.rs +++ b/hw/src/trezor.rs @@ -24,11 +24,9 @@ use super::{WalletInfo, TransactionInfo, KeyPath}; use std::cmp::{min, max}; use std::fmt; use std::sync::{Arc, Weak}; -use std::time::Duration; -use std::thread; +use std::time::{Duration, Instant}; use ethereum_types::{U256, H256, Address}; - use ethkey::Signature; use hidapi; use libusb; @@ -40,8 +38,8 @@ use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumT /// Trezor v1 vendor ID pub const TREZOR_VID: u16 = 0x534c; -/// Trezor product IDs -pub const TREZOR_PIDS: [u16; 1] = [0x0001]; +/// Trezor product IDs +pub const TREZOR_PIDS: [u16; 1] = [0x0001]; const ETH_DERIVATION_PATH: [u32; 5] = [0x8000002C, 0x8000003C, 0x80000000, 0, 0]; // m/44'/60'/0'/0/0 const ETC_DERIVATION_PATH: [u32; 5] = [0x8000002C, 0x8000003D, 0x80000000, 0, 0]; // m/44'/61'/0'/0/0 @@ -402,14 +400,28 @@ impl Manager { } } +// Try to connect to the device using polling in at most the time specified by the `timeout` +fn try_connect_polling(trezor: Arc, duration: Duration) -> bool { + let start_time = Instant::now(); + while start_time.elapsed() <= duration { + if let Ok(_) = trezor.update_devices() { + return true + } + } + false +} + /// Trezor event handler /// A separate thread is handeling incoming events +/// +/// Note, that this run to completion and race-conditions can't occur but this can +/// therefore starve other events for being process with a spinlock or similar pub struct EventHandler { trezor: Weak, } impl EventHandler { - // Trezor event handler constructor + /// Trezor event handler constructor pub fn new(trezor: Weak) -> Self { Self { trezor: trezor } } @@ -419,20 +431,17 @@ impl libusb::Hotplug for EventHandler { fn device_arrived(&mut self, _device: libusb::Device) { debug!(target: "hw", "Trezor V1 arrived"); if let Some(trezor) = self.trezor.upgrade() { - // Wait for the device to boot up - thread::sleep(Duration::from_millis(1000)); - if let Err(e) = trezor.update_devices() { - debug!(target: "hw", "Trezor V1 connect error: {:?}", e); + if try_connect_polling(trezor, Duration::from_millis(500)) != true { + debug!(target: "hw", "Ledger connect timeout"); } - } } fn device_left(&mut self, _device: libusb::Device) { debug!(target: "hw", "Trezor V1 left"); if let Some(trezor) = self.trezor.upgrade() { - if let Err(e) = trezor.update_devices() { - debug!(target: "hw", "Trezor V1 disconnect error: {:?}", e); + if try_connect_polling(trezor, Duration::from_millis(500)) != true { + debug!(target: "hw", "Ledger disconnect timeout"); } } } From 7392901e82cf9ac09dc5b2f18c7516321da5258e Mon Sep 17 00:00:00 2001 From: Dmitry Kashitsyn Date: Sat, 3 Mar 2018 18:42:13 +0100 Subject: [PATCH 04/11] `Client` refactoring (#7038) * Improves `BestBlock` comment * Improves `TraceDB` comment * Improves `journaldb::Algorithm` comment. Probably the whole enum should be renamed to `Strategy` or something alike. * Comments some of the `Client`'s fields * Deglobs client imports * Fixes comments * Extracts `import_lock` to `Importer` struct * Extracts `verifier` to `Importer` struct * Extracts `block_queue` to `Importer` struct * Extracts `miner` to `Importer` struct * Extracts `ancient_verifier` to `Importer` struct * Extracts `rng` to `Importer` struct * Extracts `import_old_block` to `Importer` struct * Adds `Nonce` trait * Adds `Balance` trait * Adds `ChainInfo` trait * Fixes imports for tests using `chain_info` method * Adds `BlockInfo` trait * Adds more `ChainInfo` imports * Adds `BlockInfo` imports * Adds `ReopenBlock` trait * Adds `PrepareOpenBlock` trait * Fixes import in tests * Adds `CallContract` trait * Fixes imports in tests using `call_contract` method * Adds `TransactionInfo` trait * Adds `RegistryInfo` trait * Fixes imports in tests using `registry_address` method * Adds `ScheduleInfo` trait * Adds `ImportSealedBlock` trait * Fixes imports in test using `import_sealed_block` method * Adds `BroadcastProposalBlock` trait * Migrates `Miner` to static dispatch * Fixes tests * Moves `calculate_enacted_retracted` to `Importer` * Moves import-related methods to `Importer` * Removes redundant `import_old_block` wrapper * Extracts `import_block*` into separate trait * Fixes tests * Handles `Pending` in `LightFetch` * Handles `Pending` in filters * Handles `Pending` in `ParityClient` * Handles `Pending` in `EthClient` * Removes `BlockId::Pending`, partly refactors dependent code * Adds `StateInfo` trait * Exports `StateOrBlock` and `BlockChain` types from `client` module * Refactors `balance` RPC using generic API * Refactors `storage_at` RPC using generic API * Makes `MinerService::pending_state`'s return type dynamic * Adds `StateOrBlock` and `BlockChain` types * Adds impl of `client::BlockChain` for `Client` * Exports `StateInfo` trait from `client` module * Missing `self` use To be fixed up to "Adds impl of `client::BlockChain` for `Client`" * Adds `number_to_id` and refactors dependent RPC methods * Refactors `code_at` using generic API * Adds `StateClient` trait * Refactors RPC to use `StateClient` trait * Reverts `client::BlockChain` trait stuff, refactors methods to accept `StateOrBlock` * Refactors TestClient * Adds helper function `block_number_to_id` * Uses `block_number_to_id` instead of local function * Handles `Pending` in `list_accounts` and `list_storage_keys` * Attempt to use associated types for state instead of trait objects * Simplifies `state_at_beginning` * Extracts `call` and `call_many` into separate trait * Refactors `build_last_hashes` to accept reference * Exports `Call` type from the module * Refactors `call` and `call_many` to accept state and header * Exports `state_at` in `StateClient` * Exports `pending_block_header` from `MinerService` * Refactors RPC `call` method using new API * Adds missing parentheses * Refactors `parity::call` to use new call API * Update .gitlab-ci.yml fix gitlab lint * Fixes error handling * Refactors `traces::call` and `call_many` to use new call API * Refactors `call_contract` * Refactors `block_header` * Refactors internal RPC method `block` * Moves `estimate_gas` to `Call` trait, refactors parameters * Refactors `estimate_gas` in RPC * Refactors `uncle` * Refactors RPC `transaction` * Covers missing branches * Makes it all compile, fixes compiler grumbles * Adds casts in `blockchain` module * Fixes `PendingBlock` tests, work on `MinerService` * Adds test stubs for StateClient and EngineInfo * Makes `state_db` public * Adds missing impls for `TestBlockChainClient` * Adds trait documentation * Adds missing docs to the `state_db` module * Fixes trivial compilation errors * Moves `code_hash` method to a `BlockInfo` trait * Refactors `Verifier` to be generic over client * Refactors `TransactionFilter` to be generic over client * Refactors `Miner` and `Client` to reflect changes in verifier and txfilter API * Moves `ServiceTransactionChecker` back to `ethcore` * Fixes trait bounds in `Miner` API * Fixes `Client` * Fixes lifetime bound in `FullFamilyParams` * Adds comments to `FullFamilyParams` * Fixes imports in `ethcore` * Fixes BlockNumber handling in `code_at` and `replay_block_transactions` * fix compile issues * First step to redundant trait merge * Fixes compilation error in RPC tests * Adds mock `State` as a stub for `TestClient` * Handles `StateOrBlock::State` in `TestBlockChainClient::balance` * Fixes `transaction_count` RPC * Fixes `transaction_count` * Moves `service_transaction.json` to the `contracts` subfolder * Fixes compilation errors in tests * Refactors client to use `AccountData` * Refactors client to use `BlockChain` * Refactors miner to use aggregate traits * Adds `SealedBlockImporter` trait * Refactors miner to use `SealedBlockImporter` trait * Removes unused imports * Simplifies `RegistryInfo::registry_address` * Fixes indentation * Removes commented out trait bound --- Cargo.lock | 1 + ethcore/light/src/client/header_chain.rs | 7 +- ethcore/light/src/client/mod.rs | 10 +- ethcore/light/src/on_demand/request.rs | 2 +- ethcore/light/src/provider.rs | 8 +- .../res/contracts}/service_transaction.json | 0 ethcore/src/blockchain/best_block.rs | 7 +- ethcore/src/client/client.rs | 1487 +++++++++-------- ethcore/src/client/mod.rs | 9 +- ethcore/src/client/test_client.rs | 360 ++-- ethcore/src/client/traits.rs | 269 ++- ethcore/src/engines/tendermint/mod.rs | 1 + ethcore/src/engines/validator_set/contract.rs | 2 +- ethcore/src/engines/validator_set/multi.rs | 2 +- .../engines/validator_set/safe_contract.rs | 2 +- ethcore/src/json_tests/chain.rs | 2 +- ethcore/src/machine.rs | 4 +- ethcore/src/miner/miner.rs | 109 +- ethcore/src/miner/mod.rs | 48 +- .../src/miner}/service_transaction_checker.rs | 17 +- ethcore/src/snapshot/service.rs | 2 +- ethcore/src/snapshot/tests/helpers.rs | 2 +- .../src/snapshot/tests/proof_of_authority.rs | 2 +- ethcore/src/snapshot/tests/service.rs | 2 +- ethcore/src/snapshot/watcher.rs | 2 +- ethcore/src/state/mod.rs | 22 + ethcore/src/state_db.rs | 18 +- ethcore/src/tests/client.rs | 2 +- ethcore/src/tests/helpers.rs | 2 +- ethcore/src/trace/db.rs | 7 +- ethcore/src/tx_filter.rs | 4 +- ethcore/src/verification/canon_verifier.rs | 5 +- ethcore/src/verification/mod.rs | 4 +- ethcore/src/verification/noop_verifier.rs | 5 +- ethcore/src/verification/verification.rs | 40 +- ethcore/src/verification/verifier.rs | 9 +- ethcore/types/src/ids.rs | 2 - miner/src/lib.rs | 5 - parity/blockchain.rs | 8 +- parity/dapps.rs | 2 +- parity/informant.rs | 2 +- rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/helpers/light_fetch.rs | 16 +- rpc/src/v1/impls/eth.rs | 366 +++- rpc/src/v1/impls/light/eth.rs | 33 +- rpc/src/v1/impls/light/parity.rs | 12 +- rpc/src/v1/impls/parity.rs | 91 +- rpc/src/v1/impls/traces.rs | 65 +- rpc/src/v1/tests/eth.rs | 6 +- rpc/src/v1/tests/helpers/miner_service.rs | 61 +- rpc/src/v1/tests/mocked/eth_pubsub.rs | 2 +- rpc/src/v1/types/block_number.rs | 32 +- rpc/src/v1/types/filter.rs | 10 +- rpc/src/v1/types/mod.rs | 2 +- rpc/src/v1/types/trace_filter.rs | 13 +- secret_store/src/acl_storage.rs | 2 +- secret_store/src/key_server_set.rs | 6 +- secret_store/src/listener/service_contract.rs | 2 +- secret_store/src/trusted_client.rs | 2 +- sync/src/chain.rs | 2 +- sync/src/light_sync/tests/mod.rs | 2 +- sync/src/tests/chain.rs | 2 +- sync/src/tests/consensus.rs | 2 +- util/journaldb/src/lib.rs | 2 +- 65 files changed, 2028 insertions(+), 1199 deletions(-) rename {miner/res => ethcore/res/contracts}/service_transaction.json (100%) rename {miner/src => ethcore/src/miner}/service_transaction_checker.rs (74%) diff --git a/Cargo.lock b/Cargo.lock index 0cc37dba0b9..08915933c18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2146,6 +2146,7 @@ dependencies = [ "parity-updater 1.9.0", "parity-version 1.10.0", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "patricia-trie 0.1.0", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 6de68bbe36c..7118e67e3e0 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -497,7 +497,7 @@ impl HeaderChain { if self.best_block.read().number < num { return None } self.candidates.read().get(&num).map(|entry| entry.canonical_hash) } - BlockId::Latest | BlockId::Pending => { + BlockId::Latest => { Some(self.best_block.read().hash) } } @@ -539,7 +539,7 @@ impl HeaderChain { self.candidates.read().get(&num).map(|entry| entry.canonical_hash) .and_then(load_from_db) } - BlockId::Latest | BlockId::Pending => { + BlockId::Latest => { // hold candidates hear to prevent deletion of the header // as we read it. let _candidates = self.candidates.read(); @@ -575,7 +575,7 @@ impl HeaderChain { if self.best_block.read().number < num { return None } candidates.get(&num).map(|era| era.candidates[0].total_difficulty) } - BlockId::Latest | BlockId::Pending => Some(self.best_block.read().total_difficulty) + BlockId::Latest => Some(self.best_block.read().total_difficulty) } } @@ -866,7 +866,6 @@ mod tests { assert!(chain.block_header(BlockId::Earliest).is_some()); assert!(chain.block_header(BlockId::Latest).is_some()); - assert!(chain.block_header(BlockId::Pending).is_some()); } #[test] diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 4c7ea70ad2b..c8f62773143 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -574,6 +574,12 @@ impl LightChainClient for Client { } } +impl ::ethcore::client::ChainInfo for Client { + fn chain_info(&self) -> BlockChainInfo { + Client::chain_info(self) + } +} + impl ::ethcore::client::EngineClient for Client { fn update_sealing(&self) { } fn submit_seal(&self, _block_hash: H256, _seal: Vec>) { } @@ -587,10 +593,6 @@ impl ::ethcore::client::EngineClient for Client { }) } - fn chain_info(&self) -> BlockChainInfo { - Client::chain_info(self) - } - fn as_full_client(&self) -> Option<&::ethcore::client::BlockChainClient> { None } diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index 5488e271521..04a8189915a 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -948,7 +948,7 @@ mod tests { use trie::recorder::Recorder; use hash::keccak; - use ethcore::client::{BlockChainClient, TestBlockChainClient, EachBlockWith}; + use ethcore::client::{BlockChainClient, BlockInfo, TestBlockChainClient, EachBlockWith}; use ethcore::header::Header; use ethcore::encoded; use ethcore::receipt::{Receipt, TransactionOutcome}; diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 70d04db9c66..1d9af0ac19b 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -20,9 +20,9 @@ use std::sync::Arc; use ethcore::blockchain_info::BlockChainInfo; -use ethcore::client::{BlockChainClient, ProvingBlockChainClient}; -use ethcore::encoded; +use ethcore::client::{BlockChainClient, ProvingBlockChainClient, ChainInfo, BlockInfo as ClientBlockInfo}; use ethcore::ids::BlockId; +use ethcore::encoded; use ethereum_types::H256; use parking_lot::RwLock; use transaction::PendingTransaction; @@ -138,7 +138,7 @@ pub trait Provider: Send + Sync { // Implementation of a light client data provider for a client. impl Provider for T { fn chain_info(&self) -> BlockChainInfo { - BlockChainClient::chain_info(self) + ChainInfo::chain_info(self) } fn reorg_depth(&self, a: &H256, b: &H256) -> Option { @@ -150,7 +150,7 @@ impl Provider for T { } fn block_header(&self, id: BlockId) -> Option { - BlockChainClient::block_header(self, id) + ClientBlockInfo::block_header(self, id) } fn transaction_index(&self, req: request::CompleteTransactionIndexRequest) diff --git a/miner/res/service_transaction.json b/ethcore/res/contracts/service_transaction.json similarity index 100% rename from miner/res/service_transaction.json rename to ethcore/res/contracts/service_transaction.json diff --git a/ethcore/src/blockchain/best_block.rs b/ethcore/src/blockchain/best_block.rs index 7c0b6294361..1436ced275d 100644 --- a/ethcore/src/blockchain/best_block.rs +++ b/ethcore/src/blockchain/best_block.rs @@ -18,7 +18,12 @@ use ethereum_types::{H256, U256}; use bytes::Bytes; use header::BlockNumber; -/// Best block info. +/// Contains information on a best block that is specific to the consensus engine. +/// +/// For GHOST fork-choice rule it would typically describe the block with highest +/// combined difficulty (usually the block with the highest block number). +/// +/// Sometimes refered as 'latest block'. #[derive(Default)] pub struct BestBlock { /// Best block hash. diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index bc4b1fe39c1..5a5ffae16ea 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -26,20 +26,24 @@ use itertools::Itertools; use hash::keccak; use bytes::Bytes; use journaldb; -use util_error::UtilError; use trie::{TrieSpec, TrieFactory, Trie}; use kvdb::{DBValue, KeyValueDB, DBTransaction}; +use util_error::UtilError; // other use ethereum_types::{H256, Address, U256}; -use block::*; +use block::{IsBlock, LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute, TransactionAddress}; use client::ancient_import::AncientVerifier; use client::Error as ClientError; +use client::{ + Nonce, Balance, ChainInfo, BlockInfo, CallContract, TransactionInfo, RegistryInfo, ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, + StateOrBlock, StateInfo, StateClient, Call, AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter +}; use client::{ BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, - ChainNotify, PruningInfo, ProvingBlockChainClient, + ChainNotify, PruningInfo, ProvingBlockChainClient, EngineInfo }; use encoded; use engines::{EthEngine, EpochTransition}; @@ -48,8 +52,8 @@ use vm::{EnvInfo, LastHashes}; use evm::Schedule; use executive::{Executive, Executed, TransactOptions, contract_address}; use factory::{Factories, VmFactory}; -use header::{BlockNumber, Header, Seal}; -use io::*; +use header::{BlockNumber, Header}; +use io::IoChannel; use log_entry::LocalizedLogEntry; use miner::{Miner, MinerService}; use parking_lot::{Mutex, RwLock}; @@ -135,338 +139,109 @@ impl SleepState { } } +struct Importer { + /// Lock used during block import + pub import_lock: Mutex<()>, // FIXME Maybe wrap the whole `Importer` instead? + + /// Used to verify blocks + pub verifier: Box>, + + /// Queue containing pending blocks + pub block_queue: BlockQueue, + + /// Handles block sealing + pub miner: Arc, + + /// Ancient block verifier: import an ancient sequence of blocks in order from a starting epoch + pub ancient_verifier: Mutex>, + + /// Random number generator used by `AncientVerifier` + pub rng: Mutex, + + /// Ethereum engine to be used during import + pub engine: Arc, +} + /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue. /// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue. pub struct Client { + /// Flag used to disable the client forever. Not to be confused with `liveness`. + /// + /// For example, auto-updater will disable client forever if there is a + /// hard fork registered on-chain that we don't have capability for. + /// When hard fork block rolls around, the client (if `update` is false) + /// knows it can't proceed further. enabled: AtomicBool, + + /// Operating mode for the client mode: Mutex, + chain: RwLock>, tracedb: RwLock>, engine: Arc, + + /// Client configuration config: ClientConfig, + + /// Database pruning strategy to use for StateDB pruning: journaldb::Algorithm, + + /// Client uses this to store blocks, traces, etc. db: RwLock>, + state_db: RwLock, - block_queue: BlockQueue, + + /// Report on the status of client report: RwLock, - import_lock: Mutex<()>, - verifier: Box, - miner: Arc, + sleep_state: Mutex, + + /// Flag changed by `sleep` and `wake_up` methods. Not to be confused with `enabled`. liveness: AtomicBool, io_channel: Mutex>, + + /// List of actors to be notified on certain chain events notify: RwLock>>, + + /// Count of pending transactions in the queue queue_transactions: AtomicUsize, last_hashes: RwLock>, factories: Factories, + + /// Number of eras kept in a journal before they are pruned history: u64, - ancient_verifier: Mutex>, + + /// An action to be done if a mode/spec_name change happens on_user_defaults_change: Mutex) + 'static + Send>>>, + + /// Link to a registry object useful for looking up names registrar: registry::Registry, registrar_address: Option
, + + /// A closure to call when we want to restart the client exit_handler: Mutex) + 'static + Send>>>, + + importer: Importer, } -impl Client { - /// Create a new client with given parameters. - /// The database is assumed to have been initialized with the correct columns. +impl Importer { pub fn new( - config: ClientConfig, - spec: &Spec, - db: Arc, - miner: Arc, + config: &ClientConfig, + engine: Arc, message_channel: IoChannel, - ) -> Result, ::error::Error> { - let trie_spec = match config.fat_db { - true => TrieSpec::Fat, - false => TrieSpec::Secure, - }; - - let trie_factory = TrieFactory::new(trie_spec); - let factories = Factories { - vm: VmFactory::new(config.vm_type.clone(), config.jump_table_size), - trie: trie_factory, - accountdb: Default::default(), - }; - - let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE); - let mut state_db = StateDB::new(journal_db, config.state_cache_size); - if state_db.journal_db().is_empty() { - // Sets the correct state root. - state_db = spec.ensure_db_good(state_db, &factories)?; - let mut batch = DBTransaction::new(); - state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash())?; - db.write(batch).map_err(ClientError::Database)?; - } - - let gb = spec.genesis_block(); - let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone())); - let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone())); - - trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()); - - let history = if config.history < MIN_HISTORY_SIZE { - info!(target: "client", "Ignoring pruning history parameter of {}\ - , falling back to minimum of {}", - config.history, MIN_HISTORY_SIZE); - MIN_HISTORY_SIZE - } else { - config.history - }; - - if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) { - warn!("State root not found for block #{} ({:x})", chain.best_block_number(), chain.best_block_hash()); - } - - let engine = spec.engine.clone(); - + miner: Arc, + ) -> Result { let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal()); - let awake = match config.mode { Mode::Dark(..) | Mode::Off => false, _ => true }; - - let registrar_address = engine.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()); - if let Some(ref addr) = registrar_address { - trace!(target: "client", "Found registrar at {}", addr); - } - - let client = Arc::new(Client { - enabled: AtomicBool::new(true), - sleep_state: Mutex::new(SleepState::new(awake)), - liveness: AtomicBool::new(awake), - mode: Mutex::new(config.mode.clone()), - chain: RwLock::new(chain), - tracedb: tracedb, - engine: engine, - pruning: config.pruning.clone(), - verifier: verification::new(config.verifier_type.clone()), - config: config, - db: RwLock::new(db), - state_db: RwLock::new(state_db), - block_queue: block_queue, - report: RwLock::new(Default::default()), + Ok(Importer { import_lock: Mutex::new(()), - miner: miner, - io_channel: Mutex::new(message_channel), - notify: RwLock::new(Vec::new()), - queue_transactions: AtomicUsize::new(0), - last_hashes: RwLock::new(VecDeque::new()), - factories: factories, - history: history, + verifier: verification::new(config.verifier_type.clone()), + block_queue, + miner, ancient_verifier: Mutex::new(None), - on_user_defaults_change: Mutex::new(None), - registrar: registry::Registry::default(), - registrar_address, - exit_handler: Mutex::new(None), - }); - - // prune old states. - { - let state_db = client.state_db.read().boxed_clone(); - let chain = client.chain.read(); - client.prune_ancient(state_db, &chain)?; - } - - // ensure genesis epoch proof in the DB. - { - let chain = client.chain.read(); - let gh = spec.genesis_header(); - if chain.epoch_transition(0, gh.hash()).is_none() { - trace!(target: "client", "No genesis transition found."); - - let proof = client.with_proving_caller( - BlockId::Number(0), - |call| client.engine.genesis_epoch_data(&gh, call) - ); - let proof = match proof { - Ok(proof) => proof, - Err(e) => { - warn!(target: "client", "Error generating genesis epoch data: {}. Snapshots generated may not be complete.", e); - Vec::new() - } - }; - - debug!(target: "client", "Obtained genesis transition proof: {:?}", proof); - - let mut batch = DBTransaction::new(); - chain.insert_epoch_transition(&mut batch, 0, EpochTransition { - block_hash: gh.hash(), - block_number: 0, - proof: proof, - }); - - client.db.read().write_buffered(batch); - } - } - - // ensure buffered changes are flushed. - client.db.read().flush().map_err(ClientError::Database)?; - Ok(client) - } - - /// Wakes up client if it's a sleep. - pub fn keep_alive(&self) { - let should_wake = match *self.mode.lock() { - Mode::Dark(..) | Mode::Passive(..) => true, - _ => false, - }; - if should_wake { - self.wake_up(); - (*self.sleep_state.lock()).last_activity = Some(Instant::now()); - } - } - - /// Adds an actor to be notified on certain events - pub fn add_notify(&self, target: Arc) { - self.notify.write().push(Arc::downgrade(&target)); - } - - /// Set a closure to call when we want to restart the client - pub fn set_exit_handler(&self, f: F) where F: Fn(bool, Option) + 'static + Send { - *self.exit_handler.lock() = Some(Box::new(f)); - } - - /// Returns engine reference. - pub fn engine(&self) -> &EthEngine { - &*self.engine - } - - fn notify(&self, f: F) where F: Fn(&ChainNotify) { - for np in self.notify.read().iter() { - if let Some(n) = np.upgrade() { - f(&*n); - } - } - } - - /// Register an action to be done if a mode/spec_name change happens. - pub fn on_user_defaults_change(&self, f: F) where F: 'static + FnMut(Option) + Send { - *self.on_user_defaults_change.lock() = Some(Box::new(f)); - } - - /// Flush the block import queue. - pub fn flush_queue(&self) { - self.block_queue.flush(); - while !self.block_queue.queue_info().is_empty() { - self.import_verified_blocks(); - } - } - - /// The env info as of the best block. - pub fn latest_env_info(&self) -> EnvInfo { - self.env_info(BlockId::Latest).expect("Best block header always stored; qed") - } - - /// The env info as of a given block. - /// returns `None` if the block unknown. - pub fn env_info(&self, id: BlockId) -> Option { - self.block_header(id).map(|header| { - EnvInfo { - number: header.number(), - author: header.author(), - timestamp: header.timestamp(), - difficulty: header.difficulty(), - last_hashes: self.build_last_hashes(header.parent_hash()), - gas_used: U256::default(), - gas_limit: header.gas_limit(), - } - }) - } - - fn build_last_hashes(&self, parent_hash: H256) -> Arc { - { - let hashes = self.last_hashes.read(); - if hashes.front().map_or(false, |h| h == &parent_hash) { - let mut res = Vec::from(hashes.clone()); - res.resize(256, H256::default()); - return Arc::new(res); - } - } - let mut last_hashes = LastHashes::new(); - last_hashes.resize(256, H256::default()); - last_hashes[0] = parent_hash; - let chain = self.chain.read(); - for i in 0..255 { - match chain.block_details(&last_hashes[i]) { - Some(details) => { - last_hashes[i + 1] = details.parent.clone(); - }, - None => break, - } - } - let mut cached_hashes = self.last_hashes.write(); - *cached_hashes = VecDeque::from(last_hashes.clone()); - Arc::new(last_hashes) - } - - fn check_and_close_block(&self, block: &PreverifiedBlock) -> Result { - let engine = &*self.engine; - let header = &block.header; - - let chain = self.chain.read(); - // Check the block isn't so old we won't be able to enact it. - let best_block_number = chain.best_block_number(); - if self.pruning_info().earliest_state > header.number() { - warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number); - return Err(()); - } - - // Check if parent is in chain - let parent = match chain.block_header(header.parent_hash()) { - Some(h) => h, - None => { - warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash()); - return Err(()); - } - }; - - // Verify Block Family - let verify_family_result = self.verifier.verify_block_family( - header, - &parent, - engine, - Some((&block.bytes, &block.transactions, &**chain, self)), - ); - - if let Err(e) = verify_family_result { - warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - return Err(()); - }; - - let verify_external_result = self.verifier.verify_block_external(header, engine); - if let Err(e) = verify_external_result { - warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - return Err(()); - }; - - // Enact Verified Block - let last_hashes = self.build_last_hashes(header.parent_hash().clone()); - let db = self.state_db.read().boxed_clone_canon(header.parent_hash()); - - let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some(); - let enact_result = enact_verified(block, + rng: Mutex::new(OsRng::new()?), engine, - self.tracedb.read().tracing_enabled(), - db, - &parent, - last_hashes, - self.factories.clone(), - is_epoch_begin, - ); - let mut locked_block = enact_result.map_err(|e| { - warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - })?; - - if header.number() < self.engine().params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() { - locked_block = locked_block.strip_receipts(); - } - - // Final Verification - if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) { - warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - return Err(()); - } - - Ok(locked_block) + }) } fn calculate_enacted_retracted(&self, import_results: &[ImportRoute]) -> (Vec, Vec) { @@ -496,10 +271,10 @@ impl Client { } /// This is triggered by a message coming from a block queue when the block is ready for insertion - pub fn import_verified_blocks(&self) -> usize { + pub fn import_verified_blocks(&self, client: &Client) -> usize { // Shortcut out if we know we're incapable of syncing the chain. - if !self.enabled.load(AtomicOrdering::Relaxed) { + if !client.enabled.load(AtomicOrdering::Relaxed) { return 0; } @@ -525,64 +300,141 @@ impl Client { invalid_blocks.insert(header.hash()); continue; } - if let Ok(closed_block) = self.check_and_close_block(&block) { + if let Ok(closed_block) = self.check_and_close_block(&block, client) { if self.engine.is_proposal(&block.header) { self.block_queue.mark_as_good(&[header.hash()]); proposed_blocks.push(block.bytes); } else { imported_blocks.push(header.hash()); - let route = self.commit_block(closed_block, &header, &block.bytes); + let route = self.commit_block(closed_block, &header, &block.bytes, client); import_results.push(route); - self.report.write().accrue_block(&block); + client.report.write().accrue_block(&block); } } else { invalid_blocks.insert(header.hash()); } } - let imported = imported_blocks.len(); - let invalid_blocks = invalid_blocks.into_iter().collect::>(); + let imported = imported_blocks.len(); + let invalid_blocks = invalid_blocks.into_iter().collect::>(); + + if !invalid_blocks.is_empty() { + self.block_queue.mark_as_bad(&invalid_blocks); + } + let is_empty = self.block_queue.mark_as_good(&imported_blocks); + let duration_ns = precise_time_ns() - start; + (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration_ns, is_empty) + }; + + { + if !imported_blocks.is_empty() && is_empty { + let (enacted, retracted) = self.calculate_enacted_retracted(&import_results); + + if is_empty { + self.miner.chain_new_blocks(client, &imported_blocks, &invalid_blocks, &enacted, &retracted); + } + + client.notify(|notify| { + notify.new_blocks( + imported_blocks.clone(), + invalid_blocks.clone(), + enacted.clone(), + retracted.clone(), + Vec::new(), + proposed_blocks.clone(), + duration, + ); + }); + } + } + + client.db.read().flush().expect("DB flush failed."); + imported + } + + fn check_and_close_block(&self, block: &PreverifiedBlock, client: &Client) -> Result { + let engine = &*self.engine; + let header = &block.header; + + let chain = client.chain.read(); + // Check the block isn't so old we won't be able to enact it. + let best_block_number = chain.best_block_number(); + if client.pruning_info().earliest_state > header.number() { + warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number); + return Err(()); + } + + // Check if parent is in chain + let parent = match chain.block_header(header.parent_hash()) { + Some(h) => h, + None => { + warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash()); + return Err(()); + } + }; + + // Verify Block Family + let verify_family_result = self.verifier.verify_block_family( + header, + &parent, + engine, + Some(verification::FullFamilyParams { + block_bytes: &block.bytes, + transactions: &block.transactions, + block_provider: &**chain, + client + }), + ); - if !invalid_blocks.is_empty() { - self.block_queue.mark_as_bad(&invalid_blocks); - } - let is_empty = self.block_queue.mark_as_good(&imported_blocks); - let duration_ns = precise_time_ns() - start; - (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration_ns, is_empty) + if let Err(e) = verify_family_result { + warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + return Err(()); }; - { - if !imported_blocks.is_empty() && is_empty { - let (enacted, retracted) = self.calculate_enacted_retracted(&import_results); + let verify_external_result = self.verifier.verify_block_external(header, engine); + if let Err(e) = verify_external_result { + warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + return Err(()); + }; - if is_empty { - self.miner.chain_new_blocks(self, &imported_blocks, &invalid_blocks, &enacted, &retracted); - } + // Enact Verified Block + let last_hashes = client.build_last_hashes(header.parent_hash()); + let db = client.state_db.read().boxed_clone_canon(header.parent_hash()); - self.notify(|notify| { - notify.new_blocks( - imported_blocks.clone(), - invalid_blocks.clone(), - enacted.clone(), - retracted.clone(), - Vec::new(), - proposed_blocks.clone(), - duration, - ); - }); - } + let is_epoch_begin = chain.epoch_transition(parent.number(), *header.parent_hash()).is_some(); + let enact_result = enact_verified(block, + engine, + client.tracedb.read().tracing_enabled(), + db, + &parent, + last_hashes, + client.factories.clone(), + is_epoch_begin, + ); + let mut locked_block = enact_result.map_err(|e| { + warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + })?; + + if header.number() < engine.params().validate_receipts_transition && header.receipts_root() != locked_block.block().header().receipts_root() { + locked_block = locked_block.strip_receipts(); } - self.db.read().flush().expect("DB flush failed."); - imported + // Final Verification + if let Err(e) = self.verifier.verify_block_final(header, locked_block.block().header()) { + warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); + return Err(()); + } + + Ok(locked_block) } /// Import a block with transaction receipts. - /// The block is guaranteed to be the next best blocks in the first block sequence. - /// Does no sealing or transaction validation. - fn import_old_block(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result { + /// + /// The block is guaranteed to be the next best blocks in the + /// first block sequence. Does no sealing or transaction validation. + fn import_old_block(&self, block_bytes: Bytes, receipts_bytes: Bytes, db: &KeyValueDB, chain: &BlockChain) -> Result { let block = BlockView::new(&block_bytes); let header = block.header(); let receipts = ::rlp::decode_list(&receipts_bytes); @@ -591,7 +443,6 @@ impl Client { { trace_time!("import_old_block"); - let chain = self.chain.read(); let mut ancient_verifier = self.ancient_verifier.lock(); { @@ -631,10 +482,10 @@ impl Client { let mut batch = DBTransaction::new(); chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, false, true); // Final commit to the DB - self.db.read().write_buffered(batch); + db.write_buffered(batch); chain.commit(); } - self.db.read().flush().expect("DB flush failed."); + db.flush().expect("DB flush failed."); Ok(hash) } @@ -642,11 +493,11 @@ impl Client { // it is for reconstructing the state transition. // // The header passed is from the original block data and is sealed. - fn commit_block(&self, block: B, header: &Header, block_data: &[u8]) -> ImportRoute where B: IsBlock + Drain { + fn commit_block(&self, block: B, header: &Header, block_data: &[u8], client: &Client) -> ImportRoute where B: IsBlock + Drain { let hash = &header.hash(); let number = header.number(); let parent = header.parent_hash(); - let chain = self.chain.read(); + let chain = client.chain.read(); // Commit results let receipts = block.receipts().to_owned(); @@ -672,12 +523,13 @@ impl Client { &state, &chain, &mut batch, + client ); state.journal_under(&mut batch, number, hash).expect("DB commit failed"); let route = chain.insert_block(&mut batch, block_data, receipts.clone()); - self.tracedb.read().import(&mut batch, TraceImportRequest { + client.tracedb.read().import(&mut batch, TraceImportRequest { traces: traces.into(), block_hash: hash.clone(), block_number: number, @@ -688,14 +540,14 @@ impl Client { let is_canon = route.enacted.last().map_or(false, |h| h == hash); state.sync_cache(&route.enacted, &route.retracted, is_canon); // Final commit to the DB - self.db.read().write_buffered(batch); + client.db.read().write_buffered(batch); chain.commit(); - self.check_epoch_end(&header, &chain); + self.check_epoch_end(&header, &chain, client); - self.update_last_hashes(&parent, hash); + client.update_last_hashes(&parent, hash); - if let Err(e) = self.prune_ancient(state, &chain) { + if let Err(e) = client.prune_ancient(state, &chain) { warn!("Failed to prune ancient state data: {}", e); } @@ -712,6 +564,7 @@ impl Client { state_db: &StateDB, chain: &BlockChain, batch: &mut DBTransaction, + client: &Client, ) { use engines::EpochChange; @@ -734,7 +587,7 @@ impl Client { author: header.author().clone(), timestamp: header.timestamp(), difficulty: header.difficulty().clone(), - last_hashes: self.build_last_hashes(header.parent_hash().clone()), + last_hashes: client.build_last_hashes(header.parent_hash()), gas_used: U256::default(), gas_limit: u64::max_value().into(), }; @@ -744,79 +597,313 @@ impl Client { let backend = ::state::backend::Proving::new(state_db.as_hashdb_mut()); let transaction = - self.contract_call_tx(BlockId::Hash(*header.parent_hash()), addr, data); + client.contract_call_tx(BlockId::Hash(*header.parent_hash()), addr, data); let mut state = State::from_existing( backend, header.state_root().clone(), self.engine.account_start_nonce(header.number()), - self.factories.clone(), + client.factories.clone(), ).expect("state known to be available for just-imported block; qed"); let options = TransactOptions::with_no_tracing().dont_check_nonce(); let res = Executive::new(&mut state, &env_info, self.engine.machine()) .transact(&transaction, options); - let res = match res { - Err(ExecutionError::Internal(e)) => - Err(format!("Internal error: {}", e)), - Err(e) => { - trace!(target: "client", "Proved call failed: {}", e); - Ok((Vec::new(), state.drop().1.extract_proof())) - } - Ok(res) => Ok((res.output, state.drop().1.extract_proof())), - }; + let res = match res { + Err(ExecutionError::Internal(e)) => + Err(format!("Internal error: {}", e)), + Err(e) => { + trace!(target: "client", "Proved call failed: {}", e); + Ok((Vec::new(), state.drop().1.extract_proof())) + } + Ok(res) => Ok((res.output, state.drop().1.extract_proof())), + }; + + res.map(|(output, proof)| (output, proof.into_iter().map(|x| x.into_vec()).collect())) + }; + + match with_state.generate_proof(&call) { + Ok(proof) => proof, + Err(e) => { + warn!(target: "client", "Failed to generate transition proof for block {}: {}", hash, e); + warn!(target: "client", "Snapshots produced by this client may be incomplete"); + Vec::new() + } + } + } + }; + + debug!(target: "client", "Block {} signals epoch end.", hash); + + let pending = PendingTransition { proof: proof }; + chain.insert_pending_transition(batch, hash, pending); + }, + EpochChange::No => {}, + EpochChange::Unsure(_) => { + warn!(target: "client", "Detected invalid engine implementation."); + warn!(target: "client", "Engine claims to require more block data, but everything provided."); + } + } + } + + // check for ending of epoch and write transition if it occurs. + fn check_epoch_end<'a>(&self, header: &'a Header, chain: &BlockChain, client: &Client) { + let is_epoch_end = self.engine.is_epoch_end( + header, + &(|hash| chain.block_header(&hash)), + &(|hash| chain.get_pending_transition(hash)), // TODO: limit to current epoch. + ); + + if let Some(proof) = is_epoch_end { + debug!(target: "client", "Epoch transition at block {}", header.hash()); + + let mut batch = DBTransaction::new(); + chain.insert_epoch_transition(&mut batch, header.number(), EpochTransition { + block_hash: header.hash(), + block_number: header.number(), + proof: proof, + }); + + // always write the batch directly since epoch transition proofs are + // fetched from a DB iterator and DB iterators are only available on + // flushed data. + client.db.read().write(batch).expect("DB flush failed"); + } + } +} + +impl Client { + /// Create a new client with given parameters. + /// The database is assumed to have been initialized with the correct columns. + pub fn new( + config: ClientConfig, + spec: &Spec, + db: Arc, + miner: Arc, + message_channel: IoChannel, + ) -> Result, ::error::Error> { + let trie_spec = match config.fat_db { + true => TrieSpec::Fat, + false => TrieSpec::Secure, + }; + + let trie_factory = TrieFactory::new(trie_spec); + let factories = Factories { + vm: VmFactory::new(config.vm_type.clone(), config.jump_table_size), + trie: trie_factory, + accountdb: Default::default(), + }; + + let journal_db = journaldb::new(db.clone(), config.pruning, ::db::COL_STATE); + let mut state_db = StateDB::new(journal_db, config.state_cache_size); + if state_db.journal_db().is_empty() { + // Sets the correct state root. + state_db = spec.ensure_db_good(state_db, &factories)?; + let mut batch = DBTransaction::new(); + state_db.journal_under(&mut batch, 0, &spec.genesis_header().hash())?; + db.write(batch).map_err(ClientError::Database)?; + } + + let gb = spec.genesis_block(); + let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone())); + let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone())); + + trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()); + + let history = if config.history < MIN_HISTORY_SIZE { + info!(target: "client", "Ignoring pruning history parameter of {}\ + , falling back to minimum of {}", + config.history, MIN_HISTORY_SIZE); + MIN_HISTORY_SIZE + } else { + config.history + }; + + if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.journal_db().contains(h.state_root())) { + warn!("State root not found for block #{} ({:x})", chain.best_block_number(), chain.best_block_hash()); + } + + let engine = spec.engine.clone(); + + let awake = match config.mode { Mode::Dark(..) | Mode::Off => false, _ => true }; + + let importer = Importer::new(&config, engine.clone(), message_channel.clone(), miner)?; + + let registrar_address = engine.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()); + if let Some(ref addr) = registrar_address { + trace!(target: "client", "Found registrar at {}", addr); + } + + let client = Arc::new(Client { + enabled: AtomicBool::new(true), + sleep_state: Mutex::new(SleepState::new(awake)), + liveness: AtomicBool::new(awake), + mode: Mutex::new(config.mode.clone()), + chain: RwLock::new(chain), + tracedb: tracedb, + engine: engine, + pruning: config.pruning.clone(), + config: config, + db: RwLock::new(db), + state_db: RwLock::new(state_db), + report: RwLock::new(Default::default()), + io_channel: Mutex::new(message_channel), + notify: RwLock::new(Vec::new()), + queue_transactions: AtomicUsize::new(0), + last_hashes: RwLock::new(VecDeque::new()), + factories: factories, + history: history, + on_user_defaults_change: Mutex::new(None), + registrar: registry::Registry::default(), + registrar_address, + exit_handler: Mutex::new(None), + importer, + }); + + // prune old states. + { + let state_db = client.state_db.read().boxed_clone(); + let chain = client.chain.read(); + client.prune_ancient(state_db, &chain)?; + } + + // ensure genesis epoch proof in the DB. + { + let chain = client.chain.read(); + let gh = spec.genesis_header(); + if chain.epoch_transition(0, gh.hash()).is_none() { + trace!(target: "client", "No genesis transition found."); + + let proof = client.with_proving_caller( + BlockId::Number(0), + |call| client.engine.genesis_epoch_data(&gh, call) + ); + let proof = match proof { + Ok(proof) => proof, + Err(e) => { + warn!(target: "client", "Error generating genesis epoch data: {}. Snapshots generated may not be complete.", e); + Vec::new() + } + }; + + debug!(target: "client", "Obtained genesis transition proof: {:?}", proof); + + let mut batch = DBTransaction::new(); + chain.insert_epoch_transition(&mut batch, 0, EpochTransition { + block_hash: gh.hash(), + block_number: 0, + proof: proof, + }); + + client.db.read().write_buffered(batch); + } + } + + // ensure buffered changes are flushed. + client.db.read().flush().map_err(ClientError::Database)?; + Ok(client) + } + + /// Wakes up client if it's a sleep. + pub fn keep_alive(&self) { + let should_wake = match *self.mode.lock() { + Mode::Dark(..) | Mode::Passive(..) => true, + _ => false, + }; + if should_wake { + self.wake_up(); + (*self.sleep_state.lock()).last_activity = Some(Instant::now()); + } + } - res.map(|(output, proof)| (output, proof.into_iter().map(|x| x.into_vec()).collect())) - }; + /// Adds an actor to be notified on certain events + pub fn add_notify(&self, target: Arc) { + self.notify.write().push(Arc::downgrade(&target)); + } - match with_state.generate_proof(&call) { - Ok(proof) => proof, - Err(e) => { - warn!(target: "client", "Failed to generate transition proof for block {}: {}", hash, e); - warn!(target: "client", "Snapshots produced by this client may be incomplete"); - Vec::new() - } - } - } - }; + /// Set a closure to call when we want to restart the client + pub fn set_exit_handler(&self, f: F) where F: Fn(bool, Option) + 'static + Send { + *self.exit_handler.lock() = Some(Box::new(f)); + } - debug!(target: "client", "Block {} signals epoch end.", hash); + /// Returns engine reference. + pub fn engine(&self) -> &EthEngine { + &*self.engine + } - let pending = PendingTransition { proof: proof }; - chain.insert_pending_transition(batch, hash, pending); - }, - EpochChange::No => {}, - EpochChange::Unsure(_) => { - warn!(target: "client", "Detected invalid engine implementation."); - warn!(target: "client", "Engine claims to require more block data, but everything provided."); + fn notify(&self, f: F) where F: Fn(&ChainNotify) { + for np in self.notify.read().iter() { + if let Some(n) = np.upgrade() { + f(&*n); } } } - // check for ending of epoch and write transition if it occurs. - fn check_epoch_end<'a>(&self, header: &'a Header, chain: &BlockChain) { - let is_epoch_end = self.engine.is_epoch_end( - header, - &(|hash| chain.block_header(&hash)), - &(|hash| chain.get_pending_transition(hash)), // TODO: limit to current epoch. - ); + /// Register an action to be done if a mode/spec_name change happens. + pub fn on_user_defaults_change(&self, f: F) where F: 'static + FnMut(Option) + Send { + *self.on_user_defaults_change.lock() = Some(Box::new(f)); + } - if let Some(proof) = is_epoch_end { - debug!(target: "client", "Epoch transition at block {}", header.hash()); + /// Flush the block import queue. + pub fn flush_queue(&self) { + self.importer.block_queue.flush(); + while !self.importer.block_queue.queue_info().is_empty() { + self.import_verified_blocks(); + } + } - let mut batch = DBTransaction::new(); - chain.insert_epoch_transition(&mut batch, header.number(), EpochTransition { - block_hash: header.hash(), - block_number: header.number(), - proof: proof, - }); + /// The env info as of the best block. + pub fn latest_env_info(&self) -> EnvInfo { + self.env_info(BlockId::Latest).expect("Best block header always stored; qed") + } - // always write the batch directly since epoch transition proofs are - // fetched from a DB iterator and DB iterators are only available on - // flushed data. - self.db.read().write(batch).expect("DB flush failed"); + /// The env info as of a given block. + /// returns `None` if the block unknown. + pub fn env_info(&self, id: BlockId) -> Option { + self.block_header(id).map(|header| { + EnvInfo { + number: header.number(), + author: header.author(), + timestamp: header.timestamp(), + difficulty: header.difficulty(), + last_hashes: self.build_last_hashes(&header.parent_hash()), + gas_used: U256::default(), + gas_limit: header.gas_limit(), + } + }) + } + + fn build_last_hashes(&self, parent_hash: &H256) -> Arc { + { + let hashes = self.last_hashes.read(); + if hashes.front().map_or(false, |h| h == parent_hash) { + let mut res = Vec::from(hashes.clone()); + res.resize(256, H256::default()); + return Arc::new(res); + } + } + let mut last_hashes = LastHashes::new(); + last_hashes.resize(256, H256::default()); + last_hashes[0] = parent_hash.clone(); + let chain = self.chain.read(); + for i in 0..255 { + match chain.block_details(&last_hashes[i]) { + Some(details) => { + last_hashes[i + 1] = details.parent.clone(); + }, + None => break, + } } + let mut cached_hashes = self.last_hashes.write(); + *cached_hashes = VecDeque::from(last_hashes.clone()); + Arc::new(last_hashes) + } + + + /// This is triggered by a message coming from a block queue when the block is ready for insertion + pub fn import_verified_blocks(&self) -> usize { + self.importer.import_verified_blocks(self) } // use a state-proving closure for the given block. @@ -890,13 +977,13 @@ impl Client { self.notify(|notify| { notify.transactions_received(hashes.clone(), peer_id); }); - let results = self.miner.import_external_transactions(self, txs); + let results = self.importer.miner.import_external_transactions(self, txs); results.len() } /// Get shared miner reference. pub fn miner(&self) -> Arc { - self.miner.clone() + self.importer.miner.clone() } /// Replace io channel. Useful for testing. @@ -904,6 +991,18 @@ impl Client { *self.io_channel.lock() = io_channel; } + /// Get a copy of the best block's state. + pub fn latest_state(&self) -> State { + let header = self.best_block_header(); + State::from_existing( + self.state_db.read().boxed_clone_canon(&header.hash()), + header.state_root(), + self.engine.account_start_nonce(header.number()), + self.factories.clone() + ) + .expect("State root of best block header always valid.") + } + /// Attempt to get a copy of a specific block's final state. /// /// This will not fail if given BlockId::Latest. @@ -912,8 +1011,7 @@ impl Client { pub fn state_at(&self, id: BlockId) -> Option> { // fast path for latest state. match id.clone() { - BlockId::Pending => return self.miner.pending_state(self.chain.read().best_block_number()).or_else(|| Some(self.state())), - BlockId::Latest => return Some(self.state()), + BlockId::Latest => return Some(self.latest_state()), _ => {}, } @@ -940,25 +1038,15 @@ impl Client { /// This will not fail if given BlockId::Latest. /// Otherwise, this can fail (but may not) if the DB prunes state. pub fn state_at_beginning(&self, id: BlockId) -> Option> { - // fast path for latest state. - match id { - BlockId::Pending => self.state_at(BlockId::Latest), - id => match self.block_number(id) { - None | Some(0) => None, - Some(n) => self.state_at(BlockId::Number(n - 1)), - } + match self.block_number(id) { + None | Some(0) => None, + Some(n) => self.state_at(BlockId::Number(n - 1)), } } /// Get a copy of the best block's state. - pub fn state(&self) -> State { - let header = self.best_block_header(); - State::from_existing( - self.state_db.read().boxed_clone_canon(&header.hash()), - header.state_root(), - self.engine.account_start_nonce(header.number()), - self.factories.clone()) - .expect("State root of best block header always valid.") + pub fn state(&self) -> Box { + Box::new(self.latest_state()) as Box<_> } /// Get info on the cache. @@ -984,7 +1072,7 @@ impl Client { fn check_garbage(&self) { self.chain.read().collect_garbage(); - self.block_queue.collect_garbage(); + self.importer.block_queue.collect_garbage(); self.tracedb.read().collect_garbage(); } @@ -1063,20 +1151,19 @@ impl Client { self.history } - fn block_hash(chain: &BlockChain, miner: &Miner, id: BlockId) -> Option { + fn block_hash(chain: &BlockChain, id: BlockId) -> Option { match id { BlockId::Hash(hash) => Some(hash), BlockId::Number(number) => chain.block_hash(number), BlockId::Earliest => chain.block_hash(0), BlockId::Latest => Some(chain.best_block_hash()), - BlockId::Pending => miner.pending_block_header(chain.best_block_number()).map(|header| header.hash()) } } fn transaction_address(&self, id: TransactionId) -> Option { match id { TransactionId::Hash(ref hash) => self.chain.read().transaction_address(hash), - TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), &self.miner, id).map(|hash| TransactionAddress { + TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), id).map(|hash| TransactionAddress { block_hash: hash, index: index, }) @@ -1161,61 +1248,207 @@ impl Client { } } - fn block_number_ref(&self, id: &BlockId) -> Option { - match *id { - BlockId::Number(number) => Some(number), - BlockId::Hash(ref hash) => self.chain.read().block_number(hash), - BlockId::Earliest => Some(0), - BlockId::Latest => Some(self.chain.read().best_block_number()), - BlockId::Pending => Some(self.chain.read().best_block_number() + 1), + fn block_number_ref(&self, id: &BlockId) -> Option { + match *id { + BlockId::Number(number) => Some(number), + BlockId::Hash(ref hash) => self.chain.read().block_number(hash), + BlockId::Earliest => Some(0), + BlockId::Latest => Some(self.chain.read().best_block_number()), + } + } +} + +impl snapshot::DatabaseRestore for Client { + /// Restart the client with a new backend + fn restore_db(&self, new_db: &str) -> Result<(), EthcoreError> { + trace!(target: "snapshot", "Replacing client database with {:?}", new_db); + + let _import_lock = self.importer.import_lock.lock(); + let mut state_db = self.state_db.write(); + let mut chain = self.chain.write(); + let mut tracedb = self.tracedb.write(); + self.importer.miner.clear(); + let db = self.db.write(); + db.restore(new_db)?; + + let cache_size = state_db.cache_size(); + *state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size); + *chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone())); + *tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone()); + Ok(()) + } +} + +impl Nonce for Client { + fn nonce(&self, address: &Address, id: BlockId) -> Option { + self.state_at(id).and_then(|s| s.nonce(address).ok()) + } +} + +impl Balance for Client { + fn balance(&self, address: &Address, state: StateOrBlock) -> Option { + match state { + StateOrBlock::State(s) => s.balance(address).ok(), + StateOrBlock::Block(id) => self.state_at(id).and_then(|s| s.balance(address).ok()) + } + } +} + +impl AccountData for Client {} + +impl ChainInfo for Client { + fn chain_info(&self) -> BlockChainInfo { + let mut chain_info = self.chain.read().chain_info(); + chain_info.pending_total_difficulty = chain_info.total_difficulty + self.importer.block_queue.total_difficulty(); + chain_info + } +} + +impl BlockInfo for Client { + fn block_header(&self, id: BlockId) -> Option<::encoded::Header> { + let chain = self.chain.read(); + + Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash)) + } + + fn best_block_header(&self) -> encoded::Header { + self.chain.read().best_block_header() + } + + fn block(&self, id: BlockId) -> Option { + let chain = self.chain.read(); + + Self::block_hash(&chain, id).and_then(|hash| { + chain.block(&hash) + }) + } + + fn code_hash(&self, address: &Address, id: BlockId) -> Option { + self.state_at(id).and_then(|s| s.code_hash(address).ok()) + } +} + +impl TransactionInfo for Client { + fn transaction_block(&self, id: TransactionId) -> Option { + self.transaction_address(id).map(|addr| addr.block_hash) + } +} + +impl BlockChainTrait for Client {} + +impl RegistryInfo for Client { + fn registry_address(&self, name: String, block: BlockId) -> Option
{ + let address = self.registrar_address?; + + self.registrar.functions() + .get_address() + .call(keccak(name.as_bytes()), "A", &|data| self.call_contract(block, address, data)) + .ok() + .and_then(|a| if a.is_zero() { + None + } else { + Some(a) + }) + } +} + +impl CallContract for Client { + fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result { + let state_pruned = || CallError::StatePruned.to_string(); + let state = &mut self.state_at(block_id).ok_or_else(&state_pruned)?; + let header = self.block_header(block_id).ok_or_else(&state_pruned)?; + + let transaction = self.contract_call_tx(block_id, address, data); + + self.call(&transaction, Default::default(), state, &header.decode()) + .map_err(|e| format!("{:?}", e)) + .map(|executed| executed.output) + } +} + +impl ImportBlock for Client { + fn import_block(&self, bytes: Bytes) -> Result { + use verification::queue::kind::BlockLike; + use verification::queue::kind::blocks::Unverified; + + // create unverified block here so the `keccak` calculation can be cached. + let unverified = Unverified::new(bytes); + + { + if self.chain.read().is_known(&unverified.hash()) { + return Err(BlockImportError::Import(ImportError::AlreadyInChain)); + } + let status = self.block_status(BlockId::Hash(unverified.parent_hash())); + if status == BlockStatus::Unknown || status == BlockStatus::Pending { + return Err(BlockImportError::Block(BlockError::UnknownParent(unverified.parent_hash()))); + } + } + Ok(self.importer.block_queue.import(unverified)?) + } + + fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result { + { + // check block order + let header = BlockView::new(&block_bytes).header_view(); + if self.chain.read().is_known(&header.hash()) { + return Err(BlockImportError::Import(ImportError::AlreadyInChain)); + } + let status = self.block_status(BlockId::Hash(header.parent_hash())); + if status == BlockStatus::Unknown || status == BlockStatus::Pending { + return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash()))); + } } + + self.importer.import_old_block(block_bytes, receipts_bytes, &**self.db.read(), &*self.chain.read()).map_err(Into::into) } } -impl snapshot::DatabaseRestore for Client { - /// Restart the client with a new backend - fn restore_db(&self, new_db: &str) -> Result<(), EthcoreError> { - trace!(target: "snapshot", "Replacing client database with {:?}", new_db); +impl StateClient for Client { + type State = State<::state_db::StateDB>; - let _import_lock = self.import_lock.lock(); - let mut state_db = self.state_db.write(); - let mut chain = self.chain.write(); - let mut tracedb = self.tracedb.write(); - self.miner.clear(); - let db = self.db.write(); - db.restore(new_db)?; + fn latest_state(&self) -> Self::State { + Client::latest_state(self) + } - let cache_size = state_db.cache_size(); - *state_db = StateDB::new(journaldb::new(db.clone(), self.pruning, ::db::COL_STATE), cache_size); - *chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone())); - *tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone()); - Ok(()) + fn state_at(&self, id: BlockId) -> Option { + Client::state_at(self, id) } } -impl BlockChainClient for Client { - fn call(&self, transaction: &SignedTransaction, analytics: CallAnalytics, block: BlockId) -> Result { - let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?; - env_info.gas_limit = U256::max_value(); - - // that's just a copy of the state. - let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; +impl Call for Client { + type State = State<::state_db::StateDB>; + + fn call(&self, transaction: &SignedTransaction, analytics: CallAnalytics, state: &mut Self::State, header: &Header) -> Result { + let env_info = EnvInfo { + number: header.number(), + author: header.author().clone(), + timestamp: header.timestamp(), + difficulty: header.difficulty().clone(), + last_hashes: self.build_last_hashes(header.parent_hash()), + gas_used: U256::default(), + gas_limit: U256::max_value(), + }; let machine = self.engine.machine(); - Self::do_virtual_call(machine, &env_info, &mut state, transaction, analytics) + Self::do_virtual_call(&machine, &env_info, state, transaction, analytics) } - fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result, CallError> { - let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?; - env_info.gas_limit = U256::max_value(); + fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], state: &mut Self::State, header: &Header) -> Result, CallError> { + let mut env_info = EnvInfo { + number: header.number(), + author: header.author().clone(), + timestamp: header.timestamp(), + difficulty: header.difficulty().clone(), + last_hashes: self.build_last_hashes(header.parent_hash()), + gas_used: U256::default(), + gas_limit: U256::max_value(), + }; - // that's just a copy of the state. - let mut state = self.state_at(block).ok_or(CallError::StatePruned)?; let mut results = Vec::with_capacity(transactions.len()); let machine = self.engine.machine(); for &(ref t, analytics) in transactions { - let ret = Self::do_virtual_call(machine, &env_info, &mut state, t, analytics)?; + let ret = Self::do_virtual_call(machine, &env_info, state, t, analytics)?; env_info.gas_used = ret.cumulative_gas_used; results.push(ret); } @@ -1223,17 +1456,24 @@ impl BlockChainClient for Client { Ok(results) } - fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result { + fn estimate_gas(&self, t: &SignedTransaction, state: &Self::State, header: &Header) -> Result { let (mut upper, max_upper, env_info) = { - let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?; - let init = env_info.gas_limit; + let init = *header.gas_limit(); let max = init * U256::from(10); - env_info.gas_limit = max; + + let env_info = EnvInfo { + number: header.number(), + author: header.author().clone(), + timestamp: header.timestamp(), + difficulty: header.difficulty().clone(), + last_hashes: self.build_last_hashes(header.parent_hash()), + gas_used: U256::default(), + gas_limit: max, + }; + (init, max, env_info) }; - // that's just a copy of the state. - let original_state = self.state_at(block).ok_or(CallError::StatePruned)?; let sender = t.sender(); let options = || TransactOptions::with_tracing().dont_check_nonce(); @@ -1242,8 +1482,8 @@ impl BlockChainClient for Client { tx.gas = gas; let tx = tx.fake_sign(sender); - let mut state = original_state.clone(); - Ok(Executive::new(&mut state, &env_info, self.engine.machine()) + let mut clone = state.clone(); + Ok(Executive::new(&mut clone, &env_info, self.engine.machine()) .transact_virtual(&tx, options()) .map(|r| r.exception.is_none()) .unwrap_or(false)) @@ -1286,7 +1526,15 @@ impl BlockChainClient for Client { trace!(target: "estimate_gas", "estimate_gas chopping {} .. {}", lower, upper); binary_chop(lower, upper, cond) } +} + +impl EngineInfo for Client { + fn engine(&self) -> &EthEngine { + Client::engine(self) + } +} +impl BlockChainClient for Client { fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result { let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?; let block = BlockId::Hash(address.block_hash); @@ -1365,24 +1613,6 @@ impl BlockChainClient for Client { } } - fn best_block_header(&self) -> encoded::Header { - self.chain.read().best_block_header() - } - - fn block_header(&self, id: BlockId) -> Option<::encoded::Header> { - let chain = self.chain.read(); - - if let BlockId::Pending = id { - if let Some(block) = self.miner.pending_block(chain.best_block_number()) { - return Some(encoded::Header::new(block.header.rlp(Seal::Without))); - } - // fall back to latest - return self.block_header(BlockId::Latest); - } - - Self::block_hash(&chain, &self.miner, id).and_then(|hash| chain.block_header_data(&hash)) - } - fn block_number(&self, id: BlockId) -> Option { self.block_number_ref(&id) } @@ -1390,63 +1620,22 @@ impl BlockChainClient for Client { fn block_body(&self, id: BlockId) -> Option { let chain = self.chain.read(); - if let BlockId::Pending = id { - if let Some(block) = self.miner.pending_block(chain.best_block_number()) { - return Some(encoded::Body::new(BlockChain::block_to_body(&block.rlp_bytes(Seal::Without)))); - } - // fall back to latest - return self.block_body(BlockId::Latest); - } - - Self::block_hash(&chain, &self.miner, id).and_then(|hash| chain.block_body(&hash)) - } - - fn block(&self, id: BlockId) -> Option { - let chain = self.chain.read(); - - if let BlockId::Pending = id { - if let Some(block) = self.miner.pending_block(chain.best_block_number()) { - return Some(encoded::Block::new(block.rlp_bytes(Seal::Without))); - } - // fall back to latest - return self.block(BlockId::Latest); - } - - Self::block_hash(&chain, &self.miner, id).and_then(|hash| { - chain.block(&hash) - }) + Self::block_hash(&chain, id).and_then(|hash| chain.block_body(&hash)) } fn block_status(&self, id: BlockId) -> BlockStatus { - if let BlockId::Pending = id { - return BlockStatus::Pending; - } - let chain = self.chain.read(); - match Self::block_hash(&chain, &self.miner, id) { + match Self::block_hash(&chain, id) { Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain, - Some(hash) => self.block_queue.status(&hash).into(), + Some(hash) => self.importer.block_queue.status(&hash).into(), None => BlockStatus::Unknown } } fn block_total_difficulty(&self, id: BlockId) -> Option { let chain = self.chain.read(); - if let BlockId::Pending = id { - let latest_difficulty = self.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed"); - let pending_difficulty = self.miner.pending_block_header(chain.best_block_number()).map(|header| *header.difficulty()); - if let Some(difficulty) = pending_difficulty { - return Some(difficulty + latest_difficulty); - } - // fall back to latest - return Some(latest_difficulty); - } - - Self::block_hash(&chain, &self.miner, id).and_then(|hash| chain.block_details(&hash)).map(|d| d.total_difficulty) - } - fn nonce(&self, address: &Address, id: BlockId) -> Option { - self.state_at(id).and_then(|s| s.nonce(address).ok()) + Self::block_hash(&chain, id).and_then(|hash| chain.block_details(&hash)).map(|d| d.total_difficulty) } fn storage_root(&self, address: &Address, id: BlockId) -> Option { @@ -1455,23 +1644,24 @@ impl BlockChainClient for Client { fn block_hash(&self, id: BlockId) -> Option { let chain = self.chain.read(); - Self::block_hash(&chain, &self.miner, id) - } - - fn code(&self, address: &Address, id: BlockId) -> Option> { - self.state_at(id).and_then(|s| s.code(address).ok()).map(|c| c.map(|c| (&*c).clone())) + Self::block_hash(&chain, id) } - fn code_hash(&self, address: &Address, id: BlockId) -> Option { - self.state_at(id).and_then(|s| s.code_hash(address).ok()) - } + fn code(&self, address: &Address, state: StateOrBlock) -> Option> { + let result = match state { + StateOrBlock::State(s) => s.code(address).ok(), + StateOrBlock::Block(id) => self.state_at(id).and_then(|s| s.code(address).ok()) + }; - fn balance(&self, address: &Address, id: BlockId) -> Option { - self.state_at(id).and_then(|s| s.balance(address).ok()) + // Converting from `Option>>` to `Option>` + result.map(|c| c.map(|c| (&*c).clone())) } - fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option { - self.state_at(id).and_then(|s| s.storage_at(address, position).ok()) + fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option { + match state { + StateOrBlock::State(s) => s.storage_at(address, position).ok(), + StateOrBlock::Block(id) => self.state_at(id).and_then(|s| s.storage_at(address, position).ok()) + } } fn list_accounts(&self, id: BlockId, after: Option<&Address>, count: u64) -> Option> { @@ -1560,10 +1750,6 @@ impl BlockChainClient for Client { self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address)) } - fn transaction_block(&self, id: TransactionId) -> Option { - self.transaction_address(id).map(|addr| addr.block_hash) - } - fn uncle(&self, id: UncleId) -> Option { let index = id.position; self.block_body(id.block).and_then(|body| body.view().uncle_rlp_at(index)) @@ -1613,52 +1799,12 @@ impl BlockChainClient for Client { self.chain.read().block_receipts(hash).map(|receipts| ::rlp::encode(&receipts).into_vec()) } - fn import_block(&self, bytes: Bytes) -> Result { - use verification::queue::kind::BlockLike; - use verification::queue::kind::blocks::Unverified; - - // create unverified block here so the `keccak` calculation can be cached. - let unverified = Unverified::new(bytes); - - { - if self.chain.read().is_known(&unverified.hash()) { - return Err(BlockImportError::Import(ImportError::AlreadyInChain)); - } - let status = self.block_status(BlockId::Hash(unverified.parent_hash())); - if status == BlockStatus::Unknown || status == BlockStatus::Pending { - return Err(BlockImportError::Block(BlockError::UnknownParent(unverified.parent_hash()))); - } - } - Ok(self.block_queue.import(unverified)?) - } - - fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result { - { - // check block order - let header = BlockView::new(&block_bytes).header_view(); - if self.chain.read().is_known(&header.hash()) { - return Err(BlockImportError::Import(ImportError::AlreadyInChain)); - } - let status = self.block_status(BlockId::Hash(header.parent_hash())); - if status == BlockStatus::Unknown || status == BlockStatus::Pending { - return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash()))); - } - } - self.import_old_block(block_bytes, receipts_bytes).map_err(Into::into) - } - fn queue_info(&self) -> BlockQueueInfo { - self.block_queue.queue_info() + self.importer.block_queue.queue_info() } fn clear_queue(&self) { - self.block_queue.clear(); - } - - fn chain_info(&self) -> BlockChainInfo { - let mut chain_info = self.chain.read().chain_info(); - chain_info.pending_total_difficulty = chain_info.total_difficulty + self.block_queue.total_difficulty(); - chain_info + self.importer.block_queue.clear(); } fn additional_params(&self) -> BTreeMap { @@ -1718,7 +1864,7 @@ impl BlockChainClient for Client { } fn last_hashes(&self) -> LastHashes { - (*self.build_last_hashes(self.chain.read().best_block_hash())).clone() + (*self.build_last_hashes(&self.chain.read().best_block_hash())).clone() } fn queue_transactions(&self, transactions: Vec, peer_id: usize) { @@ -1744,7 +1890,7 @@ impl BlockChainClient for Client { let chain = self.chain.read(); (chain.best_block_number(), chain.best_block_timestamp()) }; - self.miner.ready_transactions(number, timestamp) + self.importer.miner.ready_transactions(number, timestamp) } fn queue_consensus_message(&self, message: Bytes) { @@ -1775,64 +1921,60 @@ impl BlockChainClient for Client { } } - fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result { - let transaction = self.contract_call_tx(block_id, address, data); - - self.call(&transaction, Default::default(), block_id) - .map_err(|e| format!("{:?}", e)) - .map(|executed| { - executed.output - }) - } - fn transact_contract(&self, address: Address, data: Bytes) -> Result { let transaction = Transaction { - nonce: self.latest_nonce(&self.miner.author()), + nonce: self.latest_nonce(&self.importer.miner.author()), action: Action::Call(address), - gas: self.miner.gas_floor_target(), - gas_price: self.miner.sensible_gas_price(), + gas: self.importer.miner.gas_floor_target(), + gas_price: self.importer.miner.sensible_gas_price(), value: U256::zero(), data: data, }; let chain_id = self.engine.signing_chain_id(&self.latest_env_info()); let signature = self.engine.sign(transaction.hash(chain_id))?; let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; - self.miner.import_own_transaction(self, signed.into()) + self.importer.miner.import_own_transaction(self, signed.into()) } fn registrar_address(&self) -> Option
{ self.registrar_address.clone() } - fn registry_address(&self, name: String, block: BlockId) -> Option
{ - let address = match self.registrar_address { - Some(address) => address, - None => return None, - }; - - self.registrar.functions() - .get_address() - .call(keccak(name.as_bytes()), "A", &|data| self.call_contract(block, address, data)) - .ok() - .and_then(|a| if a.is_zero() { - None - } else { - Some(a) - }) - } - fn eip86_transition(&self) -> u64 { self.engine().params().eip86_transition } } -impl MiningBlockChainClient for Client { - fn as_block_chain_client(&self) -> &BlockChainClient { self } +impl ReopenBlock for Client { + fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { + let engine = &*self.engine; + let mut block = block.reopen(engine); + let max_uncles = engine.maximum_uncle_count(block.header().number()); + if block.uncles().len() < max_uncles { + let chain = self.chain.read(); + let h = chain.best_block_hash(); + // Add new uncles + let uncles = chain + .find_uncle_hashes(&h, engine.maximum_uncle_age()) + .unwrap_or_else(Vec::new); - fn latest_schedule(&self) -> Schedule { - self.engine.schedule(self.latest_env_info().number) + for h in uncles { + if !block.uncles().iter().any(|header| header.hash() == h) { + let uncle = chain.block_header(&h).expect("find_uncle_hashes only returns hashes for existing headers; qed"); + block.push_uncle(uncle).expect("pushing up to maximum_uncle_count; + push_uncle is not ok only if more than maximum_uncle_count is pushed; + so all push_uncle are Ok; + qed"); + if block.uncles().len() >= max_uncles { break } + } + } + + } + block } +} +impl PrepareOpenBlock for Client { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { let engine = &*self.engine; let chain = self.chain.read(); @@ -1847,7 +1989,7 @@ impl MiningBlockChainClient for Client { self.tracedb.read().tracing_enabled(), self.state_db.read().boxed_clone_canon(&h), best_header, - self.build_last_hashes(h.clone()), + self.build_last_hashes(&h), author, gas_range_target, extra_data, @@ -1870,71 +2012,36 @@ impl MiningBlockChainClient for Client { open_block } +} - fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { - let engine = &*self.engine; - let mut block = block.reopen(engine); - let max_uncles = engine.maximum_uncle_count(block.header().number()); - if block.uncles().len() < max_uncles { - let chain = self.chain.read(); - let h = chain.best_block_hash(); - // Add new uncles - let uncles = chain - .find_uncle_hashes(&h, engine.maximum_uncle_age()) - .unwrap_or_else(Vec::new); - - for h in uncles { - if !block.uncles().iter().any(|header| header.hash() == h) { - let uncle = chain.block_header(&h).expect("find_uncle_hashes only returns hashes for existing headers; qed"); - block.push_uncle(uncle).expect("pushing up to maximum_uncle_count; - push_uncle is not ok only if more than maximum_uncle_count is pushed; - so all push_uncle are Ok; - qed"); - if block.uncles().len() >= max_uncles { break } - } - } - - } - block - } - - fn vm_factory(&self) -> &VmFactory { - &self.factories.vm - } +impl BlockProducer for Client {} - fn broadcast_proposal_block(&self, block: SealedBlock) { - self.notify(|notify| { - notify.new_blocks( - vec![], - vec![], - vec![], - vec![], - vec![], - vec![block.rlp_bytes()], - 0, - ); - }); +impl ScheduleInfo for Client { + fn latest_schedule(&self) -> Schedule { + self.engine.schedule(self.latest_env_info().number) } +} +impl ImportSealedBlock for Client { fn import_sealed_block(&self, block: SealedBlock) -> ImportResult { let h = block.header().hash(); let start = precise_time_ns(); let route = { // scope for self.import_lock - let _import_lock = self.import_lock.lock(); + let _import_lock = self.importer.import_lock.lock(); trace_time!("import_sealed_block"); let number = block.header().number(); let block_data = block.rlp_bytes(); let header = block.header().clone(); - let route = self.commit_block(block, &header, &block_data); + let route = self.importer.commit_block(block, &header, &block_data, self); trace!(target: "client", "Imported sealed block #{} ({})", number, h); self.state_db.write().sync_cache(&route.enacted, &route.retracted, false); route }; - let (enacted, retracted) = self.calculate_enacted_retracted(&[route]); - self.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted); + let (enacted, retracted) = self.importer.calculate_enacted_retracted(&[route]); + self.importer.miner.chain_new_blocks(self, &[h.clone()], &[], &enacted, &retracted); self.notify(|notify| { notify.new_blocks( vec![h.clone()], @@ -1951,13 +2058,37 @@ impl MiningBlockChainClient for Client { } } +impl BroadcastProposalBlock for Client { + fn broadcast_proposal_block(&self, block: SealedBlock) { + self.notify(|notify| { + notify.new_blocks( + vec![], + vec![], + vec![], + vec![], + vec![], + vec![block.rlp_bytes()], + 0, + ); + }); + } +} + +impl SealedBlockImporter for Client {} + +impl MiningBlockChainClient for Client { + fn vm_factory(&self) -> &VmFactory { + &self.factories.vm + } +} + impl super::traits::EngineClient for Client { fn update_sealing(&self) { - self.miner.update_sealing(self) + self.importer.miner.update_sealing(self) } fn submit_seal(&self, block_hash: H256, seal: Vec) { - if self.miner.submit_seal(self, block_hash, seal).is_err() { + if self.importer.miner.submit_seal(self, block_hash, seal).is_err() { warn!(target: "poa", "Wrong internal seal submission!") } } @@ -1970,10 +2101,6 @@ impl super::traits::EngineClient for Client { self.chain.read().epoch_transition_for(parent_hash) } - fn chain_info(&self) -> BlockChainInfo { - BlockChainClient::chain_info(self) - } - fn as_full_client(&self) -> Option<&BlockChainClient> { Some(self) } fn block_number(&self, id: BlockId) -> Option { @@ -2077,7 +2204,7 @@ mod tests { #[test] fn should_not_cache_details_before_commit() { - use client::BlockChainClient; + use client::{BlockChainClient, ChainInfo}; use tests::helpers::*; use std::thread; diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 0a7ff45b25b..56eb5ae1d71 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -30,9 +30,12 @@ pub use self::error::Error; pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactResult}; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::chain_notify::ChainNotify; -pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient}; - -pub use self::traits::ProvingBlockChainClient; +pub use self::traits::{ + Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, CallContract, TransactionInfo, RegistryInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, + StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter +}; +pub use state::StateInfo; +pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient, ProvingBlockChainClient}; pub use types::ids::*; pub use types::trace_filter::Filter as TraceFilter; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index cd3ce468edc..b806183a2c9 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -34,9 +34,11 @@ use ethkey::{Generator, Random}; use transaction::{self, Transaction, LocalizedTransaction, PendingTransaction, SignedTransaction, Action}; use blockchain::{TreeRoute, BlockReceipts}; use client::{ - BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId, + Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, CallContract, TransactionInfo, RegistryInfo, + PrepareOpenBlock, BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId, TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError, - ProvingBlockChainClient, + ProvingBlockChainClient, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, + Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter }; use db::{NUM_COLUMNS, COL_STATE}; use header::{Header as BlockHeader, BlockNumber}; @@ -59,7 +61,11 @@ use executive::Executed; use error::CallError; use trace::LocalizedTrace; use state_db::StateDB; +use header::Header; use encoded; +use engines::EthEngine; +use trie; +use state::StateInfo; /// Test client. pub struct TestBlockChainClient { @@ -316,7 +322,7 @@ impl TestBlockChainClient { BlockId::Hash(hash) => Some(hash), BlockId::Number(n) => self.numbers.read().get(&(n as usize)).cloned(), BlockId::Earliest => self.numbers.read().get(&0).cloned(), - BlockId::Latest | BlockId::Pending => self.numbers.read().get(&(self.numbers.read().len() - 1)).cloned() + BlockId::Latest => self.numbers.read().get(&(self.numbers.read().len() - 1)).cloned() } } @@ -357,13 +363,13 @@ pub fn get_temp_state_db() -> StateDB { StateDB::new(journal_db, 1024 * 1024) } -impl MiningBlockChainClient for TestBlockChainClient { - fn as_block_chain_client(&self) -> &BlockChainClient { self } - - fn latest_schedule(&self) -> Schedule { - Schedule::new_post_eip150(24576, true, true, true) +impl ReopenBlock for TestBlockChainClient { + fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { + block.reopen(&*self.spec.engine) } +} +impl PrepareOpenBlock for TestBlockChainClient { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { let engine = &*self.spec.engine; let genesis_header = self.spec.genesis_header(); @@ -386,39 +392,221 @@ impl MiningBlockChainClient for TestBlockChainClient { open_block.set_timestamp(*self.latest_block_timestamp.read()); open_block } +} - fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { - block.reopen(&*self.spec.engine) +impl ScheduleInfo for TestBlockChainClient { + fn latest_schedule(&self) -> Schedule { + Schedule::new_post_eip150(24576, true, true, true) } +} + +impl ImportSealedBlock for TestBlockChainClient { + fn import_sealed_block(&self, _block: SealedBlock) -> ImportResult { + Ok(H256::default()) + } +} +impl BlockProducer for TestBlockChainClient {} + +impl BroadcastProposalBlock for TestBlockChainClient { + fn broadcast_proposal_block(&self, _block: SealedBlock) {} +} + +impl SealedBlockImporter for TestBlockChainClient {} + +impl MiningBlockChainClient for TestBlockChainClient { fn vm_factory(&self) -> &VmFactory { &self.vm_factory } +} - fn import_sealed_block(&self, _block: SealedBlock) -> ImportResult { - Ok(H256::default()) +impl Nonce for TestBlockChainClient { + fn nonce(&self, address: &Address, id: BlockId) -> Option { + match id { + BlockId::Latest => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params().account_start_nonce)), + _ => None, + } } - fn broadcast_proposal_block(&self, _block: SealedBlock) {} + fn latest_nonce(&self, address: &Address) -> U256 { + self.nonce(address, BlockId::Latest).unwrap() + } } -impl BlockChainClient for TestBlockChainClient { - fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics, _block: BlockId) -> Result { +impl Balance for TestBlockChainClient { + fn balance(&self, address: &Address, state: StateOrBlock) -> Option { + match state { + StateOrBlock::Block(BlockId::Latest) | StateOrBlock::State(_) => Some(self.balances.read().get(address).cloned().unwrap_or_else(U256::zero)), + _ => None, + } + } + + fn latest_balance(&self, address: &Address) -> U256 { + self.balance(address, BlockId::Latest.into()).unwrap() + } +} + +impl AccountData for TestBlockChainClient {} + +impl ChainInfo for TestBlockChainClient { + fn chain_info(&self) -> BlockChainInfo { + let number = self.blocks.read().len() as BlockNumber - 1; + BlockChainInfo { + total_difficulty: *self.difficulty.read(), + pending_total_difficulty: *self.difficulty.read(), + genesis_hash: self.genesis_hash.clone(), + best_block_hash: self.last_hash.read().clone(), + best_block_number: number, + best_block_timestamp: number, + first_block_hash: self.first_block.read().as_ref().map(|x| x.0), + first_block_number: self.first_block.read().as_ref().map(|x| x.1), + ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0), + ancient_block_number: self.ancient_block.read().as_ref().map(|x| x.1) + } + } +} + +impl BlockInfo for TestBlockChainClient { + fn block_header(&self, id: BlockId) -> Option { + self.block_hash(id) + .and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec())) + .map(encoded::Header::new) + } + + fn best_block_header(&self) -> encoded::Header { + self.block_header(BlockId::Hash(self.chain_info().best_block_hash)) + .expect("Best block always has header.") + } + + fn block(&self, id: BlockId) -> Option { + self.block_hash(id) + .and_then(|hash| self.blocks.read().get(&hash).cloned()) + .map(encoded::Block::new) + } + + fn code_hash(&self, address: &Address, id: BlockId) -> Option { + match id { + BlockId::Latest => self.code.read().get(address).map(|c| keccak(&c)), + _ => None, + } + } +} + +impl CallContract for TestBlockChainClient { + fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result { Ok(vec![]) } +} + +impl TransactionInfo for TestBlockChainClient { + fn transaction_block(&self, _id: TransactionId) -> Option { + None // Simple default. + } +} + +impl BlockChain for TestBlockChainClient {} + +impl RegistryInfo for TestBlockChainClient { + fn registry_address(&self, _name: String, _block: BlockId) -> Option
{ None } +} + +impl ImportBlock for TestBlockChainClient { + fn import_block(&self, b: Bytes) -> Result { + let header = Rlp::new(&b).val_at::(0); + let h = header.hash(); + let number: usize = header.number() as usize; + if number > self.blocks.read().len() { + panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().len(), number); + } + if number > 0 { + match self.blocks.read().get(header.parent_hash()) { + Some(parent) => { + let parent = Rlp::new(parent).val_at::(0); + if parent.number() != (header.number() - 1) { + panic!("Unexpected block parent"); + } + }, + None => { + panic!("Unknown block parent {:?} for block {}", header.parent_hash(), number); + } + } + } + let len = self.numbers.read().len(); + if number == len { + { + let mut difficulty = self.difficulty.write(); + *difficulty = *difficulty + header.difficulty().clone(); + } + mem::replace(&mut *self.last_hash.write(), h.clone()); + self.blocks.write().insert(h.clone(), b); + self.numbers.write().insert(number, h.clone()); + let mut parent_hash = header.parent_hash().clone(); + if number > 0 { + let mut n = number - 1; + while n > 0 && self.numbers.read()[&n] != parent_hash { + *self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone(); + n -= 1; + parent_hash = Rlp::new(&self.blocks.read()[&parent_hash]).val_at::(0).parent_hash().clone(); + } + } + } + else { + self.blocks.write().insert(h.clone(), b.to_vec()); + } + Ok(h) + } + + fn import_block_with_receipts(&self, b: Bytes, _r: Bytes) -> Result { + self.import_block(b) + } +} + +impl Call for TestBlockChainClient { + // State will not be used by test client anyway, since all methods that accept state are mocked + type State = (); + + fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics, _state: &mut Self::State, _header: &Header) -> Result { self.execution_result.read().clone().unwrap() } - fn call_many(&self, txs: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result, CallError> { + fn call_many(&self, txs: &[(SignedTransaction, CallAnalytics)], state: &mut Self::State, header: &Header) -> Result, CallError> { let mut res = Vec::with_capacity(txs.len()); for &(ref tx, analytics) in txs { - res.push(self.call(tx, analytics, block)?); + res.push(self.call(tx, analytics, state, header)?); } Ok(res) } - fn estimate_gas(&self, _t: &SignedTransaction, _block: BlockId) -> Result { + fn estimate_gas(&self, _t: &SignedTransaction, _state: &Self::State, _header: &Header) -> Result { Ok(21000.into()) } +} + +impl StateInfo for () { + fn nonce(&self, _address: &Address) -> trie::Result { unimplemented!() } + fn balance(&self, _address: &Address) -> trie::Result { unimplemented!() } + fn storage_at(&self, _address: &Address, _key: &H256) -> trie::Result { unimplemented!() } + fn code(&self, _address: &Address) -> trie::Result>> { unimplemented!() } +} + +impl StateClient for TestBlockChainClient { + // State will not be used by test client anyway, since all methods that accept state are mocked + type State = (); + + fn latest_state(&self) -> Self::State { + () + } + + fn state_at(&self, _id: BlockId) -> Option { + Some(()) + } +} + +impl EngineInfo for TestBlockChainClient { + fn engine(&self) -> &EthEngine { + unimplemented!() + } +} +impl BlockChainClient for TestBlockChainClient { fn replay(&self, _id: TransactionId, _analytics: CallAnalytics) -> Result { self.execution_result.read().clone().unwrap() } @@ -435,49 +623,20 @@ impl BlockChainClient for TestBlockChainClient { Self::block_hash(self, id) } - fn nonce(&self, address: &Address, id: BlockId) -> Option { - match id { - BlockId::Latest | BlockId::Pending => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params().account_start_nonce)), - _ => None, - } - } - fn storage_root(&self, _address: &Address, _id: BlockId) -> Option { None } - fn latest_nonce(&self, address: &Address) -> U256 { - self.nonce(address, BlockId::Latest).unwrap() - } - - fn code(&self, address: &Address, id: BlockId) -> Option> { - match id { - BlockId::Latest | BlockId::Pending => Some(self.code.read().get(address).cloned()), + fn code(&self, address: &Address, state: StateOrBlock) -> Option> { + match state { + StateOrBlock::Block(BlockId::Latest) => Some(self.code.read().get(address).cloned()), _ => None, } } - fn code_hash(&self, address: &Address, id: BlockId) -> Option { - match id { - BlockId::Latest | BlockId::Pending => self.code.read().get(address).map(|c| keccak(&c)), - _ => None, - } - } - - fn balance(&self, address: &Address, id: BlockId) -> Option { - match id { - BlockId::Latest | BlockId::Pending => Some(self.balances.read().get(address).cloned().unwrap_or_else(U256::zero)), - _ => None, - } - } - - fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, BlockId::Latest).unwrap() - } - - fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option { - match id { - BlockId::Latest | BlockId::Pending => Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new)), + fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option { + match state { + StateOrBlock::Block(BlockId::Latest) => Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new)), _ => None, } } @@ -493,10 +652,6 @@ impl BlockChainClient for TestBlockChainClient { None // Simple default. } - fn transaction_block(&self, _id: TransactionId) -> Option { - None // Simple default. - } - fn uncle(&self, _id: UncleId) -> Option { None // Simple default. } @@ -522,17 +677,6 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } - fn best_block_header(&self) -> encoded::Header { - self.block_header(BlockId::Hash(self.chain_info().best_block_hash)) - .expect("Best block always has header.") - } - - fn block_header(&self, id: BlockId) -> Option { - self.block_hash(id) - .and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec())) - .map(encoded::Header::new) - } - fn block_number(&self, _id: BlockId) -> Option { unimplemented!() } @@ -546,12 +690,6 @@ impl BlockChainClient for TestBlockChainClient { })) } - fn block(&self, id: BlockId) -> Option { - self.block_hash(id) - .and_then(|hash| self.blocks.read().get(&hash).cloned()) - .map(encoded::Block::new) - } - fn block_extra_info(&self, id: BlockId) -> Option> { self.block(id) .map(|block| block.view().header()) @@ -564,7 +702,6 @@ impl BlockChainClient for TestBlockChainClient { BlockId::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain, BlockId::Hash(ref hash) if self.blocks.read().get(hash).is_some() => BlockStatus::InChain, BlockId::Latest | BlockId::Earliest => BlockStatus::InChain, - BlockId::Pending => BlockStatus::Pending, _ => BlockStatus::Unknown, } } @@ -628,55 +765,6 @@ impl BlockChainClient for TestBlockChainClient { None } - fn import_block(&self, b: Bytes) -> Result { - let header = Rlp::new(&b).val_at::(0); - let h = header.hash(); - let number: usize = header.number() as usize; - if number > self.blocks.read().len() { - panic!("Unexpected block number. Expected {}, got {}", self.blocks.read().len(), number); - } - if number > 0 { - match self.blocks.read().get(header.parent_hash()) { - Some(parent) => { - let parent = Rlp::new(parent).val_at::(0); - if parent.number() != (header.number() - 1) { - panic!("Unexpected block parent"); - } - }, - None => { - panic!("Unknown block parent {:?} for block {}", header.parent_hash(), number); - } - } - } - let len = self.numbers.read().len(); - if number == len { - { - let mut difficulty = self.difficulty.write(); - *difficulty = *difficulty + header.difficulty().clone(); - } - mem::replace(&mut *self.last_hash.write(), h.clone()); - self.blocks.write().insert(h.clone(), b); - self.numbers.write().insert(number, h.clone()); - let mut parent_hash = header.parent_hash().clone(); - if number > 0 { - let mut n = number - 1; - while n > 0 && self.numbers.read()[&n] != parent_hash { - *self.numbers.write().get_mut(&n).unwrap() = parent_hash.clone(); - n -= 1; - parent_hash = Rlp::new(&self.blocks.read()[&parent_hash]).val_at::(0).parent_hash().clone(); - } - } - } - else { - self.blocks.write().insert(h.clone(), b.to_vec()); - } - Ok(h) - } - - fn import_block_with_receipts(&self, b: Bytes, _r: Bytes) -> Result { - self.import_block(b) - } - fn queue_info(&self) -> QueueInfo { QueueInfo { verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed), @@ -695,22 +783,6 @@ impl BlockChainClient for TestBlockChainClient { Default::default() } - fn chain_info(&self) -> BlockChainInfo { - let number = self.blocks.read().len() as BlockNumber - 1; - BlockChainInfo { - total_difficulty: *self.difficulty.read(), - pending_total_difficulty: *self.difficulty.read(), - genesis_hash: self.genesis_hash.clone(), - best_block_hash: self.last_hash.read().clone(), - best_block_number: number, - best_block_timestamp: number, - first_block_hash: self.first_block.read().as_ref().map(|x| x.0), - first_block_number: self.first_block.read().as_ref().map(|x| x.1), - ancient_block_hash: self.ancient_block.read().as_ref().map(|x| x.0), - ancient_block_number: self.ancient_block.read().as_ref().map(|x| x.1) - } - } - fn filter_traces(&self, _filter: TraceFilter) -> Option> { self.traces.read().clone() } @@ -762,8 +834,6 @@ impl BlockChainClient for TestBlockChainClient { } } - fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result { Ok(vec![]) } - fn transact_contract(&self, address: Address, data: Bytes) -> Result { let transaction = Transaction { nonce: self.latest_nonce(&self.miner.author()), @@ -781,8 +851,6 @@ impl BlockChainClient for TestBlockChainClient { fn registrar_address(&self) -> Option
{ None } - fn registry_address(&self, _name: String, _block: BlockId) -> Option
{ None } - fn eip86_transition(&self) -> u64 { u64::max_value() } } @@ -821,10 +889,6 @@ impl super::traits::EngineClient for TestBlockChainClient { None } - fn chain_info(&self) -> BlockChainInfo { - BlockChainClient::chain_info(self) - } - fn as_full_client(&self) -> Option<&BlockChainClient> { Some(self) } fn block_number(&self, id: BlockId) -> Option { diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 1c623fd059f..1228f87683c 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -32,6 +32,9 @@ use receipt::LocalizedReceipt; use trace::LocalizedTrace; use transaction::{LocalizedTransaction, PendingTransaction, SignedTransaction, ImportResult as TransactionImportResult}; use verification::queue::QueueInfo as BlockQueueInfo; +use state::StateInfo; +use header::Header; +use engines::EthEngine; use ethereum_types::{H256, U256, Address}; use bytes::Bytes; @@ -46,12 +49,158 @@ use types::block_status::BlockStatus; use types::mode::Mode; use types::pruning_info::PruningInfo; -/// Blockchain database client. Owns and manages a blockchain and a block queue. -pub trait BlockChainClient : Sync + Send { +/// State information to be used during client query +pub enum StateOrBlock { + /// State to be used, may be pending + State(Box), + + /// Id of an existing block from a chain to get state from + Block(BlockId) +} + +impl From for StateOrBlock { + fn from(info: S) -> StateOrBlock { + StateOrBlock::State(Box::new(info) as Box<_>) + } +} + +impl From> for StateOrBlock { + fn from(info: Box) -> StateOrBlock { + StateOrBlock::State(info) + } +} + +impl From for StateOrBlock { + fn from(id: BlockId) -> StateOrBlock { + StateOrBlock::Block(id) + } +} + +/// Provides `nonce` and `latest_nonce` methods +pub trait Nonce { + /// Attempt to get address nonce at given block. + /// May not fail on BlockId::Latest. + fn nonce(&self, address: &Address, id: BlockId) -> Option; + /// Get address nonce at the latest block's state. + fn latest_nonce(&self, address: &Address) -> U256 { + self.nonce(address, BlockId::Latest) + .expect("nonce will return Some when given BlockId::Latest. nonce was given BlockId::Latest. \ + Therefore nonce has returned Some; qed") + } +} + +/// Provides `balance` and `latest_balance` methods +pub trait Balance { + /// Get address balance at the given block's state. + /// + /// May not return None if given BlockId::Latest. + /// Returns None if and only if the block's root hash has been pruned from the DB. + fn balance(&self, address: &Address, state: StateOrBlock) -> Option; + + /// Get address balance at the latest block's state. + fn latest_balance(&self, address: &Address) -> U256 { + self.balance(address, BlockId::Latest.into()) + .expect("balance will return Some if given BlockId::Latest. balance was given BlockId::Latest \ + Therefore balance has returned Some; qed") + } +} + +/// Provides methods to access account info +pub trait AccountData: Nonce + Balance {} + +/// Provides `chain_info` method +pub trait ChainInfo { + /// Get blockchain information. + fn chain_info(&self) -> BlockChainInfo; +} + +/// Provides various information on a block by it's ID +pub trait BlockInfo { /// Get raw block header data by block id. fn block_header(&self, id: BlockId) -> Option; + /// Get the best block header. + fn best_block_header(&self) -> encoded::Header; + + /// Get raw block data by block header hash. + fn block(&self, id: BlockId) -> Option; + + /// Get address code hash at given block's state. + fn code_hash(&self, address: &Address, id: BlockId) -> Option; +} + +/// Provides various information on a transaction by it's ID +pub trait TransactionInfo { + /// Get the hash of block that contains the transaction, if any. + fn transaction_block(&self, id: TransactionId) -> Option; +} + +/// Provides methods to access chain state +pub trait StateClient { + /// Type representing chain state + type State: StateInfo; + + /// Get a copy of the best block's state. + fn latest_state(&self) -> Self::State; + + /// Attempt to get a copy of a specific block's final state. + /// + /// This will not fail if given BlockId::Latest. + /// Otherwise, this can fail (but may not) if the DB prunes state or the block + /// is unknown. + fn state_at(&self, id: BlockId) -> Option; +} + +/// Provides various blockchain information, like block header, chain state etc. +pub trait BlockChain: ChainInfo + BlockInfo + TransactionInfo {} + +/// Provides information on a blockchain service and it's registry +pub trait RegistryInfo { + /// Get the address of a particular blockchain service, if available. + fn registry_address(&self, name: String, block: BlockId) -> Option
; +} + +// FIXME Why these methods belong to BlockChainClient and not MiningBlockChainClient? +/// Provides methods to import block into blockchain +pub trait ImportBlock { + /// Import a block into the blockchain. + fn import_block(&self, bytes: Bytes) -> Result; + + /// Import a block with transaction receipts. Does no sealing and transaction validation. + fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result; +} + +/// Provides `call_contract` method +pub trait CallContract { + /// Like `call`, but with various defaults. Designed to be used for calling contracts. + fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result; +} + +/// Provides `call` and `call_many` methods +pub trait Call { + /// Type representing chain state + type State: StateInfo; + + /// Makes a non-persistent transaction call. + fn call(&self, tx: &SignedTransaction, analytics: CallAnalytics, state: &mut Self::State, header: &Header) -> Result; + + /// Makes multiple non-persistent but dependent transaction calls. + /// Returns a vector of successes or a failure if any of the transaction fails. + fn call_many(&self, txs: &[(SignedTransaction, CallAnalytics)], state: &mut Self::State, header: &Header) -> Result, CallError>; + + /// Estimates how much gas will be necessary for a call. + fn estimate_gas(&self, t: &SignedTransaction, state: &Self::State, header: &Header) -> Result; +} + +/// Provides `engine` method +pub trait EngineInfo { + /// Get underlying engine object + fn engine(&self) -> &EthEngine; +} + +/// Blockchain database client. Owns and manages a blockchain and a block queue. +pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContract + RegistryInfo + ImportBlock { /// Look up the block number for the given block ID. fn block_number(&self, id: BlockId) -> Option; @@ -59,67 +208,39 @@ pub trait BlockChainClient : Sync + Send { /// Block body is an RLP list of two items: uncles and transactions. fn block_body(&self, id: BlockId) -> Option; - /// Get raw block data by block header hash. - fn block(&self, id: BlockId) -> Option; - /// Get block status by block header hash. fn block_status(&self, id: BlockId) -> BlockStatus; /// Get block total difficulty. fn block_total_difficulty(&self, id: BlockId) -> Option; - /// Attempt to get address nonce at given block. - /// May not fail on BlockId::Latest. - fn nonce(&self, address: &Address, id: BlockId) -> Option; - /// Attempt to get address storage root at given block. /// May not fail on BlockId::Latest. fn storage_root(&self, address: &Address, id: BlockId) -> Option; - /// Get address nonce at the latest block's state. - fn latest_nonce(&self, address: &Address) -> U256 { - self.nonce(address, BlockId::Latest) - .expect("nonce will return Some when given BlockId::Latest. nonce was given BlockId::Latest. \ - Therefore nonce has returned Some; qed") - } - /// Get block hash. fn block_hash(&self, id: BlockId) -> Option; /// Get address code at given block's state. - fn code(&self, address: &Address, id: BlockId) -> Option>; + fn code(&self, address: &Address, state: StateOrBlock) -> Option>; /// Get address code at the latest block's state. fn latest_code(&self, address: &Address) -> Option { - self.code(address, BlockId::Latest) + self.code(address, BlockId::Latest.into()) .expect("code will return Some if given BlockId::Latest; qed") } /// Get address code hash at given block's state. - fn code_hash(&self, address: &Address, id: BlockId) -> Option; - - /// Get address balance at the given block's state. - /// - /// May not return None if given BlockId::Latest. - /// Returns None if and only if the block's root hash has been pruned from the DB. - fn balance(&self, address: &Address, id: BlockId) -> Option; - - /// Get address balance at the latest block's state. - fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, BlockId::Latest) - .expect("balance will return Some if given BlockId::Latest. balance was given BlockId::Latest \ - Therefore balance has returned Some; qed") - } /// Get value of the storage at given position at the given block's state. /// /// May not return None if given BlockId::Latest. /// Returns None if and only if the block's root hash has been pruned from the DB. - fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option; + fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option; /// Get value of the storage at given position at the latest block's state. fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 { - self.storage_at(address, position, BlockId::Latest) + self.storage_at(address, position, BlockId::Latest.into()) .expect("storage_at will return Some if given BlockId::Latest. storage_at was given BlockId::Latest. \ Therefore storage_at has returned Some; qed") } @@ -135,9 +256,6 @@ pub trait BlockChainClient : Sync + Send { /// Get transaction with given hash. fn transaction(&self, id: TransactionId) -> Option; - /// Get the hash of block that contains the transaction, if any. - fn transaction_block(&self, id: TransactionId) -> Option; - /// Get uncle with given id. fn uncle(&self, id: UncleId) -> Option; @@ -157,40 +275,18 @@ pub trait BlockChainClient : Sync + Send { /// Get raw block receipts data by block header hash. fn block_receipts(&self, hash: &H256) -> Option; - /// Import a block into the blockchain. - fn import_block(&self, bytes: Bytes) -> Result; - - /// Import a block with transaction receipts. Does no sealing and transaction validation. - fn import_block_with_receipts(&self, block_bytes: Bytes, receipts_bytes: Bytes) -> Result; - /// Get block queue information. fn queue_info(&self) -> BlockQueueInfo; /// Clear block queue and abort all import activity. fn clear_queue(&self); - /// Get blockchain information. - fn chain_info(&self) -> BlockChainInfo; - /// Get the registrar address, if it exists. fn additional_params(&self) -> BTreeMap; - /// Get the best block header. - fn best_block_header(&self) -> encoded::Header; - /// Returns logs matching given filter. fn logs(&self, filter: Filter) -> Vec; - /// Makes a non-persistent transaction call. - fn call(&self, tx: &SignedTransaction, analytics: CallAnalytics, block: BlockId) -> Result; - - /// Makes multiple non-persistent but dependent transaction calls. - /// Returns a vector of successes or a failure if any of the transaction fails. - fn call_many(&self, txs: &[(SignedTransaction, CallAnalytics)], block: BlockId) -> Result, CallError>; - - /// Estimates how much gas will be necessary for a call. - fn estimate_gas(&self, t: &SignedTransaction, block: BlockId) -> Result; - /// Replays a given transaction for inspection. fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result; @@ -270,52 +366,64 @@ pub trait BlockChainClient : Sync + Send { /// Returns information about pruning/data availability. fn pruning_info(&self) -> PruningInfo; - /// Like `call`, but with various defaults. Designed to be used for calling contracts. - fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result; - /// Import a transaction: used for misbehaviour reporting. fn transact_contract(&self, address: Address, data: Bytes) -> Result; /// Get the address of the registry itself. fn registrar_address(&self) -> Option
; - /// Get the address of a particular blockchain service, if available. - fn registry_address(&self, name: String, block: BlockId) -> Option
; - /// Get the EIP-86 transition block number. fn eip86_transition(&self) -> u64; } -/// Extended client interface used for mining -pub trait MiningBlockChainClient: BlockChainClient { +/// Provides `reopen_block` method +pub trait ReopenBlock { + /// Reopens an OpenBlock and updates uncles. + fn reopen_block(&self, block: ClosedBlock) -> OpenBlock; +} + +/// Provides `prepare_open_block` method +pub trait PrepareOpenBlock { /// Returns OpenBlock prepared for closing. fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes ) -> OpenBlock; +} - /// Reopens an OpenBlock and updates uncles. - fn reopen_block(&self, block: ClosedBlock) -> OpenBlock; - - /// Returns EvmFactory. - fn vm_factory(&self) -> &VmFactory; +/// Provides methods used for sealing new state +pub trait BlockProducer: PrepareOpenBlock + ReopenBlock {} - /// Broadcast a block proposal. - fn broadcast_proposal_block(&self, block: SealedBlock); +/// Provides `latest_schedule` method +pub trait ScheduleInfo { + /// Returns latest schedule. + fn latest_schedule(&self) -> Schedule; +} +///Provides `import_sealed_block` method +pub trait ImportSealedBlock { /// Import sealed block. Skips all verifications. fn import_sealed_block(&self, block: SealedBlock) -> ImportResult; +} - /// Returns latest schedule. - fn latest_schedule(&self) -> Schedule; +/// Provides `broadcast_proposal_block` method +pub trait BroadcastProposalBlock { + /// Broadcast a block proposal. + fn broadcast_proposal_block(&self, block: SealedBlock); +} + +/// Provides methods to import sealed block and broadcast a block proposal +pub trait SealedBlockImporter: ImportSealedBlock + BroadcastProposalBlock {} - /// Returns base of this trait - fn as_block_chain_client(&self) -> &BlockChainClient; +/// Extended client interface used for mining +pub trait MiningBlockChainClient: BlockChainClient + BlockProducer + ScheduleInfo + SealedBlockImporter { + /// Returns EvmFactory. + fn vm_factory(&self) -> &VmFactory; } /// Client facilities used by internally sealing Engines. -pub trait EngineClient: Sync + Send { +pub trait EngineClient: Sync + Send + ChainInfo { /// Make a new block and seal it. fn update_sealing(&self); @@ -332,9 +440,6 @@ pub trait EngineClient: Sync + Send { /// The block corresponding the the parent hash must be stored already. fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition>; - /// Get block chain info. - fn chain_info(&self) -> BlockChainInfo; - /// Attempt to cast the engine client to a full client. fn as_full_client(&self) -> Option<&BlockChainClient>; diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index dfac00bea32..0d8081c44ac 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -781,6 +781,7 @@ mod tests { use block::*; use error::{Error, BlockError}; use header::Header; + use client::ChainInfo; use miner::MinerService; use tests::helpers::*; use account_provider::AccountProvider; diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 20c43345c9c..0e27d594d54 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -145,7 +145,7 @@ mod tests { use account_provider::AccountProvider; use miner::MinerService; use types::ids::BlockId; - use client::BlockChainClient; + use client::{BlockChainClient, ChainInfo, BlockInfo, CallContract}; use tests::helpers::generate_dummy_client_with_spec_and_accounts; use super::super::ValidatorSet; use super::ValidatorContract; diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index b607544cbb7..2794b57a2a4 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -148,7 +148,7 @@ mod tests { use std::collections::BTreeMap; use hash::keccak; use account_provider::AccountProvider; - use client::BlockChainClient; + use client::{BlockChainClient, ChainInfo, BlockInfo, ImportBlock}; use engines::EpochChange; use engines::validator_set::ValidatorSet; use ethkey::Secret; diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 7093ab8964f..490a762a610 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -456,7 +456,7 @@ mod tests { use spec::Spec; use account_provider::AccountProvider; use transaction::{Transaction, Action}; - use client::BlockChainClient; + use client::{ChainInfo, BlockInfo, ImportBlock}; use ethkey::Secret; use miner::MinerService; use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data}; diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index e82bc774036..64414450bd5 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::sync::Arc; -use client::{EvmTestClient, BlockChainClient, Client, ClientConfig}; +use client::{EvmTestClient, Client, ClientConfig, ChainInfo, ImportBlock}; use block::Block; use spec::Genesis; use ethjson; diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index bd38130dcfb..b4f7651f86b 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -22,7 +22,7 @@ use std::sync::Arc; use block::{ExecutedBlock, IsBlock}; use builtin::Builtin; -use client::BlockChainClient; +use client::{BlockInfo, CallContract}; use error::Error; use executive::Executive; use header::{BlockNumber, Header}; @@ -366,7 +366,7 @@ impl EthereumMachine { /// Does verification of the transaction against the parent state. // TODO: refine the bound here to be a "state provider" or similar as opposed // to full client functionality. - pub fn verify_transaction(&self, t: &SignedTransaction, header: &Header, client: &BlockChainClient) -> Result<(), Error> { + pub fn verify_transaction(&self, t: &SignedTransaction, header: &Header, client: &C) -> Result<(), Error> { if let Some(ref filter) = self.tx_filter.as_ref() { if !filter.transaction_allowed(header.parent_hash(), t, client) { return Err(transaction::Error::NotAllowed.into()) diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 56a8104c433..f0152686a8a 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -36,7 +36,7 @@ use ethcore_miner::transaction_queue::{ TransactionOrigin, }; use ethcore_miner::work_notify::{WorkPoster, NotifyWork}; -use ethcore_miner::service_transaction_checker::ServiceTransactionChecker; +use miner::service_transaction_checker::ServiceTransactionChecker; use miner::{MinerService, MinerStatus}; use price_info::fetch::Client as FetchClient; use price_info::{Client as PriceInfoClient, PriceInfo}; @@ -50,9 +50,11 @@ use transaction::{ Error as TransactionError, }; use using_queue::{UsingQueue, GetAction}; - use block::{ClosedBlock, IsBlock, Block}; -use client::{MiningBlockChainClient, BlockId, TransactionId}; +use client::{ + AccountData, BlockChain, RegistryInfo, ScheduleInfo, CallContract, BlockProducer, SealedBlockImporter +}; +use client::{BlockId, TransactionId, MiningBlockChainClient}; use executive::contract_address; use header::{Header, BlockNumber}; use receipt::{Receipt, RichReceipt}; @@ -386,7 +388,7 @@ impl Miner { } /// Prepares new block for sealing including top transactions from queue. - fn prepare_block(&self, chain: &MiningBlockChainClient) -> (ClosedBlock, Option) { + fn prepare_block(&self, chain: &C) -> (ClosedBlock, Option) { trace_time!("prepare_block"); let chain_info = chain.chain_info(); let (transactions, mut open_block, original_work_hash) = { @@ -439,7 +441,7 @@ impl Miner { let hash = tx.hash(); let start = Instant::now(); // Check whether transaction type is allowed for sender - let result = match self.engine.machine().verify_transaction(&tx, open_block.header(), chain.as_block_chain_client()) { + let result = match self.engine.machine().verify_transaction(&tx, open_block.header(), chain) { Err(Error::Transaction(TransactionError::NotAllowed)) => { Err(TransactionError::NotAllowed.into()) } @@ -567,7 +569,9 @@ impl Miner { } /// Attempts to perform internal sealing (one that does not require work) and handles the result depending on the type of Seal. - fn seal_and_import_block_internally(&self, chain: &MiningBlockChainClient, block: ClosedBlock) -> bool { + fn seal_and_import_block_internally(&self, chain: &C, block: ClosedBlock) -> bool + where C: BlockChain + SealedBlockImporter + { if !block.transactions().is_empty() || self.forced_sealing() || Instant::now() > *self.next_mandatory_reseal.read() { trace!(target: "miner", "seal_block_internally: attempting internal seal."); @@ -647,7 +651,7 @@ impl Miner { } } - fn update_gas_limit(&self, client: &MiningBlockChainClient) { + fn update_gas_limit(&self, client: &C) { let gas_limit = client.best_block_header().gas_limit(); let mut queue = self.transaction_queue.write(); queue.set_gas_limit(gas_limit); @@ -658,7 +662,7 @@ impl Miner { } /// Returns true if we had to prepare new pending block. - fn prepare_work_sealing(&self, client: &MiningBlockChainClient) -> bool { + fn prepare_work_sealing(&self, client: &C) -> bool { trace!(target: "miner", "prepare_work_sealing: entering"); let prepare_new = { let mut sealing_work = self.sealing_work.lock(); @@ -690,9 +694,9 @@ impl Miner { prepare_new } - fn add_transactions_to_queue( + fn add_transactions_to_queue( &self, - client: &MiningBlockChainClient, + client: &C, transactions: Vec, default_origin: TransactionOrigin, condition: Option, @@ -718,7 +722,7 @@ impl Miner { }, Ok(transaction) => { // This check goes here because verify_transaction takes SignedTransaction parameter - self.engine.machine().verify_transaction(&transaction, &best_block_header, client.as_block_chain_client())?; + self.engine.machine().verify_transaction(&transaction, &best_block_header, client)?; let origin = self.accounts.as_ref().and_then(|accounts| { match accounts.has_account(transaction.sender()).unwrap_or(false) { @@ -774,8 +778,9 @@ impl Miner { const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5; impl MinerService for Miner { + type State = State<::state_db::StateDB>; - fn clear_and_reset(&self, chain: &MiningBlockChainClient) { + fn clear_and_reset(&self, chain: &C) { self.transaction_queue.write().clear(); // -------------------------------------------------------------------------- // | NOTE Code below requires transaction_queue and sealing_work locks. | @@ -891,16 +896,16 @@ impl MinerService for Miner { self.gas_range_target.read().1 } - fn import_external_transactions( + fn import_external_transactions( &self, - chain: &MiningBlockChainClient, + client: &C, transactions: Vec ) -> Vec> { trace!(target: "external_tx", "Importing external transactions"); let results = { let mut transaction_queue = self.transaction_queue.write(); self.add_transactions_to_queue( - chain, transactions, TransactionOrigin::External, None, &mut transaction_queue + client, transactions, TransactionOrigin::External, None, &mut transaction_queue ) }; @@ -909,14 +914,14 @@ impl MinerService for Miner { // | NOTE Code below requires transaction_queue and sealing_work locks. | // | Make sure to release the locks before calling that method. | // -------------------------------------------------------------------------- - self.update_sealing(chain); + self.update_sealing(client); } results } - fn import_own_transaction( + fn import_own_transaction( &self, - chain: &MiningBlockChainClient, + chain: &C, pending: PendingTransaction, ) -> Result { @@ -1040,7 +1045,7 @@ impl MinerService for Miner { } } - fn remove_pending_transaction(&self, chain: &MiningBlockChainClient, hash: &H256) -> Option { + fn remove_pending_transaction(&self, chain: &C, hash: &H256) -> Option { let mut queue = self.transaction_queue.write(); let tx = queue.find(hash); if tx.is_some() { @@ -1110,7 +1115,10 @@ impl MinerService for Miner { /// Update sealing if required. /// Prepare the block and work if the Engine does not seal internally. - fn update_sealing(&self, chain: &MiningBlockChainClient) { + fn update_sealing(&self, chain: &C) + where C: AccountData + BlockChain + RegistryInfo + + CallContract + BlockProducer + SealedBlockImporter + { trace!(target: "miner", "update_sealing"); const NO_NEW_CHAIN_WITH_FORKS: &str = "Your chain specification contains one or more hard forks which are required to be \ on by default. Please remove these forks and start your chain again."; @@ -1150,9 +1158,12 @@ impl MinerService for Miner { self.sealing_work.lock().queue.is_in_use() } - fn map_sealing_work(&self, chain: &MiningBlockChainClient, f: F) -> Option where F: FnOnce(&ClosedBlock) -> T { + fn map_sealing_work(&self, client: &C, f: F) -> Option + where C: AccountData + BlockChain + BlockProducer + CallContract, + F: FnOnce(&ClosedBlock) -> T + { trace!(target: "miner", "map_sealing_work: entering"); - self.prepare_work_sealing(chain); + self.prepare_work_sealing(client); trace!(target: "miner", "map_sealing_work: sealing prepared"); let mut sealing_work = self.sealing_work.lock(); let ret = sealing_work.queue.use_last_ref(); @@ -1160,7 +1171,7 @@ impl MinerService for Miner { ret.map(f) } - fn submit_seal(&self, chain: &MiningBlockChainClient, block_hash: H256, seal: Vec) -> Result<(), Error> { + fn submit_seal(&self, chain: &C, block_hash: H256, seal: Vec) -> Result<(), Error> { let result = if let Some(b) = self.sealing_work.lock().queue.get_used_if( if self.options.enable_resubmission { @@ -1188,7 +1199,10 @@ impl MinerService for Miner { }) } - fn chain_new_blocks(&self, chain: &MiningBlockChainClient, imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) { + fn chain_new_blocks(&self, chain: &C, imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) + where C: AccountData + BlockChain + CallContract + RegistryInfo + + BlockProducer + ScheduleInfo + SealedBlockImporter + { trace!(target: "miner", "chain_new_blocks"); // 1. We ignore blocks that were `imported` unless resealing on new uncles is enabled. @@ -1234,6 +1248,18 @@ impl MinerService for Miner { self.update_sealing(chain); } } + + fn pending_state(&self, latest_block_number: BlockNumber) -> Option { + Miner::pending_state(self, latest_block_number) + } + + fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
{ + Miner::pending_block_header(self, latest_block_number) + } + + fn pending_block(&self, latest_block_number: BlockNumber) -> Option { + Miner::pending_block(self, latest_block_number) + } } /// Action when service transaction is received @@ -1245,31 +1271,22 @@ enum ServiceTransactionAction { } impl ServiceTransactionAction { - pub fn check(&self, client: &MiningBlockChainClient, tx: &SignedTransaction) -> Result { + pub fn check(&self, client: &C, tx: &SignedTransaction) -> Result + { match *self { ServiceTransactionAction::Refuse => Err("configured to refuse service transactions".to_owned()), - ServiceTransactionAction::Check(ref checker) => checker.check(&client, tx), + ServiceTransactionAction::Check(ref checker) => checker.check(client, tx), } } } -impl<'a> ::ethcore_miner::service_transaction_checker::ContractCaller for &'a MiningBlockChainClient { - fn registry_address(&self, name: &str) -> Option
{ - MiningBlockChainClient::registry_address(*self, name.into(), BlockId::Latest) - } - - fn call_contract(&self, block: BlockId, address: Address, data: Vec) -> Result, String> { - MiningBlockChainClient::call_contract(*self, block, address, data) - } -} - -struct TransactionDetailsProvider<'a> { - client: &'a MiningBlockChainClient, +struct TransactionDetailsProvider<'a, C: 'a> { + client: &'a C, service_transaction_action: &'a ServiceTransactionAction, } -impl<'a> TransactionDetailsProvider<'a> { - pub fn new(client: &'a MiningBlockChainClient, service_transaction_action: &'a ServiceTransactionAction) -> Self { +impl<'a, C> TransactionDetailsProvider<'a, C> { + pub fn new(client: &'a C, service_transaction_action: &'a ServiceTransactionAction) -> Self { TransactionDetailsProvider { client: client, service_transaction_action: service_transaction_action, @@ -1277,7 +1294,9 @@ impl<'a> TransactionDetailsProvider<'a> { } } -impl<'a> TransactionQueueDetailsProvider for TransactionDetailsProvider<'a> { +impl<'a, C> TransactionQueueDetailsProvider for TransactionDetailsProvider<'a, C> + where C: AccountData + CallContract + RegistryInfo + ScheduleInfo +{ fn fetch_account(&self, address: &Address) -> AccountDetails { AccountDetails { nonce: self.client.latest_nonce(address), @@ -1300,12 +1319,14 @@ mod tests { use ethcore_miner::transaction_queue::PrioritizationStrategy; use ethereum_types::U256; use ethkey::{Generator, Random}; + use client::{TestBlockChainClient, EachBlockWith, ChainInfo}; use hash::keccak; + use header::BlockNumber; use rustc_hex::FromHex; - use transaction::Transaction; - - use client::{BlockChainClient, TestBlockChainClient, EachBlockWith}; + use spec::Spec; + use transaction::{SignedTransaction, Transaction, PendingTransaction, Action}; use miner::MinerService; + use tests::helpers::{generate_dummy_client, generate_dummy_client_with_spec_and_accounts}; #[test] diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 34c416cc146..5f451fdc2a6 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -40,6 +40,7 @@ mod miner; mod stratum; +mod service_transaction_checker; pub use self::miner::{Miner, MinerOptions, Banning, PendingSet, GasPricer, GasPriceCalibratorOptions, GasLimit}; pub use self::stratum::{Stratum, Error as StratumError, Options as StratumOptions}; @@ -47,18 +48,24 @@ pub use self::stratum::{Stratum, Error as StratumError, Options as StratumOption pub use ethcore_miner::local_transactions::Status as LocalTransactionStatus; use std::collections::BTreeMap; -use ethereum_types::{H256, U256, Address}; -use bytes::Bytes; -use block::ClosedBlock; -use client::{MiningBlockChainClient}; +use block::{ClosedBlock, Block}; +use bytes::Bytes; +use client::{ + MiningBlockChainClient, CallContract, RegistryInfo, ScheduleInfo, + BlockChain, AccountData, BlockProducer, SealedBlockImporter +}; use error::{Error}; -use header::BlockNumber; +use ethereum_types::{H256, U256, Address}; +use header::{BlockNumber, Header}; use receipt::{RichReceipt, Receipt}; use transaction::{UnverifiedTransaction, PendingTransaction, ImportResult as TransactionImportResult}; +use state::StateInfo; /// Miner client API pub trait MinerService : Send + Sync { + /// Type representing chain state + type State: StateInfo + 'static; /// Returns miner's status. fn status(&self) -> MinerStatus; @@ -107,42 +114,46 @@ pub trait MinerService : Send + Sync { fn set_tx_gas_limit(&self, limit: U256); /// Imports transactions to transaction queue. - fn import_external_transactions(&self, chain: &MiningBlockChainClient, transactions: Vec) -> + fn import_external_transactions(&self, client: &C, transactions: Vec) -> Vec>; /// Imports own (node owner) transaction to queue. - fn import_own_transaction(&self, chain: &MiningBlockChainClient, transaction: PendingTransaction) -> + fn import_own_transaction(&self, chain: &C, transaction: PendingTransaction) -> Result; /// Returns hashes of transactions currently in pending fn pending_transactions_hashes(&self, best_block: BlockNumber) -> Vec; /// Removes all transactions from the queue and restart mining operation. - fn clear_and_reset(&self, chain: &MiningBlockChainClient); + fn clear_and_reset(&self, chain: &C); /// Called when blocks are imported to chain, updates transactions queue. - fn chain_new_blocks(&self, chain: &MiningBlockChainClient, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]); + fn chain_new_blocks(&self, chain: &C, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]) + where C: AccountData + BlockChain + CallContract + RegistryInfo + BlockProducer + ScheduleInfo + SealedBlockImporter; /// PoW chain - can produce work package fn can_produce_work_package(&self) -> bool; /// New chain head event. Restart mining operation. - fn update_sealing(&self, chain: &MiningBlockChainClient); + fn update_sealing(&self, chain: &C) + where C: AccountData + BlockChain + RegistryInfo + CallContract + BlockProducer + SealedBlockImporter; /// Submit `seal` as a valid solution for the header of `pow_hash`. /// Will check the seal, but not actually insert the block into the chain. - fn submit_seal(&self, chain: &MiningBlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error>; + fn submit_seal(&self, chain: &C, pow_hash: H256, seal: Vec) -> Result<(), Error>; /// Get the sealing work package and if `Some`, apply some transform. - fn map_sealing_work(&self, chain: &MiningBlockChainClient, f: F) -> Option - where F: FnOnce(&ClosedBlock) -> T, Self: Sized; + fn map_sealing_work(&self, client: &C, f: F) -> Option + where C: AccountData + BlockChain + BlockProducer + CallContract, + F: FnOnce(&ClosedBlock) -> T, + Self: Sized; /// Query pending transactions for hash. fn transaction(&self, best_block: BlockNumber, hash: &H256) -> Option; /// Removes transaction from the queue. /// NOTE: The transaction is not removed from pending block if mining. - fn remove_pending_transaction(&self, chain: &MiningBlockChainClient, hash: &H256) -> Option; + fn remove_pending_transaction(&self, chain: &C, hash: &H256) -> Option; /// Get a list of all pending transactions in the queue. fn pending_transactions(&self) -> Vec; @@ -173,6 +184,15 @@ pub trait MinerService : Send + Sync { /// Suggested gas limit. fn sensible_gas_limit(&self) -> U256 { 21000.into() } + + /// Get `Some` `clone()` of the current pending block's state or `None` if we're not sealing. + fn pending_state(&self, latest_block_number: BlockNumber) -> Option; + + /// Get `Some` `clone()` of the current pending block header or `None` if we're not sealing. + fn pending_block_header(&self, latest_block_number: BlockNumber) -> Option
; + + /// Get `Some` `clone()` of the current pending block or `None` if we're not sealing. + fn pending_block(&self, latest_block_number: BlockNumber) -> Option; } /// Mining status diff --git a/miner/src/service_transaction_checker.rs b/ethcore/src/miner/service_transaction_checker.rs similarity index 74% rename from miner/src/service_transaction_checker.rs rename to ethcore/src/miner/service_transaction_checker.rs index 806acf29b54..a555829c5f2 100644 --- a/miner/src/service_transaction_checker.rs +++ b/ethcore/src/miner/service_transaction_checker.rs @@ -16,23 +16,14 @@ //! A service transactions contract checker. -use ethereum_types::Address; +use client::{RegistryInfo, CallContract}; use transaction::SignedTransaction; use types::ids::BlockId; -use_contract!(service_transaction, "ServiceTransaction", "res/service_transaction.json"); +use_contract!(service_transaction, "ServiceTransaction", "res/contracts/service_transaction.json"); const SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME: &'static str = "service_transaction_checker"; -/// A contract calling interface. -pub trait ContractCaller { - /// Returns address of contract from the registry, given it's name - fn registry_address(&self, name: &str) -> Option
; - - /// Executes a contract call at given block. - fn call_contract(&self, BlockId, Address, Vec) -> Result, String>; -} - /// Service transactions checker. #[derive(Default)] pub struct ServiceTransactionChecker { @@ -41,10 +32,10 @@ pub struct ServiceTransactionChecker { impl ServiceTransactionChecker { /// Checks if service transaction can be appended to the transaction queue. - pub fn check(&self, client: &ContractCaller, tx: &SignedTransaction) -> Result { + pub fn check(&self, client: &C, tx: &SignedTransaction) -> Result { assert!(tx.gas_price.is_zero()); - let address = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME) + let address = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest) .ok_or_else(|| "contract is not configured")?; trace!(target: "txqueue", "Checking service transaction checker contract from {}", address); diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index 14cf65b9ae1..ae1f6feca71 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -27,7 +27,7 @@ use super::{ManifestData, StateRebuilder, Rebuilder, RestorationStatus, Snapshot use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter}; use blockchain::BlockChain; -use client::{BlockChainClient, Client}; +use client::{Client, ChainInfo}; use engines::EthEngine; use error::Error; use ids::BlockId; diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index 8499b990489..51f417149bf 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -25,7 +25,7 @@ use hash::{KECCAK_NULL_RLP}; use account_db::AccountDBMut; use basic_account::BasicAccount; use blockchain::BlockChain; -use client::{BlockChainClient, Client}; +use client::{Client, ChainInfo}; use engines::EthEngine; use snapshot::{StateRebuilder}; use snapshot::io::{SnapshotReader, PackedWriter, PackedReader}; diff --git a/ethcore/src/snapshot/tests/proof_of_authority.rs b/ethcore/src/snapshot/tests/proof_of_authority.rs index c237acf786a..6cc2aaffaf4 100644 --- a/ethcore/src/snapshot/tests/proof_of_authority.rs +++ b/ethcore/src/snapshot/tests/proof_of_authority.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use std::str::FromStr; use account_provider::AccountProvider; -use client::{Client, BlockChainClient}; +use client::{Client, BlockChainClient, ChainInfo}; use ethkey::Secret; use snapshot::tests::helpers as snapshot_helpers; use spec::Spec; diff --git a/ethcore/src/snapshot/tests/service.rs b/ethcore/src/snapshot/tests/service.rs index 35e98f805f1..52b4b3cc979 100644 --- a/ethcore/src/snapshot/tests/service.rs +++ b/ethcore/src/snapshot/tests/service.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use tempdir::TempDir; -use client::{BlockChainClient, Client}; +use client::{Client, BlockInfo}; use ids::BlockId; use snapshot::service::{Service, ServiceParams}; use snapshot::{self, ManifestData, SnapshotService}; diff --git a/ethcore/src/snapshot/watcher.rs b/ethcore/src/snapshot/watcher.rs index b3291af7ea2..44783551fab 100644 --- a/ethcore/src/snapshot/watcher.rs +++ b/ethcore/src/snapshot/watcher.rs @@ -17,7 +17,7 @@ //! Watcher for snapshot-related chain events. use parking_lot::Mutex; -use client::{BlockChainClient, Client, ChainNotify}; +use client::{BlockInfo, Client, ChainNotify}; use ids::BlockId; use service::ClientIoMessage; diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 1b868c97b4b..09f6953f059 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -334,6 +334,28 @@ pub enum CleanupMode<'a> { TrackTouched(&'a mut HashSet
), } +/// Provides subset of `State` methods to query state information +pub trait StateInfo { + /// Get the nonce of account `a`. + fn nonce(&self, a: &Address) -> trie::Result; + + /// Get the balance of account `a`. + fn balance(&self, a: &Address) -> trie::Result; + + /// Mutate storage of account `address` so that it is `value` for `key`. + fn storage_at(&self, address: &Address, key: &H256) -> trie::Result; + + /// Get accounts' code. + fn code(&self, a: &Address) -> trie::Result>>; +} + +impl StateInfo for State { + fn nonce(&self, a: &Address) -> trie::Result { State::nonce(self, a) } + fn balance(&self, a: &Address) -> trie::Result { State::balance(self, a) } + fn storage_at(&self, address: &Address, key: &H256) -> trie::Result { State::storage_at(self, address, key) } + fn code(&self, address: &Address) -> trie::Result>> { State::code(self, address) } +} + const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ Therefore creating a SecTrieDB with this state's root will not fail."; diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index c315875fdb9..fc40123350f 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! State database abstraction. +//! State database abstraction. For more info, see the doc for `StateDB` use std::collections::{VecDeque, HashSet}; use std::sync::Arc; @@ -33,13 +33,17 @@ use bloom_journal::{Bloom, BloomJournal}; use db::COL_ACCOUNT_BLOOM; use byteorder::{LittleEndian, ByteOrder}; -/// Number of bytes allocated in the memory for accounts bloom. +/// Value used to initialize bloom bitmap size. +/// +/// Bitmap size is the size in bytes (not bits) that will be allocated in memory. pub const ACCOUNT_BLOOM_SPACE: usize = 1048576; -/// Estimated maximum number of accounts in memory bloom. +/// Value used to initialize bloom items count. +/// +/// Items count is an estimation of the maximum number of items to store. pub const DEFAULT_ACCOUNT_PRESET: usize = 1000000; -/// Database key represening number of account hashes. +/// Key for a value storing amount of hashes pub const ACCOUNT_BLOOM_HASHCOUNT_KEY: &'static [u8] = b"account_hash_count"; const STATE_CACHE_BLOCKS: usize = 12; @@ -175,7 +179,7 @@ impl StateDB { bloom } - /// Commit bloom to a database transaction + /// Commit blooms journal to the database transaction pub fn commit_bloom(batch: &mut DBTransaction, journal: BloomJournal) -> Result<(), UtilError> { assert!(journal.hash_functions <= 255); batch.put(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY, &[journal.hash_functions as u8]); @@ -305,12 +309,12 @@ impl StateDB { } } - /// Returns immutable reference to underlying hashdb. + /// Conversion method to interpret self as `HashDB` reference pub fn as_hashdb(&self) -> &HashDB { self.db.as_hashdb() } - /// Returns mutable reference to underlying hashdb. + /// Conversion method to interpret self as mutable `HashDB` reference pub fn as_hashdb_mut(&mut self) -> &mut HashDB { self.db.as_hashdb_mut() } diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index a7967dbd398..30685425bf1 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -18,7 +18,7 @@ use std::str::FromStr; use std::sync::Arc; use hash::keccak; use io::IoChannel; -use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockId}; +use client::{BlockChainClient, Client, ClientConfig, BlockId, ChainInfo, BlockInfo, PrepareOpenBlock, ImportSealedBlock, ImportBlock}; use state::{self, State, CleanupMode}; use executive::{Executive, TransactOptions}; use ethereum; diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index b23245b61e3..e6d23ec0fe9 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -19,7 +19,7 @@ use ethereum_types::{H256, U256}; use block::{OpenBlock, Drain}; use blockchain::{BlockChain, Config as BlockChainConfig}; use bytes::Bytes; -use client::{BlockChainClient, ChainNotify, Client, ClientConfig}; +use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify}; use ethereum::ethash::EthashParams; use ethkey::KeyPair; use evm::Factory as EvmFactory; diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index cbdb81a9c4b..b9c2f49ef37 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -65,7 +65,12 @@ enum CacheId { Bloom(H256), } -/// Trace database. +/// Database to store transaction execution trace. +/// +/// Whenever a transaction is executed by EVM it's execution trace is stored +/// in trace database. Each trace has information, which contracts have been +/// touched, which have been created during the execution of transaction, and +/// which calls failed. pub struct TraceDB where T: DatabaseExtras { // cache traces: RwLock>, diff --git a/ethcore/src/tx_filter.rs b/ethcore/src/tx_filter.rs index 75bd16842d2..569e836d512 100644 --- a/ethcore/src/tx_filter.rs +++ b/ethcore/src/tx_filter.rs @@ -19,7 +19,7 @@ use std::collections::HashMap; use std::collections::hash_map::Entry; use ethereum_types::{H256, Address}; -use client::{BlockChainClient, BlockId, ChainNotify}; +use client::{BlockInfo, CallContract, BlockId, ChainNotify}; use bytes::Bytes; use parking_lot::Mutex; use spec::CommonParams; @@ -64,7 +64,7 @@ impl TransactionFilter { } /// Check if transaction is allowed at given block. - pub fn transaction_allowed(&self, parent_hash: &H256, transaction: &SignedTransaction, client: &BlockChainClient) -> bool { + pub fn transaction_allowed(&self, parent_hash: &H256, transaction: &SignedTransaction, client: &C) -> bool { let mut cache = self.permission_cache.lock(); let len = cache.len(); let tx_type = match transaction.action { diff --git a/ethcore/src/verification/canon_verifier.rs b/ethcore/src/verification/canon_verifier.rs index 9c942b9b6ce..3d0fd77c6e5 100644 --- a/ethcore/src/verification/canon_verifier.rs +++ b/ethcore/src/verification/canon_verifier.rs @@ -16,6 +16,7 @@ //! Canonical verifier. +use client::{BlockInfo, CallContract}; use engines::EthEngine; use error::Error; use header::Header; @@ -25,13 +26,13 @@ use super::verification; /// A canonial verifier -- this does full verification. pub struct CanonVerifier; -impl Verifier for CanonVerifier { +impl Verifier for CanonVerifier { fn verify_block_family( &self, header: &Header, parent: &Header, engine: &EthEngine, - do_full: Option, + do_full: Option>, ) -> Result<(), Error> { verification::verify_block_family(header, parent, engine, do_full) } diff --git a/ethcore/src/verification/mod.rs b/ethcore/src/verification/mod.rs index c83d9e20fdb..d5fd4e84761 100644 --- a/ethcore/src/verification/mod.rs +++ b/ethcore/src/verification/mod.rs @@ -28,6 +28,8 @@ pub use self::canon_verifier::CanonVerifier; pub use self::noop_verifier::NoopVerifier; pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue, QueueInfo}; +use client::{BlockInfo, CallContract}; + /// Verifier type. #[derive(Debug, PartialEq, Clone)] pub enum VerifierType { @@ -47,7 +49,7 @@ impl Default for VerifierType { } /// Create a new verifier based on type. -pub fn new(v: VerifierType) -> Box { +pub fn new(v: VerifierType) -> Box> { match v { VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier), VerifierType::Noop => Box::new(NoopVerifier), diff --git a/ethcore/src/verification/noop_verifier.rs b/ethcore/src/verification/noop_verifier.rs index b1c2ec1bcd4..24b117bbc16 100644 --- a/ethcore/src/verification/noop_verifier.rs +++ b/ethcore/src/verification/noop_verifier.rs @@ -16,6 +16,7 @@ //! No-op verifier. +use client::{BlockInfo, CallContract}; use engines::EthEngine; use error::Error; use header::Header; @@ -25,13 +26,13 @@ use super::{verification, Verifier}; #[allow(dead_code)] pub struct NoopVerifier; -impl Verifier for NoopVerifier { +impl Verifier for NoopVerifier { fn verify_block_family( &self, _: &Header, _t: &Header, _: &EthEngine, - _: Option + _: Option> ) -> Result<(), Error> { Ok(()) } diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index ab1d11fd1aa..87aedd88f73 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -33,7 +33,7 @@ use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; use blockchain::*; -use client::BlockChainClient; +use client::{BlockInfo, CallContract}; use engines::EthEngine; use error::{BlockError, Error}; use header::{BlockNumber, Header}; @@ -109,24 +109,36 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &EthEngine, }) } -/// Parameters for full verification of block family: block bytes, transactions, blockchain, and state access. -pub type FullFamilyParams<'a> = (&'a [u8], &'a [SignedTransaction], &'a BlockProvider, &'a BlockChainClient); +/// Parameters for full verification of block family +pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> { + /// Serialized block bytes + pub block_bytes: &'a [u8], + + /// Signed transactions + pub transactions: &'a [SignedTransaction], + + /// Block provider to use during verification + pub block_provider: &'a BlockProvider, + + /// Engine client to use during verification + pub client: &'a C, +} /// Phase 3 verification. Check block information against parent and uncles. -pub fn verify_block_family(header: &Header, parent: &Header, engine: &EthEngine, do_full: Option) -> Result<(), Error> { +pub fn verify_block_family(header: &Header, parent: &Header, engine: &EthEngine, do_full: Option>) -> Result<(), Error> { // TODO: verify timestamp verify_parent(&header, &parent, engine.params().gas_limit_bound_divisor)?; engine.verify_block_family(&header, &parent)?; - let (bytes, txs, bc, client) = match do_full { + let params = match do_full { Some(x) => x, None => return Ok(()), }; - verify_uncles(header, bytes, bc, engine)?; + verify_uncles(header, params.block_bytes, params.block_provider, engine)?; - for transaction in txs { - engine.machine().verify_transaction(transaction, header, client)?; + for transaction in params.transactions { + engine.machine().verify_transaction(transaction, header, params.client)?; } Ok(()) @@ -486,12 +498,12 @@ mod tests { let parent = bc.block_header(header.parent_hash()) .ok_or(BlockError::UnknownParent(header.parent_hash().clone()))?; - let full_params: FullFamilyParams = ( - bytes, - &transactions[..], - bc as &BlockProvider, - &client as &::client::BlockChainClient - ); + let full_params = FullFamilyParams { + block_bytes: bytes, + transactions: &transactions[..], + block_provider: bc as &BlockProvider, + client: &client, + }; verify_block_family(&header, &parent, engine, Some(full_params)) } diff --git a/ethcore/src/verification/verifier.rs b/ethcore/src/verification/verifier.rs index 5141cea2fc5..a9ca22a4c8a 100644 --- a/ethcore/src/verification/verifier.rs +++ b/ethcore/src/verification/verifier.rs @@ -16,20 +16,23 @@ //! A generic verifier trait. +use client::{BlockInfo, CallContract}; use engines::EthEngine; use error::Error; use header::Header; use super::verification; /// Should be used to verify blocks. -pub trait Verifier: Send + Sync { +pub trait Verifier: Send + Sync + where C: BlockInfo + CallContract +{ /// Verify a block relative to its parent and uncles. - fn verify_block_family( + fn verify_block_family( &self, header: &Header, parent: &Header, engine: &EthEngine, - do_full: Option + do_full: Option> ) -> Result<(), Error>; /// Do a final verification check for an enacted header vs its expected counterpart. diff --git a/ethcore/types/src/ids.rs b/ethcore/types/src/ids.rs index ad60b3b3879..e304698a4ca 100644 --- a/ethcore/types/src/ids.rs +++ b/ethcore/types/src/ids.rs @@ -31,8 +31,6 @@ pub enum BlockId { Earliest, /// Latest mined block. Latest, - /// Pending block. - Pending, } /// Uniquely identifies transaction. diff --git a/miner/src/lib.rs b/miner/src/lib.rs index 9a2ebae13a1..e8a86bd02a4 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -31,10 +31,6 @@ extern crate parking_lot; extern crate table; extern crate transient_hashmap; -#[macro_use] -extern crate ethabi_derive; -#[macro_use] -extern crate ethabi_contract; #[cfg(test)] extern crate ethkey; #[macro_use] @@ -45,6 +41,5 @@ extern crate rustc_hex; pub mod banning_queue; pub mod external; pub mod local_transactions; -pub mod service_transaction_checker; pub mod transaction_queue; pub mod work_notify; diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 602a45730fb..594bffe2b20 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -26,7 +26,7 @@ use ethereum_types::{U256, H256, Address}; use bytes::ToPretty; use rlp::PayloadInfo; use ethcore::service::ClientService; -use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockImportError, BlockChainClient, BlockId}; +use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockImportError, Nonce, Balance, BlockChainClient, BlockId, BlockInfo, ImportBlock}; use ethcore::db::NUM_COLUMNS; use ethcore::error::ImportError; use ethcore::miner::Miner; @@ -650,7 +650,7 @@ fn execute_export_state(cmd: ExportState) -> Result<(), String> { } for account in accounts.into_iter() { - let balance = client.balance(&account, at).unwrap_or_else(U256::zero); + let balance = client.balance(&account, at.into()).unwrap_or_else(U256::zero); if cmd.min_balance.map_or(false, |m| balance < m) || cmd.max_balance.map_or(false, |m| balance > m) { last = Some(account); continue; //filtered out @@ -660,7 +660,7 @@ fn execute_export_state(cmd: ExportState) -> Result<(), String> { out.write(b",").expect("Write error"); } out.write_fmt(format_args!("\n\"0x{:x}\": {{\"balance\": \"{:x}\", \"nonce\": \"{:x}\"", account, balance, client.nonce(&account, at).unwrap_or_else(U256::zero))).expect("Write error"); - let code = client.code(&account, at).unwrap_or(None).unwrap_or_else(Vec::new); + let code = client.code(&account, at.into()).unwrap_or(None).unwrap_or_else(Vec::new); if !code.is_empty() { out.write_fmt(format_args!(", \"code_hash\": \"0x{:x}\"", keccak(&code))).expect("Write error"); if cmd.code { @@ -683,7 +683,7 @@ fn execute_export_state(cmd: ExportState) -> Result<(), String> { if last_storage.is_some() { out.write(b",").expect("Write error"); } - out.write_fmt(format_args!("\n\t\"0x{:x}\": \"0x{:x}\"", key, client.storage_at(&account, &key, at).unwrap_or_else(Default::default))).expect("Write error"); + out.write_fmt(format_args!("\n\t\"0x{:x}\": \"0x{:x}\"", key, client.storage_at(&account, &key, at.into()).unwrap_or_else(Default::default))).expect("Write error"); last_storage = Some(key); } } diff --git a/parity/dapps.rs b/parity/dapps.rs index 5c52b204c20..449fc41bff1 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use bytes::Bytes; use dir::default_data_path; use dir::helpers::replace_home; -use ethcore::client::{Client, BlockChainClient, BlockId}; +use ethcore::client::{Client, BlockChainClient, BlockId, CallContract}; use ethsync::LightSync; use futures::{Future, future, IntoFuture}; use hash_fetch::fetch::Client as FetchClient; diff --git a/parity/informant.rs b/parity/informant.rs index 018b4b99e93..0d25397137f 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -22,7 +22,7 @@ use std::sync::{Arc}; use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::time::{Instant, Duration}; -use ethcore::client::{BlockId, BlockChainClient, BlockChainInfo, BlockQueueInfo, ChainNotify, ClientReport, Client}; +use ethcore::client::{BlockId, BlockChainClient, ChainInfo, BlockInfo, BlockChainInfo, BlockQueueInfo, ChainNotify, ClientReport, Client}; use ethcore::header::BlockNumber; use ethcore::service::ClientIoMessage; use ethcore::snapshot::{RestorationStatus, SnapshotService as SS}; diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 96a8e836f65..58970b1a724 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -60,6 +60,7 @@ node-health = { path = "../dapps/node-health" } parity-reactor = { path = "../util/reactor" } parity-updater = { path = "../updater" } parity-version = { path = "../util/version" } +patricia-trie = { path = "../util/patricia_trie" } rlp = { path = "../util/rlp" } stats = { path = "../util/stats" } vm = { path = "../ethcore/vm" } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index d8e5495cc8d..4a2003d652b 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -68,6 +68,7 @@ extern crate rlp; extern crate stats; extern crate keccak_hash as hash; extern crate hardware_wallet; +extern crate patricia_trie as trie; #[macro_use] extern crate log; diff --git a/rpc/src/v1/helpers/light_fetch.rs b/rpc/src/v1/helpers/light_fetch.rs index eccf521d9f3..f0c389d5778 100644 --- a/rpc/src/v1/helpers/light_fetch.rs +++ b/rpc/src/v1/helpers/light_fetch.rs @@ -197,7 +197,19 @@ impl LightFetch { let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone()); let req: CallRequestHelper = req.into(); - let id = num.unwrap_or_default().into(); + + // Note: Here we treat `Pending` as `Latest`. + // Since light clients don't produce pending blocks + // (they don't have state) we can safely fallback to `Latest`. + let id = match num.unwrap_or_default() { + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => { + warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`"); + BlockId::Latest + } + }; let from = req.from.unwrap_or(Address::zero()); let nonce_fut = match req.nonce { @@ -308,7 +320,7 @@ impl LightFetch { let best_number = self.client.chain_info().best_block_number; let block_number = |id| match id { BlockId::Earliest => Some(0), - BlockId::Latest | BlockId::Pending => Some(best_number), + BlockId::Latest => Some(best_number), BlockId::Hash(h) => self.client.block_header(BlockId::Hash(h)).map(|hdr| hdr.number()), BlockId::Number(x) => Some(x), }; diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 08ce7fbbcd0..4305dc63171 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -28,16 +28,17 @@ use parking_lot::Mutex; use ethash::SeedHashCompute; use ethcore::account_provider::{AccountProvider, DappId}; use ethcore::block::IsBlock; -use ethcore::client::{MiningBlockChainClient, BlockId, TransactionId, UncleId}; +use ethcore::client::{MiningBlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo}; use ethcore::ethereum::Ethash; use ethcore::filter::Filter as EthcoreFilter; -use ethcore::header::{Header as BlockHeader, BlockNumber as EthBlockNumber}; +use ethcore::header::{BlockNumber as EthBlockNumber, Seal}; use ethcore::log_entry::LogEntry; use ethcore::miner::MinerService; use ethcore::snapshot::SnapshotService; +use ethcore::encoded; use ethsync::{SyncProvider}; use miner::external::ExternalMinerService; -use transaction::SignedTransaction; +use transaction::{SignedTransaction, LocalizedTransaction}; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_core::futures::future; @@ -51,11 +52,11 @@ use v1::traits::Eth; use v1::types::{ RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, Index, Filter, Log, Receipt, Work, - H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256, + H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256, block_number_to_id, }; use v1::metadata::Metadata; -const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed"; +const EXTRA_INFO_PROOF: &'static str = "Object exists in blockchain (fetched earlier), extra_info is always available if object exists; qed"; /// Eth RPC options pub struct EthClientOptions { @@ -109,11 +110,43 @@ pub struct EthClient where eip86_transition: u64, } -impl EthClient where - C: MiningBlockChainClient, +enum BlockNumberOrId { + Number(BlockNumber), + Id(BlockId), +} + +impl From for BlockNumberOrId { + fn from(value: BlockId) -> BlockNumberOrId { + BlockNumberOrId::Id(value) + } +} + +impl From for BlockNumberOrId { + fn from(value: BlockNumber) -> BlockNumberOrId { + BlockNumberOrId::Number(value) + } +} + +enum PendingOrBlock { + Block(BlockId), + Pending, +} + +struct PendingUncleId { + id: PendingOrBlock, + position: usize, +} + +enum PendingTransactionId { + Hash(H256), + Location(PendingOrBlock, usize) +} + +impl EthClient where + C: MiningBlockChainClient + StateClient + Call + EngineInfo, SN: SnapshotService, S: SyncProvider, - M: MinerService, + M: MinerService, EM: ExternalMinerService { /// Creates new EthClient. @@ -145,9 +178,46 @@ impl EthClient where unwrap_provider(&self.accounts) } - fn block(&self, id: BlockId, include_txs: bool) -> Result> { + fn rich_block(&self, id: BlockNumberOrId, include_txs: bool) -> Result> { let client = &self.client; - match (client.block(id.clone()), client.block_total_difficulty(id)) { + + let client_query = |id| (client.block(id), client.block_total_difficulty(id), client.block_extra_info(id)); + + let (block, difficulty, extra) = match id { + BlockNumberOrId::Number(BlockNumber::Pending) => { + let info = self.client.chain_info(); + let pending_block = self.miner.pending_block(info.best_block_number); + let difficulty = { + let latest_difficulty = self.client.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed"); + let pending_difficulty = self.miner.pending_block_header(info.best_block_number).map(|header| *header.difficulty()); + + if let Some(difficulty) = pending_difficulty { + difficulty + latest_difficulty + } else { + latest_difficulty + } + }; + + let extra = pending_block.as_ref().map(|b| self.client.engine().extra_info(&b.header)); + + (pending_block.map(|b| encoded::Block::new(b.rlp_bytes(Seal::Without))), Some(difficulty), extra) + }, + + BlockNumberOrId::Number(num) => { + let id = match num { + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Pending => unreachable!(), // Already covered + }; + + client_query(id) + }, + + BlockNumberOrId::Id(id) => client_query(id), + }; + + match (block, difficulty) { (Some(block), Some(total_difficulty)) => { let view = block.header_view(); Ok(Some(RichBlock { @@ -176,29 +246,109 @@ impl EthClient where }, extra_data: Bytes::new(view.extra_data()), }, - extra_info: client.block_extra_info(id.clone()).expect(EXTRA_INFO_PROOF), + extra_info: extra.expect(EXTRA_INFO_PROOF), })) }, _ => Ok(None) } } - fn transaction(&self, id: TransactionId) -> Result> { - match self.client.transaction(id) { + fn transaction(&self, id: PendingTransactionId) -> Result> { + let client_transaction = |id| match self.client.transaction(id) { Some(t) => Ok(Some(Transaction::from_localized(t, self.eip86_transition))), None => Ok(None), + }; + + match id { + PendingTransactionId::Hash(hash) => client_transaction(TransactionId::Hash(hash)), + + PendingTransactionId::Location(PendingOrBlock::Block(block), index) => { + client_transaction(TransactionId::Location(block, index)) + }, + + PendingTransactionId::Location(PendingOrBlock::Pending, index) => { + let info = self.client.chain_info(); + let pending_block = match self.miner.pending_block(info.best_block_number) { + Some(block) => block, + None => return Ok(None), + }; + + // Implementation stolen from `extract_transaction_at_index` + let transaction = pending_block.transactions.get(index) + // Verify if transaction signature is correct. + .and_then(|tx| SignedTransaction::new(tx.clone()).ok()) + .map(|signed_tx| { + let (signed, sender, _) = signed_tx.deconstruct(); + let block_hash = pending_block.header.hash(); + let block_number = pending_block.header.number(); + let transaction_index = index; + let cached_sender = Some(sender); + + LocalizedTransaction { + signed, + block_number, + block_hash, + transaction_index, + cached_sender, + } + }) + .map(|tx| Transaction::from_localized(tx, self.eip86_transition)); + + Ok(transaction) + } } } - fn uncle(&self, id: UncleId) -> Result> { + fn uncle(&self, id: PendingUncleId) -> Result> { let client = &self.client; - let uncle: BlockHeader = match client.uncle(id) { - Some(hdr) => hdr.decode(), - None => { return Ok(None); } - }; - let parent_difficulty = match client.block_total_difficulty(BlockId::Hash(uncle.parent_hash().clone())) { - Some(difficulty) => difficulty, - None => { return Ok(None); } + + let (uncle, parent_difficulty, extra) = match id { + PendingUncleId { id: PendingOrBlock::Pending, position } => { + let info = self.client.chain_info(); + + let pending_block = match self.miner.pending_block(info.best_block_number) { + Some(block) => block, + None => return Ok(None), + }; + + let uncle = match pending_block.uncles.get(position) { + Some(uncle) => uncle.clone(), + None => return Ok(None), + }; + + let difficulty = { + let latest_difficulty = self.client.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed"); + let pending_difficulty = self.miner.pending_block_header(info.best_block_number).map(|header| *header.difficulty()); + + if let Some(difficulty) = pending_difficulty { + difficulty + latest_difficulty + } else { + latest_difficulty + } + }; + + let extra = self.client.engine().extra_info(&pending_block.header); + + (uncle, difficulty, extra) + }, + + PendingUncleId { id: PendingOrBlock::Block(block_id), position } => { + let uncle_id = UncleId { block: block_id, position }; + + let uncle = match client.uncle(uncle_id) { + Some(hdr) => hdr.decode(), + None => { return Ok(None); } + }; + + let parent_difficulty = match client.block_total_difficulty(BlockId::Hash(uncle.parent_hash().clone())) { + Some(difficulty) => difficulty, + None => { return Ok(None); } + }; + + let extra = client.uncle_extra_info(uncle_id).expect(EXTRA_INFO_PROOF); + + (uncle, parent_difficulty, extra) + } }; let size = client.block(BlockId::Hash(uncle.hash())) @@ -229,7 +379,7 @@ impl EthClient where uncles: vec![], transactions: BlockTransactions::Hashes(vec![]), }, - extra_info: client.uncle_extra_info(id).expect(EXTRA_INFO_PROOF), + extra_info: extra, }; Ok(Some(block)) } @@ -241,6 +391,24 @@ impl EthClient where .and_then(|_| store.dapp_addresses(dapp)) .map_err(|e| errors::account("Could not fetch accounts.", e)) } + + fn get_state(&self, number: BlockNumber) -> StateOrBlock { + match number { + BlockNumber::Num(num) => BlockId::Number(num).into(), + BlockNumber::Earliest => BlockId::Earliest.into(), + BlockNumber::Latest => BlockId::Latest.into(), + + BlockNumber::Pending => { + let info = self.client.chain_info(); + + self.miner + .pending_state(info.best_block_number) + .map(|s| Box::new(s) as Box) + .unwrap_or(Box::new(self.client.latest_state()) as Box) + .into() + } + } + } } pub fn pending_logs(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec where M: MinerService { @@ -265,20 +433,27 @@ pub fn pending_logs(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFi fn check_known(client: &C, number: BlockNumber) -> Result<()> where C: MiningBlockChainClient { use ethcore::block_status::BlockStatus; - match client.block_status(number.into()) { + let id = match number { + BlockNumber::Pending => return Ok(()), + + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Earliest => BlockId::Earliest, + }; + + match client.block_status(id) { BlockStatus::InChain => Ok(()), - BlockStatus::Pending => Ok(()), _ => Err(errors::unknown_block()), } } const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6. -impl Eth for EthClient where - C: MiningBlockChainClient + 'static, +impl Eth for EthClient where + C: MiningBlockChainClient + StateClient + Call + EngineInfo + 'static, SN: SnapshotService + 'static, S: SyncProvider + 'static, - M: MinerService + 'static, + M: MinerService + 'static, EM: ExternalMinerService + 'static, { type Metadata = Metadata; @@ -357,10 +532,10 @@ impl Eth for EthClient where fn balance(&self, address: RpcH160, num: Trailing) -> BoxFuture { let address = address.into(); - let id = num.unwrap_or_default(); + let num = num.unwrap_or_default(); - try_bf!(check_known(&*self.client, id.clone())); - let res = match self.client.balance(&address, id.into()) { + try_bf!(check_known(&*self.client, num.clone())); + let res = match self.client.balance(&address, self.get_state(num)) { Some(balance) => Ok(balance.into()), None => Err(errors::state_pruned()), }; @@ -372,10 +547,10 @@ impl Eth for EthClient where let address: Address = RpcH160::into(address); let position: U256 = RpcU256::into(pos); - let id = num.unwrap_or_default(); + let num = num.unwrap_or_default(); - try_bf!(check_known(&*self.client, id.clone())); - let res = match self.client.storage_at(&address, &H256::from(position), id.into()) { + try_bf!(check_known(&*self.client, num.clone())); + let res = match self.client.storage_at(&address, &H256::from(position), self.get_state(num)) { Some(s) => Ok(s.into()), None => Err(errors::state_pruned()), }; @@ -390,15 +565,33 @@ impl Eth for EthClient where BlockNumber::Pending if self.options.pending_nonce_from_queue => { let nonce = self.miner.last_nonce(&address) .map(|n| n + 1.into()) - .or_else(|| self.client.nonce(&address, BlockNumber::Pending.into())); + .or_else(|| self.client.nonce(&address, BlockId::Latest)); + match nonce { Some(nonce) => Ok(nonce.into()), None => Err(errors::database("latest nonce missing")) } - } - id => { - try_bf!(check_known(&*self.client, id.clone())); - match self.client.nonce(&address, id.into()) { + }, + + BlockNumber::Pending => { + let info = self.client.chain_info(); + let nonce = self.miner + .pending_state(info.best_block_number) + .and_then(|s| s.nonce(&address).ok()) + .or_else(|| { + warn!("Fallback to `BlockId::Latest`"); + self.client.nonce(&address, BlockId::Latest) + }); + + match nonce { + Some(nonce) => Ok(nonce.into()), + None => Err(errors::database("latest nonce missing")) + } + }, + + number => { + try_bf!(check_known(&*self.client, number.clone())); + match self.client.nonce(&address, block_number_to_id(number)) { Some(nonce) => Ok(nonce.into()), None => Err(errors::state_pruned()), } @@ -419,7 +612,7 @@ impl Eth for EthClient where self.miner.status().transactions_in_pending_block.into() ), _ => - self.client.block(num.into()) + self.client.block(block_number_to_id(num)) .map(|block| block.transactions_count().into()) })) } @@ -432,7 +625,7 @@ impl Eth for EthClient where fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture> { Box::new(future::ok(match num { BlockNumber::Pending => Some(0.into()), - _ => self.client.block(num.into()) + _ => self.client.block(block_number_to_id(num)) .map(|block| block.uncles_count().into() ), })) @@ -441,10 +634,10 @@ impl Eth for EthClient where fn code_at(&self, address: RpcH160, num: Trailing) -> BoxFuture { let address: Address = RpcH160::into(address); - let id = num.unwrap_or_default(); - try_bf!(check_known(&*self.client, id.clone())); + let num = num.unwrap_or_default(); + try_bf!(check_known(&*self.client, num.clone())); - let res = match self.client.code(&address, id.into()) { + let res = match self.client.code(&address, self.get_state(num)) { Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)), None => Err(errors::state_pruned()), }; @@ -453,17 +646,17 @@ impl Eth for EthClient where } fn block_by_hash(&self, hash: RpcH256, include_txs: bool) -> BoxFuture> { - Box::new(future::done(self.block(BlockId::Hash(hash.into()), include_txs))) + Box::new(future::done(self.rich_block(BlockId::Hash(hash.into()).into(), include_txs))) } fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture> { - Box::new(future::done(self.block(num.into(), include_txs))) + Box::new(future::done(self.rich_block(num.into(), include_txs))) } fn transaction_by_hash(&self, hash: RpcH256) -> BoxFuture> { let hash: H256 = hash.into(); let block_number = self.client.chain_info().best_block_number; - let tx = try_bf!(self.transaction(TransactionId::Hash(hash))).or_else(|| { + let tx = try_bf!(self.transaction(PendingTransactionId::Hash(hash))).or_else(|| { self.miner.transaction(block_number, &hash) .map(|t| Transaction::from_pending(t, block_number, self.eip86_transition)) }); @@ -472,15 +665,20 @@ impl Eth for EthClient where } fn transaction_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> BoxFuture> { - Box::new(future::done( - self.transaction(TransactionId::Location(BlockId::Hash(hash.into()), index.value())) - )) + let id = PendingTransactionId::Location(PendingOrBlock::Block(BlockId::Hash(hash.into())), index.value()); + Box::new(future::done(self.transaction(id))) } fn transaction_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture> { - Box::new(future::done( - self.transaction(TransactionId::Location(num.into(), index.value())) - )) + let block_id = match num { + BlockNumber::Latest => PendingOrBlock::Block(BlockId::Latest), + BlockNumber::Earliest => PendingOrBlock::Block(BlockId::Earliest), + BlockNumber::Num(num) => PendingOrBlock::Block(BlockId::Number(num)), + BlockNumber::Pending => PendingOrBlock::Pending, + }; + + let transaction_id = PendingTransactionId::Location(block_id, index.value()); + Box::new(future::done(self.transaction(transaction_id))) } fn transaction_receipt(&self, hash: RpcH256) -> BoxFuture> { @@ -497,17 +695,22 @@ impl Eth for EthClient where } fn uncle_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> BoxFuture> { - Box::new(future::done(self.uncle(UncleId { - block: BlockId::Hash(hash.into()), + Box::new(future::done(self.uncle(PendingUncleId { + id: PendingOrBlock::Block(BlockId::Hash(hash.into())), position: index.value() }))) } fn uncle_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture> { - Box::new(future::done(self.uncle(UncleId { - block: num.into(), - position: index.value() - }))) + let id = match num { + BlockNumber::Latest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Latest), position: index.value() }, + BlockNumber::Earliest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Earliest), position: index.value() }, + BlockNumber::Num(num) => PendingUncleId { id: PendingOrBlock::Block(BlockId::Number(num)), position: index.value() }, + + BlockNumber::Pending => PendingUncleId { id: PendingOrBlock::Pending, position: index.value() }, + }; + + Box::new(future::done(self.uncle(id))) } fn compilers(&self) -> Result> { @@ -630,7 +833,28 @@ impl Eth for EthClient where let signed = try_bf!(fake_sign::sign_call(request, meta.is_dapp())); let num = num.unwrap_or_default(); - let result = self.client.call(&signed, Default::default(), num.into()); + + let (mut state, header) = if num == BlockNumber::Pending { + let info = self.client.chain_info(); + let state = try_bf!(self.miner.pending_state(info.best_block_number).ok_or(errors::state_pruned())); + let header = try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or(errors::state_pruned())); + + (state, header) + } else { + let id = match num { + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => unreachable!(), // Already covered + }; + + let state = try_bf!(self.client.state_at(id).ok_or(errors::state_pruned())); + let header = try_bf!(self.client.block_header(id).ok_or(errors::state_pruned())); + + (state, header.decode()) + }; + + let result = self.client.call(&signed, Default::default(), &mut state, &header); Box::new(future::done(result .map(|b| b.output.into()) @@ -641,7 +865,29 @@ impl Eth for EthClient where fn estimate_gas(&self, meta: Self::Metadata, request: CallRequest, num: Trailing) -> BoxFuture { let request = CallRequest::into(request); let signed = try_bf!(fake_sign::sign_call(request, meta.is_dapp())); - Box::new(future::done(self.client.estimate_gas(&signed, num.unwrap_or_default().into()) + let num = num.unwrap_or_default(); + + let (state, header) = if num == BlockNumber::Pending { + let info = self.client.chain_info(); + let state = try_bf!(self.miner.pending_state(info.best_block_number).ok_or(errors::state_pruned())); + let header = try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or(errors::state_pruned())); + + (state, header) + } else { + let id = match num { + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => unreachable!(), // Already covered + }; + + let state = try_bf!(self.client.state_at(id).ok_or(errors::state_pruned())); + let header = try_bf!(self.client.block_header(id).ok_or(errors::state_pruned())); + + (state, header.decode()) + }; + + Box::new(future::done(self.client.estimate_gas(&signed, &state, &header) .map(Into::into) .map_err(errors::call) )) diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index 7a5fb288699..e29f088c87d 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -65,6 +65,23 @@ pub struct EthClient { gas_price_percentile: usize, } +impl EthClient { + fn num_to_id(num: BlockNumber) -> BlockId { + // Note: Here we treat `Pending` as `Latest`. + // Since light clients don't produce pending blocks + // (they don't have state) we can safely fallback to `Latest`. + match num { + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => { + warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`"); + BlockId::Latest + } + } + } +} + impl Clone for EthClient { fn clone(&self) -> Self { // each instance should have its own poll manager. @@ -264,7 +281,7 @@ impl Eth for EthClient { } fn balance(&self, address: RpcH160, num: Trailing) -> BoxFuture { - Box::new(self.fetcher().account(address.into(), num.unwrap_or_default().into()) + Box::new(self.fetcher().account(address.into(), Self::num_to_id(num.unwrap_or_default())) .map(|acc| acc.map_or(0.into(), |a| a.balance).into())) } @@ -277,11 +294,11 @@ impl Eth for EthClient { } fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture> { - Box::new(self.rich_block(num.into(), include_txs).map(Some)) + Box::new(self.rich_block(Self::num_to_id(num), include_txs).map(Some)) } fn transaction_count(&self, address: RpcH160, num: Trailing) -> BoxFuture { - Box::new(self.fetcher().account(address.into(), num.unwrap_or_default().into()) + Box::new(self.fetcher().account(address.into(), Self::num_to_id(num.unwrap_or_default())) .map(|acc| acc.map_or(0.into(), |a| a.nonce).into())) } @@ -304,7 +321,7 @@ impl Eth for EthClient { fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture> { let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); - Box::new(self.fetcher().header(num.into()).and_then(move |hdr| { + Box::new(self.fetcher().header(Self::num_to_id(num)).and_then(move |hdr| { if hdr.transactions_root() == KECCAK_NULL_RLP { Either::A(future::ok(Some(U256::from(0).into()))) } else { @@ -336,7 +353,7 @@ impl Eth for EthClient { fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture> { let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); - Box::new(self.fetcher().header(num.into()).and_then(move |hdr| { + Box::new(self.fetcher().header(Self::num_to_id(num)).and_then(move |hdr| { if hdr.uncles_hash() == KECCAK_EMPTY_LIST_RLP { Either::B(future::ok(Some(U256::from(0).into()))) } else { @@ -350,7 +367,7 @@ impl Eth for EthClient { } fn code_at(&self, address: RpcH160, num: Trailing) -> BoxFuture { - Box::new(self.fetcher().code(address.into(), num.unwrap_or_default().into()).map(Into::into)) + Box::new(self.fetcher().code(address.into(), Self::num_to_id(num.unwrap_or_default())).map(Into::into)) } fn send_raw_transaction(&self, raw: Bytes) -> Result { @@ -422,7 +439,7 @@ impl Eth for EthClient { fn transaction_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture> { let eip86 = self.client.eip86_transition(); - Box::new(self.fetcher().block(num.into()).map(move |block| { + Box::new(self.fetcher().block(Self::num_to_id(num)).map(move |block| { light_fetch::extract_transaction_at_index(block, idx.value(), eip86) })) } @@ -467,7 +484,7 @@ impl Eth for EthClient { fn uncle_by_block_number_and_index(&self, num: BlockNumber, idx: Index) -> BoxFuture> { let client = self.client.clone(); - Box::new(self.fetcher().block(num.into()).map(move |block| { + Box::new(self.fetcher().block(Self::num_to_id(num)).map(move |block| { extract_uncle_at_index(block, idx, client) })) } diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 060d7288b0e..3b2fab9a3cb 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -27,6 +27,7 @@ use ethsync::LightSyncProvider; use ethcore::account_provider::AccountProvider; use ethcore_logger::RotatingLogger; use node_health::{NodeHealth, Health}; +use ethcore::ids::BlockId; use light::client::LightChainClient; @@ -405,7 +406,16 @@ impl Parity for ParityClient { } }; - Box::new(self.fetcher().header(number.unwrap_or_default().into()).map(from_encoded)) + // Note: Here we treat `Pending` as `Latest`. + // Since light clients don't produce pending blocks + // (they don't have state) we can safely fallback to `Latest`. + let id = match number.unwrap_or_default() { + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest, + }; + + Box::new(self.fetcher().header(id).map(from_encoded)) } fn ipfs_cid(&self, content: Bytes) -> Result { diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index b822bfd56eb..95c810aa1a3 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -27,10 +27,11 @@ use ethkey::{Brain, Generator}; use ethstore::random_phrase; use ethsync::{SyncProvider, ManageNetwork}; use ethcore::account_provider::AccountProvider; -use ethcore::client::{MiningBlockChainClient}; +use ethcore::client::{MiningBlockChainClient, StateClient, Call}; use ethcore::ids::BlockId; use ethcore::miner::MinerService; use ethcore::mode::Mode; +use ethcore::state::StateInfo; use ethcore_logger::RotatingLogger; use node_health::{NodeHealth, Health}; use updater::{Service as UpdateService}; @@ -48,7 +49,8 @@ use v1::types::{ TransactionStats, LocalTransactionStatus, BlockNumber, ConsensusCapability, VersionInfo, OperationsInfo, DappId, ChainStatus, - AccountInfo, HwAccountInfo, RichHeader + AccountInfo, HwAccountInfo, RichHeader, + block_number_to_id }; use Host; @@ -112,9 +114,10 @@ impl ParityClient where } } -impl Parity for ParityClient where - C: MiningBlockChainClient + 'static, - M: MinerService + 'static, +impl Parity for ParityClient where + S: StateInfo + 'static, + C: MiningBlockChainClient + StateClient + Call + 'static, + M: MinerService + 'static, U: UpdateService + 'static, { type Metadata = Metadata; @@ -275,14 +278,32 @@ impl Parity for ParityClient where } fn list_accounts(&self, count: u64, after: Option, block_number: Trailing) -> Result>> { + let number = match block_number.unwrap_or_default() { + BlockNumber::Pending => { + warn!("BlockNumber::Pending is unsupported"); + return Ok(None); + }, + + num => block_number_to_id(num) + }; + Ok(self.client - .list_accounts(block_number.unwrap_or_default().into(), after.map(Into::into).as_ref(), count) + .list_accounts(number, after.map(Into::into).as_ref(), count) .map(|a| a.into_iter().map(Into::into).collect())) } fn list_storage_keys(&self, address: H160, count: u64, after: Option, block_number: Trailing) -> Result>> { + let number = match block_number.unwrap_or_default() { + BlockNumber::Pending => { + warn!("BlockNumber::Pending is unsupported"); + return Ok(None); + }, + + num => block_number_to_id(num) + }; + Ok(self.client - .list_storage(block_number.unwrap_or_default().into(), &address.into(), after.map(Into::into).as_ref(), count) + .list_storage(number, &address.into(), after.map(Into::into).as_ref(), count) .map(|a| a.into_iter().map(Into::into).collect())) } @@ -396,17 +417,31 @@ impl Parity for ParityClient where } fn block_header(&self, number: Trailing) -> BoxFuture { - const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed"; - - let id: BlockId = number.unwrap_or_default().into(); - let encoded = match self.client.block_header(id.clone()) { - Some(encoded) => encoded, - None => return Box::new(future::err(errors::unknown_block())), + const EXTRA_INFO_PROOF: &str = "Object exists in blockchain (fetched earlier), extra_info is always available if object exists; qed"; + let number = number.unwrap_or_default(); + + let (header, extra) = if number == BlockNumber::Pending { + let info = self.client.chain_info(); + let header = try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or(errors::unknown_block())); + + (header.encoded(), None) + } else { + let id = match number { + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => unreachable!(), // Already covered + }; + + let header = try_bf!(self.client.block_header(id.clone()).ok_or(errors::unknown_block())); + let info = self.client.block_extra_info(id).expect(EXTRA_INFO_PROOF); + + (header, Some(info)) }; Box::new(future::ok(RichHeader { - inner: encoded.into(), - extra_info: self.client.block_extra_info(id).expect(EXTRA_INFO_PROOF), + inner: header.into(), + extra_info: extra.unwrap_or_default(), })) } @@ -414,7 +449,7 @@ impl Parity for ParityClient where ipfs::cid(content) } - fn call(&self, meta: Self::Metadata, requests: Vec, block: Trailing) -> Result> { + fn call(&self, meta: Self::Metadata, requests: Vec, num: Trailing) -> Result> { let requests = requests .into_iter() .map(|request| Ok(( @@ -423,9 +458,29 @@ impl Parity for ParityClient where ))) .collect::>>()?; - let block = block.unwrap_or_default(); + let num = num.unwrap_or_default(); + + let (mut state, header) = if num == BlockNumber::Pending { + let info = self.client.chain_info(); + let state = self.miner.pending_state(info.best_block_number).ok_or(errors::state_pruned())?; + let header = self.miner.pending_block_header(info.best_block_number).ok_or(errors::state_pruned())?; + + (state, header) + } else { + let id = match num { + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => unreachable!(), // Already covered + }; + + let state = self.client.state_at(id).ok_or(errors::state_pruned())?; + let header = self.client.block_header(id).ok_or(errors::state_pruned())?; + + (state, header.decode()) + }; - self.client.call_many(&requests, block.into()) + self.client.call_many(&requests, &mut state, &header) .map(|res| res.into_iter().map(|res| res.output.into()).collect()) .map_err(errors::call) } diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 6ee10e2373f..0ee1afd5fa7 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -18,7 +18,7 @@ use std::sync::Arc; -use ethcore::client::{MiningBlockChainClient, CallAnalytics, TransactionId, TraceId}; +use ethcore::client::{MiningBlockChainClient, CallAnalytics, TransactionId, TraceId, StateClient, StateInfo, Call, BlockId}; use rlp::UntrustedRlp; use transaction::SignedTransaction; @@ -27,7 +27,7 @@ use jsonrpc_macros::Trailing; use v1::Metadata; use v1::traits::Traces; use v1::helpers::{errors, fake_sign}; -use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, TraceOptions, H256}; +use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, TraceOptions, H256, block_number_to_id}; fn to_call_analytics(flags: TraceOptions) -> CallAnalytics { CallAnalytics { @@ -51,7 +51,10 @@ impl TracesClient { } } -impl Traces for TracesClient where C: MiningBlockChainClient + 'static { +impl Traces for TracesClient where + S: StateInfo + 'static, + C: MiningBlockChainClient + StateClient + Call + 'static +{ type Metadata = Metadata; fn filter(&self, filter: TraceFilter) -> Result>> { @@ -60,7 +63,12 @@ impl Traces for TracesClient where C: MiningBlockChainClient + 'static { } fn block_traces(&self, block_number: BlockNumber) -> Result>> { - Ok(self.client.block_traces(block_number.into()) + let id = match block_number { + BlockNumber::Pending => return Ok(None), + num => block_number_to_id(num) + }; + + Ok(self.client.block_traces(id) .map(|traces| traces.into_iter().map(LocalizedTrace::from).collect())) } @@ -85,7 +93,18 @@ impl Traces for TracesClient where C: MiningBlockChainClient + 'static { let request = CallRequest::into(request); let signed = fake_sign::sign_call(request, meta.is_dapp())?; - self.client.call(&signed, to_call_analytics(flags), block.into()) + let id = match block { + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + + BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())), + }; + + let mut state = self.client.state_at(id).ok_or(errors::state_pruned())?; + let header = self.client.block_header(id).ok_or(errors::state_pruned())?; + + self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode()) .map(TraceResults::from) .map_err(errors::call) } @@ -101,7 +120,18 @@ impl Traces for TracesClient where C: MiningBlockChainClient + 'static { }) .collect::>>()?; - self.client.call_many(&requests, block.into()) + let id = match block { + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + + BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())), + }; + + let mut state = self.client.state_at(id).ok_or(errors::state_pruned())?; + let header = self.client.block_header(id).ok_or(errors::state_pruned())?; + + self.client.call_many(&requests, &mut state, &header.decode()) .map(|results| results.into_iter().map(TraceResults::from).collect()) .map_err(errors::call) } @@ -112,7 +142,18 @@ impl Traces for TracesClient where C: MiningBlockChainClient + 'static { let tx = UntrustedRlp::new(&raw_transaction.into_vec()).as_val().map_err(|e| errors::invalid_params("Transaction is not valid RLP", e))?; let signed = SignedTransaction::new(tx).map_err(errors::transaction)?; - self.client.call(&signed, to_call_analytics(flags), block.into()) + let id = match block { + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + + BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())), + }; + + let mut state = self.client.state_at(id).ok_or(errors::state_pruned())?; + let header = self.client.block_header(id).ok_or(errors::state_pruned())?; + + self.client.call(&signed, to_call_analytics(flags), &mut state, &header.decode()) .map(TraceResults::from) .map_err(errors::call) } @@ -124,7 +165,15 @@ impl Traces for TracesClient where C: MiningBlockChainClient + 'static { } fn replay_block_transactions(&self, block_number: BlockNumber, flags: TraceOptions) -> Result> { - self.client.replay_block_transactions(block_number.into(), to_call_analytics(flags)) + let id = match block_number { + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + + BlockNumber::Pending => return Err(errors::invalid_params("`BlockNumber::Pending` is not supported", ())), + }; + + self.client.replay_block_transactions(id, to_call_analytics(flags)) .map(|results| results.into_iter().map(TraceResults::from).collect()) .map_err(errors::call) } diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index c8444d30400..84dd64644d7 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -22,10 +22,10 @@ use std::time::Duration; use ethereum_types::{U256, H256, Address}; use ethcore::account_provider::AccountProvider; use ethcore::block::Block; -use ethcore::client::{BlockChainClient, Client, ClientConfig}; +use ethcore::client::{BlockChainClient, Client, ClientConfig, ChainInfo, ImportBlock}; use ethcore::ethereum; use ethcore::ids::BlockId; -use ethcore::miner::{MinerOptions, Banning, GasPricer, MinerService, Miner, PendingSet, GasLimit}; +use ethcore::miner::{MinerOptions, Banning, GasPricer, Miner, PendingSet, GasLimit}; use ethcore::spec::{Genesis, Spec}; use ethcore::views::BlockView; use ethjson::blockchain::BlockChain; @@ -101,7 +101,7 @@ fn make_spec(chain: &BlockChain) -> Spec { struct EthTester { client: Arc, - _miner: Arc, + _miner: Arc, _snapshot: Arc, accounts: Arc, handler: IoHandler, diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 994cd544a0a..ca9993baea4 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -18,16 +18,19 @@ use std::collections::{BTreeMap, HashMap}; use std::collections::hash_map::Entry; -use ethereum_types::{H256, U256, Address}; + use bytes::Bytes; use ethcore::account_provider::SignError as AccountError; -use ethcore::block::ClosedBlock; -use ethcore::client::MiningBlockChainClient; +use ethcore::block::{Block, ClosedBlock}; +use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo}; +use ethcore::engines::EthEngine; use ethcore::error::Error; -use ethcore::header::BlockNumber; +use ethcore::header::{BlockNumber, Header}; +use ethcore::ids::BlockId; use ethcore::miner::{MinerService, MinerStatus}; -use miner::local_transactions::Status as LocalTransactionStatus; use ethcore::receipt::{Receipt, RichReceipt}; +use ethereum_types::{H256, U256, Address}; +use miner::local_transactions::Status as LocalTransactionStatus; use parking_lot::{RwLock, Mutex}; use transaction::{UnverifiedTransaction, SignedTransaction, PendingTransaction, ImportResult as TransactionImportResult}; @@ -92,7 +95,39 @@ impl TestMinerService { } } +impl StateClient for TestMinerService { + // State will not be used by test client anyway, since all methods that accept state are mocked + type State = (); + + fn latest_state(&self) -> Self::State { + () + } + + fn state_at(&self, _id: BlockId) -> Option { + Some(()) + } +} + +impl EngineInfo for TestMinerService { + fn engine(&self) -> &EthEngine { + unimplemented!() + } +} + impl MinerService for TestMinerService { + type State = (); + + fn pending_state(&self, _latest_block_number: BlockNumber) -> Option { + None + } + + fn pending_block_header(&self, _latest_block_number: BlockNumber) -> Option
{ + None + } + + fn pending_block(&self, _latest_block_number: BlockNumber) -> Option { + None + } /// Returns miner's status. fn status(&self) -> MinerStatus { @@ -164,7 +199,7 @@ impl MinerService for TestMinerService { } /// Imports transactions to transaction queue. - fn import_external_transactions(&self, _chain: &MiningBlockChainClient, transactions: Vec) -> + fn import_external_transactions(&self, _chain: &C, transactions: Vec) -> Vec> { // lets assume that all txs are valid let transactions: Vec<_> = transactions.into_iter().map(|tx| SignedTransaction::new(tx).unwrap()).collect(); @@ -181,7 +216,7 @@ impl MinerService for TestMinerService { } /// Imports transactions to transaction queue. - fn import_own_transaction(&self, chain: &MiningBlockChainClient, pending: PendingTransaction) -> + fn import_own_transaction(&self, chain: &C, pending: PendingTransaction) -> Result { // keep the pending nonces up to date @@ -201,12 +236,12 @@ impl MinerService for TestMinerService { } /// Removes all transactions from the queue and restart mining operation. - fn clear_and_reset(&self, _chain: &MiningBlockChainClient) { + fn clear_and_reset(&self, _chain: &C) { unimplemented!(); } /// Called when blocks are imported to chain, updates transactions queue. - fn chain_new_blocks(&self, _chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) { + fn chain_new_blocks(&self, _chain: &C, _imported: &[H256], _invalid: &[H256], _enacted: &[H256], _retracted: &[H256]) { unimplemented!(); } @@ -216,11 +251,11 @@ impl MinerService for TestMinerService { } /// New chain head event. Restart mining operation. - fn update_sealing(&self, _chain: &MiningBlockChainClient) { + fn update_sealing(&self, _chain: &C) { unimplemented!(); } - fn map_sealing_work(&self, chain: &MiningBlockChainClient, f: F) -> Option where F: FnOnce(&ClosedBlock) -> T { + fn map_sealing_work(&self, chain: &C, f: F) -> Option where F: FnOnce(&ClosedBlock) -> T { let open_block = chain.prepare_open_block(self.author(), *self.gas_range_target.write(), self.extra_data()); Some(f(&open_block.close())) } @@ -229,7 +264,7 @@ impl MinerService for TestMinerService { self.pending_transactions.lock().get(hash).cloned().map(Into::into) } - fn remove_pending_transaction(&self, _chain: &MiningBlockChainClient, hash: &H256) -> Option { + fn remove_pending_transaction(&self, _chain: &C, hash: &H256) -> Option { self.pending_transactions.lock().remove(hash).map(Into::into) } @@ -279,7 +314,7 @@ impl MinerService for TestMinerService { /// Submit `seal` as a valid solution for the header of `pow_hash`. /// Will check the seal, but not actually insert the block into the chain. - fn submit_seal(&self, _chain: &MiningBlockChainClient, _pow_hash: H256, _seal: Vec) -> Result<(), Error> { + fn submit_seal(&self, _chain: &C, _pow_hash: H256, _seal: Vec) -> Result<(), Error> { unimplemented!(); } diff --git a/rpc/src/v1/tests/mocked/eth_pubsub.rs b/rpc/src/v1/tests/mocked/eth_pubsub.rs index 0cf1194327d..1a0186c97e8 100644 --- a/rpc/src/v1/tests/mocked/eth_pubsub.rs +++ b/rpc/src/v1/tests/mocked/eth_pubsub.rs @@ -82,7 +82,7 @@ fn should_subscribe_to_new_heads() { fn should_subscribe_to_logs() { use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; use ethcore::ids::BlockId; - use ethcore::client::BlockChainClient; + use ethcore::client::BlockInfo; // given let el = EventLoop::spawn(); diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs index 9570f442086..b6c1860f5ba 100644 --- a/rpc/src/v1/types/block_number.rs +++ b/rpc/src/v1/types/block_number.rs @@ -91,14 +91,14 @@ impl<'a> Visitor<'a> for BlockNumberVisitor { } } -impl Into for BlockNumber { - fn into(self) -> BlockId { - match self { - BlockNumber::Num(n) => BlockId::Number(n), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => BlockId::Pending, - } +/// Converts `BlockNumber` to `BlockId`, panics on `BlockNumber::Pending` +pub fn block_number_to_id(number: BlockNumber) -> BlockId { + match number { + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + + BlockNumber::Pending => panic!("`BlockNumber::Pending` should be handled manually") } } @@ -122,11 +122,17 @@ mod tests { } #[test] - fn block_number_into() { - assert_eq!(BlockId::Number(100), BlockNumber::Num(100).into()); - assert_eq!(BlockId::Earliest, BlockNumber::Earliest.into()); - assert_eq!(BlockId::Latest, BlockNumber::Latest.into()); - assert_eq!(BlockId::Pending, BlockNumber::Pending.into()); + fn normal_block_number_to_id() { + assert_eq!(block_number_to_id(BlockNumber::Num(100)), BlockId::Number(100)); + assert_eq!(block_number_to_id(BlockNumber::Earliest), BlockId::Earliest); + assert_eq!(block_number_to_id(BlockNumber::Latest), BlockId::Latest); + } + + #[test] + #[should_panic] + fn pending_block_number_to_id() { + // Since this function is not allowed to be called in such way, panic should happen + block_number_to_id(BlockNumber::Pending); } } diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index 82cd5db301b..52217459c1f 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -72,9 +72,15 @@ pub struct Filter { impl Into for Filter { fn into(self) -> EthFilter { + let num_to_id = |num| match num { + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest, + }; + EthFilter { - from_block: self.from_block.map_or_else(|| BlockId::Latest, Into::into), - to_block: self.to_block.map_or_else(|| BlockId::Latest, Into::into), + from_block: self.from_block.map_or_else(|| BlockId::Latest, &num_to_id), + to_block: self.to_block.map_or_else(|| BlockId::Latest, &num_to_id), address: self.address.and_then(|address| match address { VariadicValue::Null => None, VariadicValue::Single(a) => Some(vec![a.into()]), diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index 65d1feeb4c3..7f1e51fad01 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -50,7 +50,7 @@ pub mod pubsub; pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo}; pub use self::bytes::Bytes; pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich}; -pub use self::block_number::BlockNumber; +pub use self::block_number::{BlockNumber, block_number_to_id}; pub use self::call_request::CallRequest; pub use self::confirmations::{ ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, diff --git a/rpc/src/v1/types/trace_filter.rs b/rpc/src/v1/types/trace_filter.rs index abd873c5985..3a64f524889 100644 --- a/rpc/src/v1/types/trace_filter.rs +++ b/rpc/src/v1/types/trace_filter.rs @@ -44,8 +44,17 @@ pub struct TraceFilter { impl Into for TraceFilter { fn into(self) -> client::TraceFilter { - let start = self.from_block.map_or(BlockId::Latest, Into::into); - let end = self.to_block.map_or(BlockId::Latest, Into::into); + let num_to_id = |num| match num { + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => { + warn!("Pending traces are not supported and might be removed in future versions. Falling back to Latest"); + BlockId::Latest + } + }; + let start = self.from_block.map_or(BlockId::Latest, &num_to_id); + let end = self.to_block.map_or(BlockId::Latest, &num_to_id); client::TraceFilter { range: start..end, from_address: self.from_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index 8dfde41227d..f3d116145f8 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use std::collections::{HashMap, HashSet}; use parking_lot::{Mutex, RwLock}; use ethkey::public_to_address; -use ethcore::client::{BlockChainClient, BlockId, ChainNotify}; +use ethcore::client::{BlockId, ChainNotify, CallContract, RegistryInfo}; use ethereum_types::{H256, Address}; use bytes::Bytes; use trusted_client::TrustedClient; diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index d5de48815c6..f069368b044 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -18,8 +18,8 @@ use std::sync::Arc; use std::net::SocketAddr; use std::collections::{BTreeMap, HashSet}; use parking_lot::Mutex; +use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify, CallContract, RegistryInfo}; use ethcore::filter::Filter; -use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; use ethkey::public_to_address; use hash::keccak; use ethereum_types::{H256, Address}; @@ -281,7 +281,7 @@ impl CachedContract { fn start_migration(&mut self, migration_id: H256) { // trust is not needed here, because it is the reaction to the read of the trusted client - if let (Some(client), Some(contract_address)) = (self.client.get_untrusted(), self.contract_address) { + if let (Some(client), Some(contract_address)) = (self.client.get_untrusted(), self.contract_address.as_ref()) { // check if we need to send start migration transaction if !update_last_transaction_block(&*client, &migration_id, &mut self.start_migration_tx) { return; @@ -291,7 +291,7 @@ impl CachedContract { let transaction_data = self.contract.functions().start_migration().input(migration_id); // send transaction - if let Err(error) = client.transact_contract(contract_address, transaction_data) { + if let Err(error) = client.transact_contract(*contract_address, transaction_data) { warn!(target: "secretstore_net", "{}: failed to submit auto-migration start transaction: {}", self.self_key_pair.public(), error); } else { diff --git a/secret_store/src/listener/service_contract.rs b/secret_store/src/listener/service_contract.rs index dd7af393d50..f2d13f192f7 100644 --- a/secret_store/src/listener/service_contract.rs +++ b/secret_store/src/listener/service_contract.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use parking_lot::RwLock; use ethcore::filter::Filter; -use ethcore::client::{Client, BlockChainClient, BlockId}; +use ethcore::client::{Client, BlockChainClient, BlockId, RegistryInfo, CallContract}; use ethkey::{Public, Signature, public_to_address}; use hash::keccak; use ethereum_types::{H256, U256, Address}; diff --git a/secret_store/src/trusted_client.rs b/secret_store/src/trusted_client.rs index 688e4099dc6..8a0f83d44f3 100644 --- a/secret_store/src/trusted_client.rs +++ b/secret_store/src/trusted_client.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::sync::{Arc, Weak}; -use ethcore::client::{Client, BlockChainClient}; +use ethcore::client::{Client, BlockChainClient, ChainInfo}; use ethsync::SyncProvider; #[derive(Clone)] diff --git a/sync/src/chain.rs b/sync/src/chain.rs index c5c291bd03e..d569991a579 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -2232,7 +2232,7 @@ mod tests { use ::SyncConfig; use super::{PeerInfo, PeerAsking}; use ethcore::header::*; - use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; + use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient, ChainInfo, BlockInfo}; use ethcore::miner::MinerService; use transaction::UnverifiedTransaction; diff --git a/sync/src/light_sync/tests/mod.rs b/sync/src/light_sync/tests/mod.rs index 1653d09d370..9fd270838bf 100644 --- a/sync/src/light_sync/tests/mod.rs +++ b/sync/src/light_sync/tests/mod.rs @@ -16,7 +16,7 @@ use tests::helpers::TestNet; -use ethcore::client::{BlockChainClient, BlockId, EachBlockWith}; +use ethcore::client::{BlockInfo, BlockId, EachBlockWith}; mod test_net; diff --git a/sync/src/tests/chain.rs b/sync/src/tests/chain.rs index f9ce17b5b7c..517ff0d99a4 100644 --- a/sync/src/tests/chain.rs +++ b/sync/src/tests/chain.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::sync::Arc; -use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockId, EachBlockWith}; +use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockId, EachBlockWith, ChainInfo, BlockInfo}; use chain::{SyncState}; use super::helpers::*; use SyncConfig; diff --git a/sync/src/tests/consensus.rs b/sync/src/tests/consensus.rs index b6d915bdaa3..33ecbb7f4f6 100644 --- a/sync/src/tests/consensus.rs +++ b/sync/src/tests/consensus.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use hash::keccak; use ethereum_types::{U256, Address}; use io::{IoHandler, IoContext, IoChannel}; -use ethcore::client::{BlockChainClient, Client}; +use ethcore::client::{Client, ChainInfo}; use ethcore::service::ClientIoMessage; use ethcore::spec::Spec; use ethcore::miner::MinerService; diff --git a/util/journaldb/src/lib.rs b/util/journaldb/src/lib.rs index 949c0609f24..23b56fa03f9 100644 --- a/util/journaldb/src/lib.rs +++ b/util/journaldb/src/lib.rs @@ -52,7 +52,7 @@ pub mod overlaydb; /// Export the `JournalDB` trait. pub use self::traits::JournalDB; -/// A journal database algorithm. +/// Journal database operating strategy. #[derive(Debug, PartialEq, Clone, Copy)] pub enum Algorithm { /// Keep all keys forever. From 61d1f802e792970ab939aee90fa3f16d2383f3e9 Mon Sep 17 00:00:00 2001 From: Rando <5chdn@users.noreply.github.com> Date: Sat, 3 Mar 2018 22:02:33 +0100 Subject: [PATCH 05/11] Bump master to 1.11.0 (#8021) * Bump master to 1.11.0 * Bump price-info * Bump mac installer version * Fix gitlab builds --- Cargo.lock | 188 ++++++++++++++++----------------- Cargo.toml | 2 +- dapps/Cargo.toml | 2 +- dapps/ui/Cargo.toml | 2 +- devtools/Cargo.toml | 2 +- ethash/Cargo.toml | 2 +- ethcore/Cargo.toml | 2 +- ethcore/light/Cargo.toml | 2 +- ethcore/node_filter/Cargo.toml | 2 +- evmjit/Cargo.toml | 2 +- hash-fetch/Cargo.toml | 2 +- hw/Cargo.toml | 2 +- ipfs/Cargo.toml | 2 +- logger/Cargo.toml | 2 +- mac/Parity.pkgproj | 2 +- mac/Parity/Info.plist | 2 +- miner/Cargo.toml | 2 +- nsis/installer.nsi | 2 +- price-info/Cargo.toml | 2 +- rpc/Cargo.toml | 2 +- scripts/gitlab-build.sh | 2 +- stratum/Cargo.toml | 2 +- sync/Cargo.toml | 2 +- transaction-pool/Cargo.toml | 2 +- updater/Cargo.toml | 2 +- util/io/Cargo.toml | 2 +- util/network/Cargo.toml | 2 +- util/version/Cargo.toml | 2 +- 28 files changed, 121 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08915933c18..c4a53b0cacc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -416,7 +416,7 @@ dependencies = [ [[package]] name = "ethash" -version = "1.9.0" +version = "1.11.0" dependencies = [ "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -441,7 +441,7 @@ dependencies = [ [[package]] name = "ethcore" -version = "1.9.0" +version = "1.11.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "bn 0.4.4 (git+https://github.com/paritytech/bn)", @@ -451,20 +451,20 @@ dependencies = [ "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-derive 5.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.9.0", + "ethash 1.11.0", "ethcore-bloom-journal 0.1.0", "ethcore-bytes 0.1.0", - "ethcore-io 1.9.0", - "ethcore-logger 1.9.0", - "ethcore-miner 1.9.0", - "ethcore-stratum 1.9.0", + "ethcore-io 1.11.0", + "ethcore-logger 1.11.0", + "ethcore-miner 1.11.0", + "ethcore-stratum 1.11.0", "ethcore-transaction 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "ethkey 0.3.0", "ethstore 0.2.0", "evm 0.1.0", - "hardware-wallet 1.9.0", + "hardware-wallet 1.11.0", "hashdb 0.1.1", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -485,7 +485,7 @@ dependencies = [ "parity-machine 0.1.0", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.1.0", - "price-info 1.7.0", + "price-info 1.11.0", "rand 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.2.1", @@ -522,11 +522,11 @@ version = "0.1.0" [[package]] name = "ethcore-devtools" -version = "1.9.0" +version = "1.11.0" [[package]] name = "ethcore-io" -version = "1.9.0" +version = "1.11.0" dependencies = [ "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -537,13 +537,13 @@ dependencies = [ [[package]] name = "ethcore-light" -version = "1.9.0" +version = "1.11.0" dependencies = [ "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.9.0", + "ethcore 1.11.0", "ethcore-bytes 0.1.0", - "ethcore-io 1.9.0", - "ethcore-network 1.9.0", + "ethcore-io 1.11.0", + "ethcore-network 1.11.0", "ethcore-transaction 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -574,7 +574,7 @@ dependencies = [ [[package]] name = "ethcore-logger" -version = "1.9.0" +version = "1.11.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -596,13 +596,13 @@ dependencies = [ [[package]] name = "ethcore-miner" -version = "1.9.0" +version = "1.11.0" dependencies = [ "common-types 0.1.0", "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-derive 5.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.9.0", + "ethash 1.11.0", "ethcore-transaction 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", @@ -620,14 +620,14 @@ dependencies = [ [[package]] name = "ethcore-network" -version = "1.9.0" +version = "1.11.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bytes 0.1.0", - "ethcore-io 1.9.0", - "ethcore-logger 1.9.0", + "ethcore-io 1.11.0", + "ethcore-logger 1.11.0", "ethcrypto 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", @@ -661,13 +661,13 @@ dependencies = [ "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-derive 5.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.9.0", + "ethcore 1.11.0", "ethcore-bytes 0.1.0", - "ethcore-logger 1.9.0", + "ethcore-logger 1.11.0", "ethcrypto 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", - "ethsync 1.9.0", + "ethsync 1.11.0", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -692,10 +692,10 @@ dependencies = [ [[package]] name = "ethcore-stratum" -version = "1.9.0" +version = "1.11.0" dependencies = [ "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-logger 1.9.0", + "ethcore-logger 1.11.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.10)", "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.10)", @@ -841,14 +841,14 @@ dependencies = [ [[package]] name = "ethsync" -version = "1.9.0" +version = "1.11.0" dependencies = [ "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.9.0", + "ethcore 1.11.0", "ethcore-bytes 0.1.0", - "ethcore-io 1.9.0", - "ethcore-light 1.9.0", - "ethcore-network 1.9.0", + "ethcore-io 1.11.0", + "ethcore-light 1.11.0", + "ethcore-network 1.11.0", "ethcore-transaction 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", @@ -875,7 +875,7 @@ version = "0.1.0" dependencies = [ "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "evmjit 1.9.0", + "evmjit 1.11.0", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "keccak-hash 0.1.0", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -891,7 +891,7 @@ name = "evmbin" version = "0.1.0" dependencies = [ "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.9.0", + "ethcore 1.11.0", "ethcore-bytes 0.1.0", "ethcore-transaction 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -907,7 +907,7 @@ dependencies = [ [[package]] name = "evmjit" -version = "1.9.0" +version = "1.11.0" dependencies = [ "tiny-keccak 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1034,7 +1034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hardware-wallet" -version = "1.9.0" +version = "1.11.0" dependencies = [ "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", @@ -1237,7 +1237,7 @@ name = "journaldb" version = "0.1.0" dependencies = [ "ethcore-bytes 0.1.0", - "ethcore-logger 1.9.0", + "ethcore-logger 1.11.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.1.1", "heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1704,15 +1704,15 @@ dependencies = [ [[package]] name = "node-filter" -version = "1.9.0" +version = "1.11.0" dependencies = [ "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-derive 5.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.9.0", + "ethcore 1.11.0", "ethcore-bytes 0.1.0", - "ethcore-io 1.9.0", - "ethcore-network 1.9.0", + "ethcore-io 1.11.0", + "ethcore-network 1.11.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kvdb-memorydb 0.1.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1898,7 +1898,7 @@ dependencies = [ [[package]] name = "parity" -version = "1.10.0" +version = "1.11.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1908,20 +1908,20 @@ dependencies = [ "dir 0.1.0", "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.9.0", + "ethcore 1.11.0", "ethcore-bytes 0.1.0", - "ethcore-io 1.9.0", - "ethcore-light 1.9.0", - "ethcore-logger 1.9.0", + "ethcore-io 1.11.0", + "ethcore-light 1.11.0", + "ethcore-logger 1.11.0", "ethcore-migrations 0.1.0", - "ethcore-miner 1.9.0", - "ethcore-network 1.9.0", + "ethcore-miner 1.11.0", + "ethcore-network 1.11.0", "ethcore-secretstore 1.0.0", - "ethcore-stratum 1.9.0", + "ethcore-stratum 1.11.0", "ethcore-transaction 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", - "ethsync 1.9.0", + "ethsync 1.11.0", "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1934,20 +1934,20 @@ dependencies = [ "kvdb-rocksdb 0.1.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "migration 0.1.0", - "node-filter 1.9.0", + "node-filter 1.11.0", "node-health 0.1.0", "num_cpus 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "panic_hook 0.1.0", - "parity-dapps 1.9.0", - "parity-hash-fetch 1.9.0", - "parity-ipfs-api 1.9.0", + "parity-dapps 1.11.0", + "parity-hash-fetch 1.11.0", + "parity-ipfs-api 1.11.0", "parity-local-store 0.1.0", "parity-reactor 0.1.0", - "parity-rpc 1.9.0", + "parity-rpc 1.11.0", "parity-rpc-client 1.4.0", - "parity-updater 1.9.0", - "parity-version 1.10.0", + "parity-updater 1.11.0", + "parity-version 1.11.0", "parity-whisper 0.1.0", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "path 0.1.0", @@ -1973,12 +1973,12 @@ dependencies = [ [[package]] name = "parity-dapps" -version = "1.9.0" +version = "1.11.0" dependencies = [ "base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bytes 0.1.0", - "ethcore-devtools 1.9.0", + "ethcore-devtools 1.11.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "fetch 0.1.0", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1992,10 +1992,10 @@ dependencies = [ "mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", "node-health 0.1.0", "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-hash-fetch 1.9.0", + "parity-hash-fetch 1.11.0", "parity-reactor 0.1.0", - "parity-ui 1.9.0", - "parity-version 1.10.0", + "parity-ui 1.11.0", + "parity-version 1.11.0", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2036,7 +2036,7 @@ dependencies = [ [[package]] name = "parity-hash-fetch" -version = "1.9.0" +version = "1.11.0" dependencies = [ "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2057,10 +2057,10 @@ dependencies = [ [[package]] name = "parity-ipfs-api" -version = "1.9.0" +version = "1.11.0" dependencies = [ "cid 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.9.0", + "ethcore 1.11.0", "ethcore-bytes 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.10)", @@ -2074,8 +2074,8 @@ dependencies = [ name = "parity-local-store" version = "0.1.0" dependencies = [ - "ethcore 1.9.0", - "ethcore-io 1.9.0", + "ethcore 1.11.0", + "ethcore-io 1.11.0", "ethcore-transaction 0.1.0", "ethkey 0.3.0", "kvdb 0.1.0", @@ -2104,30 +2104,30 @@ dependencies = [ [[package]] name = "parity-rpc" -version = "1.9.0" +version = "1.11.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "cid 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.9.0", - "ethcore 1.9.0", + "ethash 1.11.0", + "ethcore 1.11.0", "ethcore-bytes 0.1.0", - "ethcore-devtools 1.9.0", - "ethcore-io 1.9.0", - "ethcore-light 1.9.0", - "ethcore-logger 1.9.0", - "ethcore-miner 1.9.0", - "ethcore-network 1.9.0", + "ethcore-devtools 1.11.0", + "ethcore-io 1.11.0", + "ethcore-light 1.11.0", + "ethcore-logger 1.11.0", + "ethcore-miner 1.11.0", + "ethcore-network 1.11.0", "ethcore-transaction 0.1.0", "ethcrypto 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "ethkey 0.3.0", "ethstore 0.2.0", - "ethsync 1.9.0", + "ethsync 1.11.0", "fetch 0.1.0", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "hardware-wallet 1.9.0", + "hardware-wallet 1.11.0", "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.10)", "jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.10)", @@ -2143,8 +2143,8 @@ dependencies = [ "node-health 0.1.0", "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-reactor 0.1.0", - "parity-updater 1.9.0", - "parity-version 1.10.0", + "parity-updater 1.11.0", + "parity-version 1.11.0", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.1.0", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2175,7 +2175,7 @@ dependencies = [ "keccak-hash 0.1.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rpc 1.9.0", + "parity-rpc 1.11.0", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2201,7 +2201,7 @@ dependencies = [ [[package]] name = "parity-ui" -version = "1.9.0" +version = "1.11.0" dependencies = [ "parity-ui-dev 1.9.0 (git+https://github.com/parity-js/shell.git?rev=43a46b83821604da9e6a928599d23647a047af19)", "parity-ui-old-dev 1.9.0 (git+https://github.com/parity-js/dapp-wallet.git?rev=1a58bf4836c84e1632e27ef607b5a388abd2bf2d)", @@ -2244,18 +2244,18 @@ dependencies = [ [[package]] name = "parity-updater" -version = "1.9.0" +version = "1.11.0" dependencies = [ "ethabi 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-contract 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-derive 5.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.9.0", + "ethcore 1.11.0", "ethcore-bytes 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethsync 1.9.0", + "ethsync 1.11.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-hash-fetch 1.9.0", - "parity-version 1.10.0", + "parity-hash-fetch 1.11.0", + "parity-version 1.11.0", "parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "path 0.1.0", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2264,7 +2264,7 @@ dependencies = [ [[package]] name = "parity-version" -version = "1.10.0" +version = "1.11.0" dependencies = [ "ethcore-bytes 0.1.0", "rlp 0.2.1", @@ -2290,7 +2290,7 @@ version = "0.1.0" dependencies = [ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-network 1.9.0", + "ethcore-network 1.11.0", "ethcrypto 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", @@ -2354,7 +2354,7 @@ version = "0.1.0" dependencies = [ "elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-bytes 0.1.0", - "ethcore-logger 1.9.0", + "ethcore-logger 1.11.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "hashdb 0.1.1", "keccak-hash 0.1.0", @@ -2434,7 +2434,7 @@ dependencies = [ [[package]] name = "price-info" -version = "1.7.0" +version = "1.11.0" dependencies = [ "fetch 0.1.0", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2503,7 +2503,7 @@ name = "pwasm-run-test" version = "0.1.0" dependencies = [ "clap 2.29.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-logger 1.9.0", + "ethcore-logger 1.11.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethjson 0.1.0", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2725,7 +2725,7 @@ version = "1.4.0" dependencies = [ "bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rpc 1.9.0", + "parity-rpc 1.11.0", "parity-rpc-client 1.4.0", "rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3255,7 +3255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "transaction-pool" -version = "1.9.0" +version = "1.11.0" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3480,7 +3480,7 @@ name = "wasm" version = "0.1.0" dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-logger 1.9.0", + "ethcore-logger 1.11.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index e89a09a597b..4876e314b30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ description = "Parity Ethereum client" name = "parity" # NOTE Make sure to update util/version/Cargo.toml as well -version = "1.10.0" +version = "1.11.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index bbe6954277d..610d320c710 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Parity Dapps crate" name = "parity-dapps" -version = "1.9.0" +version = "1.11.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/dapps/ui/Cargo.toml b/dapps/ui/Cargo.toml index cb7886b3219..bf6b50afffe 100644 --- a/dapps/ui/Cargo.toml +++ b/dapps/ui/Cargo.toml @@ -3,7 +3,7 @@ description = "Ethcore Parity UI" homepage = "http://parity.io" license = "GPL-3.0" name = "parity-ui" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [build-dependencies] diff --git a/devtools/Cargo.toml b/devtools/Cargo.toml index a80f68ef7c9..d5e275a3adb 100644 --- a/devtools/Cargo.toml +++ b/devtools/Cargo.toml @@ -3,5 +3,5 @@ description = "Ethcore development/test/build tools" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-devtools" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] diff --git a/ethash/Cargo.toml b/ethash/Cargo.toml index ba031cfa935..d120c9f89a7 100644 --- a/ethash/Cargo.toml +++ b/ethash/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ethash" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [lib] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index c64a801872c..48d0197928b 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -3,7 +3,7 @@ description = "Ethcore library" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [dependencies] diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 6778a4834fc..bc04a9604a4 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -3,7 +3,7 @@ description = "Parity Light Client Implementation" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-light" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [dependencies] diff --git a/ethcore/node_filter/Cargo.toml b/ethcore/node_filter/Cargo.toml index baad75760cd..d4621e54867 100644 --- a/ethcore/node_filter/Cargo.toml +++ b/ethcore/node_filter/Cargo.toml @@ -3,7 +3,7 @@ description = "Parity smart network connections" homepage = "http://parity.io" license = "GPL-3.0" name = "node-filter" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [dependencies] diff --git a/evmjit/Cargo.toml b/evmjit/Cargo.toml index 8020d89c050..c7bf1f8ba94 100644 --- a/evmjit/Cargo.toml +++ b/evmjit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "evmjit" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [lib] diff --git a/hash-fetch/Cargo.toml b/hash-fetch/Cargo.toml index f20f76fcc47..85650225a02 100644 --- a/hash-fetch/Cargo.toml +++ b/hash-fetch/Cargo.toml @@ -3,7 +3,7 @@ description = "Fetching hash-addressed content." homepage = "http://parity.io" license = "GPL-3.0" name = "parity-hash-fetch" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [dependencies] diff --git a/hw/Cargo.toml b/hw/Cargo.toml index 9e248e7ae90..c799c79c382 100644 --- a/hw/Cargo.toml +++ b/hw/Cargo.toml @@ -3,7 +3,7 @@ description = "Hardware wallet support." homepage = "http://parity.io" license = "GPL-3.0" name = "hardware-wallet" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [dependencies] diff --git a/ipfs/Cargo.toml b/ipfs/Cargo.toml index 353b47df18a..4c85c8770cb 100644 --- a/ipfs/Cargo.toml +++ b/ipfs/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Parity IPFS-compatible API" name = "parity-ipfs-api" -version = "1.9.0" +version = "1.11.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/logger/Cargo.toml b/logger/Cargo.toml index 65eca86af53..0e1b03f9411 100644 --- a/logger/Cargo.toml +++ b/logger/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Log implementation for Parity" name = "ethcore-logger" -version = "1.9.0" +version = "1.11.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/mac/Parity.pkgproj b/mac/Parity.pkgproj index 6c411d2289d..e5229f927a3 100755 --- a/mac/Parity.pkgproj +++ b/mac/Parity.pkgproj @@ -462,7 +462,7 @@ OVERWRITE_PERMISSIONS VERSION - 1.10.0 + 1.11.0 UUID 2DCD5B81-7BAF-4DA1-9251-6274B089FD36 diff --git a/mac/Parity/Info.plist b/mac/Parity/Info.plist index 86c4ddbcdf4..2cc7e67b83e 100644 --- a/mac/Parity/Info.plist +++ b/mac/Parity/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.10 + 1.11 CFBundleVersion 1 LSApplicationCategoryType diff --git a/miner/Cargo.toml b/miner/Cargo.toml index 0fc9052ea1e..b9f44cd6e02 100644 --- a/miner/Cargo.toml +++ b/miner/Cargo.toml @@ -3,7 +3,7 @@ description = "Parity Miner interface." name = "ethcore-miner" homepage = "http://parity.io" license = "GPL-3.0" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [dependencies] diff --git a/nsis/installer.nsi b/nsis/installer.nsi index 03daf3bc224..01155723401 100644 --- a/nsis/installer.nsi +++ b/nsis/installer.nsi @@ -9,7 +9,7 @@ !define COMPANYNAME "Parity Technologies" !define DESCRIPTION "Fast, light, robust Ethereum implementation" !define VERSIONMAJOR 1 -!define VERSIONMINOR 10 +!define VERSIONMINOR 11 !define VERSIONBUILD 0 !define ARGS "" !define FIRST_START_ARGS "--mode=passive ui" diff --git a/price-info/Cargo.toml b/price-info/Cargo.toml index 21b4fa3fd28..459bccc1dc4 100644 --- a/price-info/Cargo.toml +++ b/price-info/Cargo.toml @@ -3,7 +3,7 @@ description = "Fetch current ETH price" homepage = "http://parity.io" license = "GPL-3.0" name = "price-info" -version = "1.7.0" +version = "1.11.0" authors = ["Parity Technologies "] [dependencies] diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 58970b1a724..c0158658b63 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Parity JSON-RPC servers." name = "parity-rpc" -version = "1.9.0" +version = "1.11.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/scripts/gitlab-build.sh b/scripts/gitlab-build.sh index a67cca99a5c..a2a084adc9b 100755 --- a/scripts/gitlab-build.sh +++ b/scripts/gitlab-build.sh @@ -313,7 +313,7 @@ case $BUILD_PLATFORM in snapcraft clean echo "Prepare snapcraft.yaml for build on Gitlab CI in Docker image" sed -i 's/git/'"$VER"'/g' snap/snapcraft.yaml - if [[ "$CI_BUILD_REF_NAME" = "beta" || "$VER" == *1.9* ]]; + if [[ "$CI_BUILD_REF_NAME" = "beta" || "$VER" == *1.10* ]]; then sed -i -e 's/grade: devel/grade: stable/' snap/snapcraft.yaml; fi diff --git a/stratum/Cargo.toml b/stratum/Cargo.toml index 8f2b05dc2aa..e166d8a55ee 100644 --- a/stratum/Cargo.toml +++ b/stratum/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Ethcore stratum lib" name = "ethcore-stratum" -version = "1.9.0" +version = "1.11.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/sync/Cargo.toml b/sync/Cargo.toml index 4b8701c9087..3db8ec7d8dd 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Ethcore blockchain sync" name = "ethsync" -version = "1.9.0" +version = "1.11.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/transaction-pool/Cargo.toml b/transaction-pool/Cargo.toml index 6652001f5d4..49bf2db64db 100644 --- a/transaction-pool/Cargo.toml +++ b/transaction-pool/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Generic transaction pool." name = "transaction-pool" -version = "1.9.0" +version = "1.11.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/updater/Cargo.toml b/updater/Cargo.toml index a821ef6729f..0621fbc676a 100644 --- a/updater/Cargo.toml +++ b/updater/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Parity Updater Service." name = "parity-updater" -version = "1.9.0" +version = "1.11.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/util/io/Cargo.toml b/util/io/Cargo.toml index 7b74c7ba353..dca0426d509 100644 --- a/util/io/Cargo.toml +++ b/util/io/Cargo.toml @@ -3,7 +3,7 @@ description = "Ethcore IO library" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-io" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [dependencies] diff --git a/util/network/Cargo.toml b/util/network/Cargo.toml index 4271b591411..c0ced05847d 100644 --- a/util/network/Cargo.toml +++ b/util/network/Cargo.toml @@ -3,7 +3,7 @@ description = "Ethcore network library" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-network" -version = "1.9.0" +version = "1.11.0" authors = ["Parity Technologies "] [dependencies] diff --git a/util/version/Cargo.toml b/util/version/Cargo.toml index 37c3d627bfc..64054827262 100644 --- a/util/version/Cargo.toml +++ b/util/version/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "parity-version" # NOTE: this value is used for Parity version string (via env CARGO_PKG_VERSION) -version = "1.10.0" +version = "1.11.0" authors = ["Parity Technologies "] build = "build.rs" From 5c13a8945e3cf520cf1c5cc02b127f48420959dc Mon Sep 17 00:00:00 2001 From: Rando <5chdn@users.noreply.github.com> Date: Sun, 4 Mar 2018 16:18:51 +0100 Subject: [PATCH 06/11] Add MCIP-6 Byzyantium transition to Musicoin spec (#7841) * Add test chain spec for musicoin byzantium testnet * Add MCIP-6 Byzyantium transition to Musicoin spec * Update mcip6_byz.json * ethcore: update musicoin byzantium block number * ethcore: update musicoin byzantium block number * ethcore: update musicoin bootnodes * Update musicoin.json * Update musicoin.json * More bootnodes. --- ethcore/res/ethereum/mcip6_byz.json | 160 ++++++++++++++++++++++++++++ ethcore/res/ethereum/musicoin.json | 37 +++---- 2 files changed, 177 insertions(+), 20 deletions(-) create mode 100644 ethcore/res/ethereum/mcip6_byz.json diff --git a/ethcore/res/ethereum/mcip6_byz.json b/ethcore/res/ethereum/mcip6_byz.json new file mode 100644 index 00000000000..da40d6882c3 --- /dev/null +++ b/ethcore/res/ethereum/mcip6_byz.json @@ -0,0 +1,160 @@ +{ + "name":"Musicoin Byzantium Test", + "dataDir":"mcip6test", + "engine":{ + "Ethash":{ + "params":{ + "minimumDifficulty":"0x020000", + "difficultyBoundDivisor":"0x0800", + "durationLimit":"0x0d", + "homesteadTransition":"0x17", + "eip100bTransition":"0x2a", + "eip150Transition":"0x2a", + "eip160Transition":"0x7fffffffffffff", + "eip161abcTransition":"0x7fffffffffffff", + "eip161dTransition":"0x7fffffffffffff", + "eip649Transition":"0x2a", + "blockReward":"0x1105a0185b50a80000", + "mcip3Transition":"0x17", + "mcip3MinerReward":"0xd8d726b7177a80000", + "mcip3UbiReward":"0x2b5e3af16b1880000", + "mcip3UbiContract":"0x00efdd5883ec628983e9063c7d969fe268bbf310", + "mcip3DevReward":"0xc249fdd327780000", + "mcip3DevContract":"0x00756cf8159095948496617f5fb17ed95059f536" + } + } + }, + "params":{ + "gasLimitBoundDivisor":"0x0400", + "registrar":"0x5C271c4C9A67E7D73b7b3669d47504741354f21D", + "accountStartNonce":"0x00", + "maximumExtraDataSize":"0x20", + "minGasLimit":"0x1388", + "networkID":"0x76740c", + "forkBlock":"0x2b", + "forkCanonHash":"0x23c3171e864a5d513a3ef85e4cf86dac4cc36b89e5b8e63bf0ebcca68b9e43c9", + "eip86Transition":"0x7fffffffffffff", + "eip98Transition":"0x7fffffffffffff", + "eip140Transition":"0x2a", + "eip155Transition":"0x2a", + "eip211Transition":"0x2a", + "eip214Transition":"0x2a", + "eip658Transition":"0x2a", + "maxCodeSize":"0x6000" + }, + "genesis":{ + "seal":{ + "ethereum":{ + "nonce":"0x000000000000002a", + "mixHash":"0x00000000000000000000000000000000000000647572616c65787365646c6578" + } + }, + "difficulty":"0x3d0900", + "author":"0x0000000000000000000000000000000000000000", + "timestamp":"0x00", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData":"", + "gasLimit":"0x7a1200" + }, + "nodes":[ + "enode://5ddc110733f6d34101973cdef3f9b43484159acf6f816d3b1ee92bc3c98ea453e857bb1207edf0ec0242008ab3a0f9f05eeaee99d47bd414c08a5bdf4847de13@176.9.3.148:30303", + "enode://38f074f4db8e64dfbaf87984bf290eef67772a901a7113d1b62f36216be152b8450c393d6fc562a5e38f04f99bc8f439a99010a230b1d92dc1df43bf0bd00615@176.9.3.148:30403" + ], + "accounts":{ + "0000000000000000000000000000000000000001":{ + "balance":"1", + "builtin":{ + "name":"ecrecover", + "pricing":{ + "linear":{ + "base":3000, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000002":{ + "balance":"1", + "builtin":{ + "name":"sha256", + "pricing":{ + "linear":{ + "base":60, + "word":12 + } + } + } + }, + "0000000000000000000000000000000000000003":{ + "balance":"1", + "builtin":{ + "name":"ripemd160", + "pricing":{ + "linear":{ + "base":600, + "word":120 + } + } + } + }, + "0000000000000000000000000000000000000004":{ + "balance":"1", + "builtin":{ + "name":"identity", + "pricing":{ + "linear":{ + "base":15, + "word":3 + } + } + } + }, + "0000000000000000000000000000000000000005":{ + "builtin":{ + "name":"modexp", + "activate_at":"0x2a", + "pricing":{ + "modexp":{ + "divisor":20 + } + } + } + }, + "0000000000000000000000000000000000000006":{ + "builtin":{ + "name":"alt_bn128_add", + "activate_at":"0x2a", + "pricing":{ + "linear":{ + "base":500, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000007":{ + "builtin":{ + "name":"alt_bn128_mul", + "activate_at":"0x2a", + "pricing":{ + "linear":{ + "base":40000, + "word":0 + } + } + } + }, + "0000000000000000000000000000000000000008":{ + "builtin":{ + "name":"alt_bn128_pairing", + "activate_at":"0x2a", + "pricing":{ + "alt_bn128_pairing":{ + "base":100000, + "pair":80000 + } + } + } + } + } +} diff --git a/ethcore/res/ethereum/musicoin.json b/ethcore/res/ethereum/musicoin.json index cf4d4ffba4d..2dcdb6d8f6c 100644 --- a/ethcore/res/ethereum/musicoin.json +++ b/ethcore/res/ethereum/musicoin.json @@ -8,12 +8,12 @@ "difficultyBoundDivisor":"0x0800", "durationLimit":"0x0d", "homesteadTransition":"0x118c30", - "eip100bTransition":"0x7fffffffffffff", - "eip150Transition":"0x7fffffffffffff", + "eip100bTransition":"0x21e88e", + "eip150Transition":"0x21e88e", "eip160Transition":"0x7fffffffffffff", "eip161abcTransition":"0x7fffffffffffff", "eip161dTransition":"0x7fffffffffffff", - "eip649Transition":"0x7fffffffffffff", + "eip649Transition":"0x21e88e", "blockReward":"0x1105a0185b50a80000", "mcip3Transition":"0x124f81", "mcip3MinerReward":"0xd8d726b7177a80000", @@ -31,15 +31,15 @@ "maximumExtraDataSize":"0x20", "minGasLimit":"0x1388", "networkID":"0x76740f", - "forkBlock":"0x5b6", - "forkCanonHash":"0xa5e88ad9e34d113e264e307bc27e8471452c8fc13780324bb3abb96fd0558343", + "forkBlock":"0x1d8015", + "forkCanonHash":"0x380602acf82b629a0be6b5adb2b4a801e960a07dc8261bf196d21befdbb8f2f9", "eip86Transition":"0x7fffffffffffff", "eip98Transition":"0x7fffffffffffff", - "eip140Transition":"0x7fffffffffffff", - "eip155Transition":"0x7fffffffffffff", - "eip211Transition":"0x7fffffffffffff", - "eip214Transition":"0x7fffffffffffff", - "eip658Transition":"0x7fffffffffffff", + "eip140Transition":"0x21e88e", + "eip155Transition":"0x21e88e", + "eip211Transition":"0x21e88e", + "eip214Transition":"0x21e88e", + "eip658Transition":"0x21e88e", "maxCodeSize":"0x6000" }, "genesis":{ @@ -57,12 +57,9 @@ "gasLimit":"0x7a1200" }, "nodes":[ - "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", - "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", - "enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303", - "enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303", - "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", - "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303", + "enode://09fcd36d553044c8b499b9b9e13a228ffd99572c513f77073d41f009717c464cd4399c0e665d6aff1590324254ee4e698b2b2533b1998dd04d896b9d6aff7895@35.185.67.35:30303", + "enode://89e51a34770a0badf8ea18c4c4d2c361cde707abd60031d99b1ab3010363e1898230a516ddb37d974af8d8db1b322779d7fe0caae0617bed4924d1b4968cf92b@35.231.48.142:30303", + "enode://b58c0c71f08864c0cf7fa9dea2c4cbefae5ae7a36cc30d286603b24982d25f3ccc056b589119324c51768fc2054b8c529ecf682e06e1e9980170b93ff194ed7a@132.148.132.9:30303", "enode://d302f52c8789ad87ee528f1431a67f1aa646c9bec17babb4665dfb3d61de5b9119a70aa77b2147a5f28854092ba09769323c1c552a6ac6f6a34cbcf767e2d2fe@158.69.248.48:30303", "enode://c72564bce8331ae298fb8ece113a456e3927d7e5989c2be3e445678b3600579f722410ef9bbfe339335d676af77343cb21b5b1703b7bebc32be85fce937a2220@191.252.185.71:30303", "enode://e3ae4d25ee64791ff98bf17c37acf90933359f2505c00f65c84f6863231a32a94153cadb0a462e428f18f35ded6bd91cd91033d26576a28558c22678be9cfaee@5.63.158.137:35555" @@ -119,7 +116,7 @@ "0000000000000000000000000000000000000005":{ "builtin":{ "name":"modexp", - "activate_at":"0x7fffffffffffff", + "activate_at":"0x21e88e", "pricing":{ "modexp":{ "divisor":20 @@ -130,7 +127,7 @@ "0000000000000000000000000000000000000006":{ "builtin":{ "name":"alt_bn128_add", - "activate_at":"0x7fffffffffffff", + "activate_at":"0x21e88e", "pricing":{ "linear":{ "base":500, @@ -142,7 +139,7 @@ "0000000000000000000000000000000000000007":{ "builtin":{ "name":"alt_bn128_mul", - "activate_at":"0x7fffffffffffff", + "activate_at":"0x21e88e", "pricing":{ "linear":{ "base":40000, @@ -154,7 +151,7 @@ "0000000000000000000000000000000000000008":{ "builtin":{ "name":"alt_bn128_pairing", - "activate_at":"0x7fffffffffffff", + "activate_at":"0x21e88e", "pricing":{ "alt_bn128_pairing":{ "base":100000, From 7f01b685ba5041bf7bd6c36550bf05702c795cd3 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Sun, 4 Mar 2018 19:31:26 +0100 Subject: [PATCH 07/11] prelude to the block module cleanup (#8025) * prelude to block cleanup * fixed tests --- ethcore/src/block.rs | 89 +++++++++++++----------------------- ethcore/src/miner/miner.rs | 2 +- ethcore/src/tests/helpers.rs | 1 - ethcore/src/tests/trace.rs | 3 -- 4 files changed, 32 insertions(+), 63 deletions(-) diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 9e015fae03c..8f6e0194258 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -22,7 +22,7 @@ use std::collections::HashSet; use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP}; use triehash::ordered_trie_root; -use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, DecoderError}; +use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, DecoderError, encode_list}; use ethereum_types::{H256, U256, Address, Bloom}; use bytes::Bytes; use unexpected::{Mismatch, OutOfBounds}; @@ -67,7 +67,6 @@ impl Block { } } - impl Decodable for Block { fn decode(rlp: &UntrustedRlp) -> Result { if rlp.as_raw().len() != rlp.payload_info()?.total() { @@ -284,38 +283,12 @@ impl<'x> OpenBlock<'x> { Ok(r) } - /// Alter the author for the block. - pub fn set_author(&mut self, author: Address) { self.block.header.set_author(author); } - /// Alter the timestamp of the block. pub fn set_timestamp(&mut self, timestamp: u64) { self.block.header.set_timestamp(timestamp); } - /// Alter the difficulty for the block. - pub fn set_difficulty(&mut self, a: U256) { self.block.header.set_difficulty(a); } - - /// Alter the gas limit for the block. - pub fn set_gas_limit(&mut self, a: U256) { self.block.header.set_gas_limit(a); } - - /// Alter the gas limit for the block. - pub fn set_gas_used(&mut self, a: U256) { self.block.header.set_gas_used(a); } - - /// Alter the uncles hash the block. - pub fn set_uncles_hash(&mut self, h: H256) { self.block.header.set_uncles_hash(h); } - - /// Alter transactions root for the block. - pub fn set_transactions_root(&mut self, h: H256) { self.block.header.set_transactions_root(h); } - - /// Alter the receipts root for the block. - pub fn set_receipts_root(&mut self, h: H256) { self.block.header.set_receipts_root(h); } - - /// Alter the extra_data for the block. - pub fn set_extra_data(&mut self, extra_data: Bytes) -> Result<(), BlockError> { - if extra_data.len() > self.engine.maximum_extra_data_size() { - Err(BlockError::ExtraDataOutOfBounds(OutOfBounds{min: None, max: Some(self.engine.maximum_extra_data_size()), found: extra_data.len()})) - } else { - self.block.header.set_extra_data(extra_data); - Ok(()) - } + /// Removes block gas limit. + pub fn remove_gas_limit(&mut self) { + self.block.header.set_gas_limit(U256::max_value()); } /// Add an uncle to the block, if possible. @@ -347,24 +320,19 @@ impl<'x> OpenBlock<'x> { /// If valid, it will be executed, and archived together with the receipt. pub fn push_transaction(&mut self, t: SignedTransaction, h: Option) -> Result<&Receipt, Error> { if self.block.transactions_set.contains(&t.hash()) { - return Err(From::from(TransactionError::AlreadyImported)); + return Err(TransactionError::AlreadyImported.into()); } let env_info = self.env_info(); -// info!("env_info says gas_used={}", env_info.gas_used); - match self.block.state.apply(&env_info, self.engine.machine(), &t, self.block.traces.is_enabled()) { - Ok(outcome) => { - self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); - self.block.transactions.push(t.into()); - let t = outcome.trace; - if let Tracing::Enabled(ref mut traces) = self.block.traces { - traces.push(t.into()); - } - self.block.receipts.push(outcome.receipt); - Ok(self.block.receipts.last().expect("receipt just pushed; qed")) - } - Err(x) => Err(From::from(x)) + let outcome = self.block.state.apply(&env_info, self.engine.machine(), &t, self.block.traces.is_enabled())?; + + self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); + self.block.transactions.push(t.into()); + if let Tracing::Enabled(ref mut traces) = self.block.traces { + traces.push(outcome.trace.into()); } + self.block.receipts.push(outcome.receipt); + Ok(self.block.receipts.last().expect("receipt just pushed; qed")) } /// Push transactions onto the block. @@ -373,14 +341,19 @@ impl<'x> OpenBlock<'x> { } /// Populate self from a header. - pub fn populate_from(&mut self, header: &Header) { - self.set_difficulty(*header.difficulty()); - self.set_gas_limit(*header.gas_limit()); - self.set_timestamp(header.timestamp()); - self.set_author(header.author().clone()); - self.set_extra_data(header.extra_data().clone()).unwrap_or_else(|e| warn!("Couldn't set extradata: {}. Ignoring.", e)); - self.set_uncles_hash(header.uncles_hash().clone()); - self.set_transactions_root(header.transactions_root().clone()); + fn populate_from(&mut self, header: &Header) { + self.block.header.set_difficulty(*header.difficulty()); + self.block.header.set_gas_limit(*header.gas_limit()); + self.block.header.set_timestamp(header.timestamp()); + self.block.header.set_author(*header.author()); + self.block.header.set_uncles_hash(*header.uncles_hash()); + self.block.header.set_transactions_root(*header.transactions_root()); + // TODO: that's horrible. set only for backwards compatibility + if header.extra_data().len() > self.engine.maximum_extra_data_size() { + warn!("Couldn't set extradata. Ignoring."); + } else { + self.block.header.set_extra_data(header.extra_data().clone()); + } } /// Turn this into a `ClosedBlock`. @@ -397,7 +370,7 @@ impl<'x> OpenBlock<'x> { warn!("Encountered error on state commit: {}", e); } s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes()))); - let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out(); + let uncle_bytes = encode_list(&s.block.uncles).into_vec(); s.block.header.set_uncles_hash(keccak(&uncle_bytes)); s.block.header.set_state_root(s.block.state.root().clone()); s.block.header.set_receipts_root(ordered_trie_root(s.block.receipts.iter().map(|r| r.rlp_bytes()))); @@ -406,8 +379,8 @@ impl<'x> OpenBlock<'x> { ClosedBlock { block: s.block, - uncle_bytes: uncle_bytes, - unclosed_state: unclosed_state, + uncle_bytes, + unclosed_state, } } @@ -425,7 +398,7 @@ impl<'x> OpenBlock<'x> { if s.block.header.transactions_root().is_zero() || s.block.header.transactions_root() == &KECCAK_NULL_RLP { s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes()))); } - let uncle_bytes = s.block.uncles.iter().fold(RlpStream::new_list(s.block.uncles.len()), |mut s, u| {s.append_raw(&u.rlp(Seal::With), 1); s} ).out(); + let uncle_bytes = encode_list(&s.block.uncles).into_vec(); if s.block.header.uncles_hash().is_zero() || s.block.header.uncles_hash() == &KECCAK_EMPTY_LIST_RLP { s.block.header.set_uncles_hash(keccak(&uncle_bytes)); } @@ -439,7 +412,7 @@ impl<'x> OpenBlock<'x> { LockedBlock { block: s.block, - uncle_bytes: uncle_bytes, + uncle_bytes, } } diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index f0152686a8a..ec4ab87fdf3 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -424,7 +424,7 @@ impl Miner { }; if self.options.infinite_pending_block { - open_block.set_gas_limit(!U256::zero()); + open_block.remove_gas_limit(); } (transactions, open_block, last_work_hash) diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index e6d23ec0fe9..8151d4064be 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -148,7 +148,6 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, ac vec![], false, ).unwrap(); - b.set_difficulty(U256::from(0x20000)); rolling_timestamp += 10; b.set_timestamp(rolling_timestamp); diff --git a/ethcore/src/tests/trace.rs b/ethcore/src/tests/trace.rs index 3bf4b822862..77f4891a6a5 100644 --- a/ethcore/src/tests/trace.rs +++ b/ethcore/src/tests/trace.rs @@ -88,7 +88,6 @@ fn can_trace_block_and_uncle_reward() { vec![], false, ).unwrap(); - root_block.set_difficulty(U256::from(0x20000)); rolling_timestamp += 10; root_block.set_timestamp(rolling_timestamp); @@ -117,7 +116,6 @@ fn can_trace_block_and_uncle_reward() { vec![], false, ).unwrap(); - parent_block.set_difficulty(U256::from(0x20000)); rolling_timestamp += 10; parent_block.set_timestamp(rolling_timestamp); @@ -145,7 +143,6 @@ fn can_trace_block_and_uncle_reward() { vec![], false ).unwrap(); - block.set_difficulty(U256::from(0x20000)); rolling_timestamp += 10; block.set_timestamp(rolling_timestamp); From 72789b2e084a4ce2293490afe270bfef83e0927d Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Mon, 5 Mar 2018 13:52:18 +0300 Subject: [PATCH 08/11] fix cache & snapcraft CI build (#8052) after successful testing it is necessary to port in a ```beta``` and ```stable``` --- .gitlab-ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 454fa9984ab..f3a8a3f0c1e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,12 +8,10 @@ variables: CARGOFLAGS: "" CI_SERVER_NAME: "GitLab CI" LIBSSL: "libssl1.0.0 (>=1.0.0)" - CARGO_HOME: $CI_PROJECT_DIR/cargo cache: key: "$CI_BUILD_STAGE-$CI_BUILD_REF_NAME" paths: - target/ - - cargo/ untracked: true linux-stable: stage: build @@ -132,7 +130,7 @@ linux-aarch64: name: "aarch64-unknown-linux-gnu_parity" linux-snap: stage: build - image: snapcore/snapcraft:stable + image: parity/snapcraft:gitlab-ci only: - stable - beta From 8d24dc17497e7fe65e69d79aa79869dda1986ba3 Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Mon, 5 Mar 2018 11:56:14 +0100 Subject: [PATCH 09/11] Update refs to shell (#8051) --- Cargo.lock | 12 ++++++------ dapps/ui/Cargo.toml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c4a53b0cacc..40a9c2f41df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2203,17 +2203,17 @@ dependencies = [ name = "parity-ui" version = "1.11.0" dependencies = [ - "parity-ui-dev 1.9.0 (git+https://github.com/parity-js/shell.git?rev=43a46b83821604da9e6a928599d23647a047af19)", + "parity-ui-dev 1.9.0 (git+https://github.com/parity-js/shell.git?rev=7f2f1ec51d96fc6682a82e95e09ada2e9283e4a0)", "parity-ui-old-dev 1.9.0 (git+https://github.com/parity-js/dapp-wallet.git?rev=1a58bf4836c84e1632e27ef607b5a388abd2bf2d)", "parity-ui-old-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git?rev=4c067dfa1a17fe71ab2ca26b18c52dcbd0f4fc04)", - "parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=1997bd3777a0e0bc1114cac341734d4793cca9e5)", + "parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=ac243b5ce9ce10d5f9f6137e974f195e8403b68e)", "rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parity-ui-dev" version = "1.9.0" -source = "git+https://github.com/parity-js/shell.git?rev=43a46b83821604da9e6a928599d23647a047af19#43a46b83821604da9e6a928599d23647a047af19" +source = "git+https://github.com/parity-js/shell.git?rev=7f2f1ec51d96fc6682a82e95e09ada2e9283e4a0#7f2f1ec51d96fc6682a82e95e09ada2e9283e4a0" dependencies = [ "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2237,7 +2237,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.9.0" -source = "git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=1997bd3777a0e0bc1114cac341734d4793cca9e5#1997bd3777a0e0bc1114cac341734d4793cca9e5" +source = "git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=ac243b5ce9ce10d5f9f6137e974f195e8403b68e#ac243b5ce9ce10d5f9f6137e974f195e8403b68e" dependencies = [ "parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3744,10 +3744,10 @@ dependencies = [ "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parity-dapps-glue 1.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "261c025c67ba416e9fe63aa9b3236520ce3c74cfbe43590c9cdcec4ccc8180e4" "checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "" -"checksum parity-ui-dev 1.9.0 (git+https://github.com/parity-js/shell.git?rev=43a46b83821604da9e6a928599d23647a047af19)" = "" +"checksum parity-ui-dev 1.9.0 (git+https://github.com/parity-js/shell.git?rev=7f2f1ec51d96fc6682a82e95e09ada2e9283e4a0)" = "" "checksum parity-ui-old-dev 1.9.0 (git+https://github.com/parity-js/dapp-wallet.git?rev=1a58bf4836c84e1632e27ef607b5a388abd2bf2d)" = "" "checksum parity-ui-old-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git?rev=4c067dfa1a17fe71ab2ca26b18c52dcbd0f4fc04)" = "" -"checksum parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=1997bd3777a0e0bc1114cac341734d4793cca9e5)" = "" +"checksum parity-ui-precompiled 1.9.0 (git+https://github.com/js-dist-paritytech/parity-master-1-10-shell.git?rev=ac243b5ce9ce10d5f9f6137e974f195e8403b68e)" = "" "checksum parity-wasm 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ba4b1d4236b76694f6ab8d8d00cdbe1e37c6dd1b5c803d26721f27e097d4d9" "checksum parity-wordlist 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0dec124478845b142f68b446cbee953d14d4b41f1bc0425024417720dce693" "checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412" diff --git a/dapps/ui/Cargo.toml b/dapps/ui/Cargo.toml index bf6b50afffe..991f0eb94ff 100644 --- a/dapps/ui/Cargo.toml +++ b/dapps/ui/Cargo.toml @@ -10,9 +10,9 @@ authors = ["Parity Technologies "] rustc_version = "0.2" [dependencies] -parity-ui-dev = { git = "https://github.com/parity-js/shell.git", rev = "43a46b83821604da9e6a928599d23647a047af19", optional = true } +parity-ui-dev = { git = "https://github.com/parity-js/shell.git", rev = "7f2f1ec51d96fc6682a82e95e09ada2e9283e4a0", optional = true } parity-ui-old-dev = { git = "https://github.com/parity-js/dapp-wallet.git", rev = "1a58bf4836c84e1632e27ef607b5a388abd2bf2d", optional = true } -parity-ui-precompiled = { git = "https://github.com/js-dist-paritytech/parity-master-1-10-shell.git", rev="1997bd3777a0e0bc1114cac341734d4793cca9e5", optional = true } +parity-ui-precompiled = { git = "https://github.com/js-dist-paritytech/parity-master-1-10-shell.git", rev="ac243b5ce9ce10d5f9f6137e974f195e8403b68e", optional = true } parity-ui-old-precompiled = { git = "https://github.com/js-dist-paritytech/parity-master-1-10-wallet.git", rev="4c067dfa1a17fe71ab2ca26b18c52dcbd0f4fc04", optional = true } [features] From 16d0d134ec79f25d9423faf288b0339ce2c5eff0 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 5 Mar 2018 11:56:35 +0100 Subject: [PATCH 10/11] Abstract devp2p (#8048) * Rename ethcore-network to ethcore-network-devp2p * Fix typo * Extract generic traits into util/network * Simplify util/network * Fix devp2p tests * Remove old feature * Fix RPC tests --- Cargo.lock | 18 +- ethcore/light/src/net/context.rs | 2 +- ethcore/light/src/net/mod.rs | 14 +- ethcore/node_filter/Cargo.toml | 2 +- ethcore/node_filter/src/lib.rs | 2 +- sync/Cargo.toml | 1 + sync/src/api.rs | 9 +- sync/src/lib.rs | 4 +- sync/src/sync_io.rs | 12 +- util/network-devp2p/Cargo.toml | 44 ++ .../src/connection.rs | 2 +- .../src/connection_filter.rs | 0 .../src/discovery.rs | 4 +- .../src/handshake.rs | 3 +- util/{network => network-devp2p}/src/host.rs | 192 ++------- .../src/ip_utils.rs | 0 util/network-devp2p/src/lib.rs | 118 +++++ .../src/node_table.rs | 3 +- .../src/service.rs | 6 +- .../src/session.rs | 79 +--- util/{network => network-devp2p}/src/stats.rs | 0 .../tests/tests.rs | 2 + util/network/Cargo.toml | 26 -- util/network/src/lib.rs | 406 ++++++++++++++---- whisper/src/net/mod.rs | 12 +- 25 files changed, 566 insertions(+), 395 deletions(-) create mode 100644 util/network-devp2p/Cargo.toml rename util/{network => network-devp2p}/src/connection.rs (99%) rename util/{network => network-devp2p}/src/connection_filter.rs (100%) rename util/{network => network-devp2p}/src/discovery.rs (99%) rename util/{network => network-devp2p}/src/handshake.rs (99%) rename util/{network => network-devp2p}/src/host.rs (86%) rename util/{network => network-devp2p}/src/ip_utils.rs (100%) create mode 100644 util/network-devp2p/src/lib.rs rename util/{network => network-devp2p}/src/node_table.rs (99%) rename util/{network => network-devp2p}/src/service.rs (97%) rename util/{network => network-devp2p}/src/session.rs (89%) rename util/{network => network-devp2p}/src/stats.rs (100%) rename util/{network => network-devp2p}/tests/tests.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 40a9c2f41df..0d61c1ace2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -621,6 +621,20 @@ dependencies = [ [[package]] name = "ethcore-network" version = "1.11.0" +dependencies = [ + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-io 1.11.0", + "ethcrypto 0.1.0", + "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethkey 0.3.0", + "ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.2.1", + "snappy 0.1.0 (git+https://github.com/paritytech/rust-snappy)", +] + +[[package]] +name = "ethcore-network-devp2p" +version = "1.11.0" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -628,6 +642,7 @@ dependencies = [ "ethcore-bytes 0.1.0", "ethcore-io 1.11.0", "ethcore-logger 1.11.0", + "ethcore-network 1.11.0", "ethcrypto 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", @@ -849,6 +864,7 @@ dependencies = [ "ethcore-io 1.11.0", "ethcore-light 1.11.0", "ethcore-network 1.11.0", + "ethcore-network-devp2p 1.11.0", "ethcore-transaction 0.1.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethkey 0.3.0", @@ -1712,7 +1728,7 @@ dependencies = [ "ethcore 1.11.0", "ethcore-bytes 0.1.0", "ethcore-io 1.11.0", - "ethcore-network 1.11.0", + "ethcore-network-devp2p 1.11.0", "ethereum-types 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "kvdb-memorydb 0.1.0", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs index 33009d7f607..613e26b1f11 100644 --- a/ethcore/light/src/net/context.rs +++ b/ethcore/light/src/net/context.rs @@ -47,7 +47,7 @@ pub trait IoContext { } -impl<'a> IoContext for NetworkContext<'a> { +impl IoContext for T where T: ?Sized + NetworkContext { fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { if let Err(e) = self.send(peer, packet_id, packet_body) { debug!(target: "pip", "Error sending packet to peer {}: {}", peer, e); diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 7166cae338d..81c92e09bc3 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -1089,23 +1089,23 @@ impl NetworkProtocolHandler for LightProtocol { } fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { - self.handle_packet(io, peer, packet_id, data); + self.handle_packet(&io, peer, packet_id, data); } fn connected(&self, io: &NetworkContext, peer: &PeerId) { - self.on_connect(peer, io); + self.on_connect(peer, &io); } fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { - self.on_disconnect(*peer, io); + self.on_disconnect(*peer, &io); } fn timeout(&self, io: &NetworkContext, timer: TimerToken) { match timer { - TIMEOUT => self.timeout_check(io), - TICK_TIMEOUT => self.tick_handlers(io), - PROPAGATE_TIMEOUT => self.propagate_transactions(io), - RECALCULATE_COSTS_TIMEOUT => self.begin_new_cost_period(io), + TIMEOUT => self.timeout_check(&io), + TICK_TIMEOUT => self.tick_handlers(&io), + PROPAGATE_TIMEOUT => self.propagate_transactions(&io), + RECALCULATE_COSTS_TIMEOUT => self.begin_new_cost_period(&io), _ => warn!(target: "pip", "received timeout on unknown token {}", timer), } } diff --git a/ethcore/node_filter/Cargo.toml b/ethcore/node_filter/Cargo.toml index d4621e54867..0231276e103 100644 --- a/ethcore/node_filter/Cargo.toml +++ b/ethcore/node_filter/Cargo.toml @@ -9,7 +9,7 @@ authors = ["Parity Technologies "] [dependencies] ethcore = { path = ".."} ethcore-bytes = { path = "../../util/bytes" } -ethcore-network = { path = "../../util/network" } +ethcore-network-devp2p = { path = "../../util/network-devp2p" } ethereum-types = "0.2" log = "0.3" parking_lot = "0.5" diff --git a/ethcore/node_filter/src/lib.rs b/ethcore/node_filter/src/lib.rs index a145b4e87fe..1fa955464ae 100644 --- a/ethcore/node_filter/src/lib.rs +++ b/ethcore/node_filter/src/lib.rs @@ -19,7 +19,7 @@ extern crate ethabi; extern crate ethcore; extern crate ethcore_bytes as bytes; -extern crate ethcore_network as network; +extern crate ethcore_network_devp2p as network; extern crate ethereum_types; extern crate lru_cache; extern crate parking_lot; diff --git a/sync/Cargo.toml b/sync/Cargo.toml index 3db8ec7d8dd..5a14a11332e 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -10,6 +10,7 @@ authors = ["Parity Technologies "] [dependencies] ethcore-bytes = { path = "../util/bytes" } ethcore-network = { path = "../util/network" } +ethcore-network-devp2p = { path = "../util/network-devp2p" } ethcore-io = { path = "../util/io" } ethcore-light = { path = "../ethcore/light" } ethcore-transaction = { path = "../ethcore/transaction" } diff --git a/sync/src/api.rs b/sync/src/api.rs index d0f55d53cf2..38543de15c3 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -18,8 +18,9 @@ use std::sync::Arc; use std::collections::{HashMap, BTreeMap}; use std::io; use bytes::Bytes; -use network::{NetworkProtocolHandler, NetworkService, NetworkContext, HostInfo, PeerId, ProtocolId, - NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, Error, ErrorKind, ConnectionFilter}; +use devp2p::{NetworkService, ConnectionFilter}; +use network::{NetworkProtocolHandler, NetworkContext, HostInfo, PeerId, ProtocolId, + NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, Error, ErrorKind}; use ethereum_types::{H256, H512, U256}; use io::{TimerToken}; use ethcore::ethstore::ethkey::Secret; @@ -393,7 +394,7 @@ impl ChainNotify for EthSync { }; let chain_info = self.eth_handler.chain.chain_info(); - light_proto.make_announcement(context, Announcement { + light_proto.make_announcement(&context, Announcement { head_hash: chain_info.best_block_hash, head_num: chain_info.best_block_number, head_td: chain_info.total_difficulty, @@ -737,7 +738,7 @@ impl LightSync { { self.network.with_context_eval( self.subprotocol_name, - move |ctx| self.proto.with_context(ctx, f), + move |ctx| self.proto.with_context(&ctx, f), ) } } diff --git a/sync/src/lib.rs b/sync/src/lib.rs index baceb0c7f96..9d0f5b14f5b 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -22,6 +22,7 @@ //! extern crate ethcore_network as network; +extern crate ethcore_network_devp2p as devp2p; extern crate ethcore_bytes as bytes; extern crate ethcore_io as io; extern crate ethcore_transaction as transaction; @@ -68,4 +69,5 @@ mod api; pub use api::*; pub use chain::{SyncStatus, SyncState}; -pub use network::{validate_node_url, NonReservedPeerMode, Error, ErrorKind, ConnectionFilter, ConnectionDirection}; +pub use devp2p::{validate_node_url, ConnectionFilter, ConnectionDirection}; +pub use network::{NonReservedPeerMode, Error, ErrorKind}; diff --git a/sync/src/sync_io.rs b/sync/src/sync_io.rs index b383a51cdf4..76f323e8261 100644 --- a/sync/src/sync_io.rs +++ b/sync/src/sync_io.rs @@ -61,19 +61,19 @@ pub trait SyncIo { } /// Wraps `NetworkContext` and the blockchain client -pub struct NetSyncIo<'s, 'h> where 'h: 's { - network: &'s NetworkContext<'h>, +pub struct NetSyncIo<'s> { + network: &'s NetworkContext, chain: &'s BlockChainClient, snapshot_service: &'s SnapshotService, chain_overlay: &'s RwLock>, } -impl<'s, 'h> NetSyncIo<'s, 'h> { +impl<'s> NetSyncIo<'s> { /// Creates a new instance from the `NetworkContext` and the blockchain client reference. - pub fn new(network: &'s NetworkContext<'h>, + pub fn new(network: &'s NetworkContext, chain: &'s BlockChainClient, snapshot_service: &'s SnapshotService, - chain_overlay: &'s RwLock>) -> NetSyncIo<'s, 'h> { + chain_overlay: &'s RwLock>) -> NetSyncIo<'s> { NetSyncIo { network: network, chain: chain, @@ -83,7 +83,7 @@ impl<'s, 'h> NetSyncIo<'s, 'h> { } } -impl<'s, 'h> SyncIo for NetSyncIo<'s, 'h> { +impl<'s> SyncIo for NetSyncIo<'s> { fn disable_peer(&mut self, peer_id: PeerId) { self.network.disable_peer(peer_id); } diff --git a/util/network-devp2p/Cargo.toml b/util/network-devp2p/Cargo.toml new file mode 100644 index 00000000000..f8e0115d5ee --- /dev/null +++ b/util/network-devp2p/Cargo.toml @@ -0,0 +1,44 @@ +[package] +description = "DevP2P implementation of the ethcore network library" +homepage = "http://parity.io" +license = "GPL-3.0" +name = "ethcore-network-devp2p" +version = "1.11.0" +authors = ["Parity Technologies "] + +[dependencies] +log = "0.3" +mio = "0.6.8" +bytes = "0.4" +rand = "0.4" +time = "0.1.34" +tiny-keccak = "1.3" +rust-crypto = "0.2.34" +slab = "0.2" +igd = "0.6" +libc = "0.2.7" +parking_lot = "0.5" +ansi_term = "0.10" +rustc-hex = "1.0" +ethcore-io = { path = "../io" } +ethcore-bytes = { path = "../bytes" } +ethcore-network = { path = "../network" } +ethereum-types = "0.2" +ethkey = { path = "../../ethkey" } +ethcrypto = { path = "../../ethcrypto" } +rlp = { path = "../rlp" } +path = { path = "../path" } +ethcore-logger = { path ="../../logger" } +ipnetwork = "0.12.6" +keccak-hash = { path = "../hash" } +snappy = { git = "https://github.com/paritytech/rust-snappy" } +serde = "1.0" +serde_json = "1.0" +serde_derive = "1.0" +error-chain = { version = "0.11", default-features = false } + +[dev-dependencies] +tempdir = "0.3" + +[features] +default = [] diff --git a/util/network/src/connection.rs b/util/network-devp2p/src/connection.rs similarity index 99% rename from util/network/src/connection.rs rename to util/network-devp2p/src/connection.rs index 1b87c6d2f79..e32439756fd 100644 --- a/util/network/src/connection.rs +++ b/util/network-devp2p/src/connection.rs @@ -36,7 +36,7 @@ use rcrypto::buffer::*; use tiny_keccak::Keccak; use bytes::{Buf, BufMut}; use crypto; -use error::{Error, ErrorKind}; +use network::{Error, ErrorKind}; const ENCRYPTED_HEADER_LEN: usize = 32; const RECIEVE_PAYLOAD_TIMEOUT: u64 = 30000; diff --git a/util/network/src/connection_filter.rs b/util/network-devp2p/src/connection_filter.rs similarity index 100% rename from util/network/src/connection_filter.rs rename to util/network-devp2p/src/connection_filter.rs diff --git a/util/network/src/discovery.rs b/util/network-devp2p/src/discovery.rs similarity index 99% rename from util/network/src/discovery.rs rename to util/network-devp2p/src/discovery.rs index 286cf3461f8..683a6c5fad0 100644 --- a/util/network/src/discovery.rs +++ b/util/network-devp2p/src/discovery.rs @@ -27,10 +27,10 @@ use time; use ethereum_types::{H256, H520}; use rlp::*; use node_table::*; -use error::{Error, ErrorKind}; +use network::{Error, ErrorKind}; use io::{StreamToken, IoContext}; use ethkey::{Secret, KeyPair, sign, recover}; -use IpFilter; +use network::IpFilter; use PROTOCOL_VERSION; diff --git a/util/network/src/handshake.rs b/util/network-devp2p/src/handshake.rs similarity index 99% rename from util/network/src/handshake.rs rename to util/network-devp2p/src/handshake.rs index 64622f27414..240046a98e1 100644 --- a/util/network/src/handshake.rs +++ b/util/network-devp2p/src/handshake.rs @@ -22,13 +22,12 @@ use ethereum_types::{H256, H520}; use ethcore_bytes::Bytes; use rlp::*; use connection::{Connection}; -use host::{HostInfo}; use node_table::NodeId; use stats::NetworkStats; use io::{IoContext, StreamToken}; use ethkey::{KeyPair, Public, Secret, recover, sign, Generator, Random}; use crypto::{ecdh, ecies}; -use error::{Error, ErrorKind}; +use network::{Error, ErrorKind, HostInfo}; #[derive(PartialEq, Eq, Debug)] enum HandshakeState { diff --git a/util/network/src/host.rs b/util/network-devp2p/src/host.rs similarity index 86% rename from util/network/src/host.rs rename to util/network-devp2p/src/host.rs index 47b5322259f..351f232800e 100644 --- a/util/network/src/host.rs +++ b/util/network-devp2p/src/host.rs @@ -31,17 +31,20 @@ use mio::deprecated::{EventLoop}; use mio::tcp::*; use ethereum_types::H256; use rlp::*; -use session::{Session, SessionInfo, SessionData}; +use session::{Session, SessionData}; use io::*; -use {NetworkProtocolHandler, NonReservedPeerMode, PROTOCOL_VERSION, IpFilter}; +use PROTOCOL_VERSION; use node_table::*; +use network::{NetworkConfiguration, NetworkIoMessage, ProtocolId, PeerId, PacketId}; +use network::{NonReservedPeerMode, NetworkContext as NetworkContextTrait}; +use network::HostInfo as HostInfoTrait; +use network::{SessionInfo, Error, ErrorKind, DisconnectReason, NetworkProtocolHandler}; use stats::NetworkStats; use discovery::{Discovery, TableUpdates, NodeEntry}; use ip_utils::{map_external_address, select_public_address}; use path::restrict_permissions_owner; use parking_lot::{Mutex, RwLock}; use connection_filter::{ConnectionFilter, ConnectionDirection}; -use error::{Error, ErrorKind, DisconnectReason}; type Slab = ::slab::Slab; @@ -72,132 +75,6 @@ const DISCOVERY_ROUND_TIMEOUT: u64 = 300; // for NODE_TABLE TimerToken const NODE_TABLE_TIMEOUT: u64 = 300_000; -#[derive(Debug, PartialEq, Clone)] -/// Network service configuration -pub struct NetworkConfiguration { - /// Directory path to store general network configuration. None means nothing will be saved - pub config_path: Option, - /// Directory path to store network-specific configuration. None means nothing will be saved - pub net_config_path: Option, - /// IP address to listen for incoming connections. Listen to all connections by default - pub listen_address: Option, - /// IP address to advertise. Detected automatically if none. - pub public_address: Option, - /// Port for UDP connections, same as TCP by default - pub udp_port: Option, - /// Enable NAT configuration - pub nat_enabled: bool, - /// Enable discovery - pub discovery_enabled: bool, - /// List of initial node addresses - pub boot_nodes: Vec, - /// Use provided node key instead of default - pub use_secret: Option, - /// Minimum number of connected peers to maintain - pub min_peers: u32, - /// Maximum allowed number of peers - pub max_peers: u32, - /// Maximum handshakes - pub max_handshakes: u32, - /// Reserved protocols. Peers with protocol get additional connection slots. - pub reserved_protocols: HashMap, - /// List of reserved node addresses. - pub reserved_nodes: Vec, - /// The non-reserved peer mode. - pub non_reserved_mode: NonReservedPeerMode, - /// IP filter - pub ip_filter: IpFilter, - /// Client identifier - pub client_version: String, -} - -impl Default for NetworkConfiguration { - fn default() -> Self { - NetworkConfiguration::new() - } -} - -impl NetworkConfiguration { - /// Create a new instance of default settings. - pub fn new() -> Self { - NetworkConfiguration { - config_path: None, - net_config_path: None, - listen_address: None, - public_address: None, - udp_port: None, - nat_enabled: true, - discovery_enabled: true, - boot_nodes: Vec::new(), - use_secret: None, - min_peers: 25, - max_peers: 50, - max_handshakes: 64, - reserved_protocols: HashMap::new(), - ip_filter: IpFilter::default(), - reserved_nodes: Vec::new(), - non_reserved_mode: NonReservedPeerMode::Accept, - client_version: "Parity-network".into(), - } - } - - /// Create new default configuration with sepcified listen port. - pub fn new_with_port(port: u16) -> NetworkConfiguration { - let mut config = NetworkConfiguration::new(); - config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port))); - config - } - - /// Create new default configuration for localhost-only connection with random port (usefull for testing) - pub fn new_local() -> NetworkConfiguration { - let mut config = NetworkConfiguration::new(); - config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0))); - config.nat_enabled = false; - config - } -} - -/// Protocol handler level packet id -pub type PacketId = u8; -/// Protocol / handler id -pub type ProtocolId = [u8; 3]; - -/// Messages used to communitate with the event loop from other threads. -#[derive(Clone)] -pub enum NetworkIoMessage { - /// Register a new protocol handler. - AddHandler { - /// Handler shared instance. - handler: Arc, - /// Protocol Id. - protocol: ProtocolId, - /// Supported protocol versions. - versions: Vec, - /// Number of packet IDs reserved by the protocol. - packet_count: u8, - }, - /// Register a new protocol timer - AddTimer { - /// Protocol Id. - protocol: ProtocolId, - /// Timer token. - token: TimerToken, - /// Timer delay in milliseconds. - delay: u64, - }, - /// Initliaze public interface. - InitPublicInterface, - /// Disconnect a peer. - Disconnect(PeerId), - /// Disconnect and temporary disable peer. - DisablePeer(PeerId), - /// Network has been started with the host as the given enode. - NetworkStarted(String), -} - -/// Local (temporary) peer session ID. -pub type PeerId = usize; - #[derive(Debug, PartialEq, Eq)] /// Protocol info pub struct CapabilityInfo { @@ -248,14 +125,14 @@ impl<'s> NetworkContext<'s> { _ => self.sessions.read().get(peer).cloned(), } } +} - /// Send a packet over the network to another peer. - pub fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec) -> Result<(), Error> { +impl<'s> NetworkContextTrait for NetworkContext<'s> { + fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec) -> Result<(), Error> { self.send_protocol(self.protocol, peer, packet_id, data) } - /// Send a packet over the network to another peer using specified protocol. - pub fn send_protocol(&self, protocol: ProtocolId, peer: PeerId, packet_id: PacketId, data: Vec) -> Result<(), Error> { + fn send_protocol(&self, protocol: ProtocolId, peer: PeerId, packet_id: PacketId, data: Vec) -> Result<(), Error> { let session = self.resolve_session(peer); if let Some(session) = session { session.lock().send_packet(self.io, Some(protocol), packet_id as u8, &data)?; @@ -265,36 +142,30 @@ impl<'s> NetworkContext<'s> { Ok(()) } - /// Respond to a current network message. Panics if no there is no packet in the context. If the session is expired returns nothing. - pub fn respond(&self, packet_id: PacketId, data: Vec) -> Result<(), Error> { + fn respond(&self, packet_id: PacketId, data: Vec) -> Result<(), Error> { assert!(self.session.is_some(), "Respond called without network context"); self.session_id.map_or_else(|| Err(ErrorKind::Expired.into()), |id| self.send(id, packet_id, data)) } - /// Get an IoChannel. - pub fn io_channel(&self) -> IoChannel { + fn io_channel(&self) -> IoChannel { self.io.channel() } - /// Disconnect a peer and prevent it from connecting again. - pub fn disable_peer(&self, peer: PeerId) { + fn disable_peer(&self, peer: PeerId) { self.io.message(NetworkIoMessage::DisablePeer(peer)) .unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e)); } - /// Disconnect peer. Reconnect can be attempted later. - pub fn disconnect_peer(&self, peer: PeerId) { + fn disconnect_peer(&self, peer: PeerId) { self.io.message(NetworkIoMessage::Disconnect(peer)) .unwrap_or_else(|e| warn!("Error sending network IO message: {:?}", e)); } - /// Check if the session is still active. - pub fn is_expired(&self) -> bool { + fn is_expired(&self) -> bool { self.session.as_ref().map_or(false, |s| s.lock().expired()) } - /// Register a new IO timer. 'IoHandler::timeout' will be called with the token. - pub fn register_timer(&self, token: TimerToken, ms: u64) -> Result<(), Error> { + fn register_timer(&self, token: TimerToken, ms: u64) -> Result<(), Error> { self.io.message(NetworkIoMessage::AddTimer { token: token, delay: ms, @@ -303,24 +174,20 @@ impl<'s> NetworkContext<'s> { Ok(()) } - /// Returns peer identification string - pub fn peer_client_version(&self, peer: PeerId) -> String { + fn peer_client_version(&self, peer: PeerId) -> String { self.resolve_session(peer).map_or("unknown".to_owned(), |s| s.lock().info.client_version.clone()) } - /// Returns information on p2p session - pub fn session_info(&self, peer: PeerId) -> Option { + fn session_info(&self, peer: PeerId) -> Option { self.resolve_session(peer).map(|s| s.lock().info.clone()) } - /// Returns max version for a given protocol. - pub fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option { + fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option { let session = self.resolve_session(peer); session.and_then(|s| s.lock().capability_version(protocol)) } - /// Returns this object's subprotocol name. - pub fn subprotocol_name(&self) -> ProtocolId { self.protocol } + fn subprotocol_name(&self) -> ProtocolId { self.protocol } } /// Shared host information @@ -341,24 +208,21 @@ pub struct HostInfo { pub public_endpoint: Option, } -impl HostInfo { - /// Returns public key - pub fn id(&self) -> &NodeId { +impl HostInfoTrait for HostInfo { + fn id(&self) -> &NodeId { self.keys.public() } - /// Returns secret key - pub fn secret(&self) -> &Secret { + fn secret(&self) -> &Secret { self.keys.secret() } - /// Increments and returns connection nonce. - pub fn next_nonce(&mut self) -> H256 { + fn next_nonce(&mut self) -> H256 { self.nonce = keccak(&self.nonce); self.nonce } - pub fn client_version(&self) -> &str { + fn client_version(&self) -> &str { &self.config.client_version } } @@ -378,7 +242,7 @@ pub struct Host { sessions: Arc>>, discovery: Mutex>, nodes: RwLock, - handlers: RwLock>>, + handlers: RwLock>>, timers: RwLock>, timer_counter: RwLock, stats: Arc, @@ -1006,14 +870,14 @@ impl Host { self.nodes.write().update(node_changes, &*self.reserved_nodes.read()); } - pub fn with_context(&self, protocol: ProtocolId, io: &IoContext, action: F) where F: FnOnce(&NetworkContext) { + pub fn with_context(&self, protocol: ProtocolId, io: &IoContext, action: F) where F: FnOnce(&NetworkContextTrait) { let reserved = { self.reserved_nodes.read() }; let context = NetworkContext::new(io, protocol, None, self.sessions.clone(), &reserved); action(&context); } - pub fn with_context_eval(&self, protocol: ProtocolId, io: &IoContext, action: F) -> T where F: FnOnce(&NetworkContext) -> T { + pub fn with_context_eval(&self, protocol: ProtocolId, io: &IoContext, action: F) -> T where F: FnOnce(&NetworkContextTrait) -> T { let reserved = { self.reserved_nodes.read() }; let context = NetworkContext::new(io, protocol, None, self.sessions.clone(), &reserved); diff --git a/util/network/src/ip_utils.rs b/util/network-devp2p/src/ip_utils.rs similarity index 100% rename from util/network/src/ip_utils.rs rename to util/network-devp2p/src/ip_utils.rs diff --git a/util/network-devp2p/src/lib.rs b/util/network-devp2p/src/lib.rs new file mode 100644 index 00000000000..8315c0f6b46 --- /dev/null +++ b/util/network-devp2p/src/lib.rs @@ -0,0 +1,118 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Network and general IO module. +//! +//! Example usage for creating a network service and adding an IO handler: +//! +//! ```rust +//! extern crate ethcore_network as net; +//! extern crate ethcore_network_devp2p as devp2p; +//! use net::*; +//! use devp2p::NetworkService; +//! use std::sync::Arc; +//! +//! struct MyHandler; +//! +//! impl NetworkProtocolHandler for MyHandler { +//! fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) { +//! io.register_timer(0, 1000); +//! } +//! +//! fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { +//! println!("Received {} ({} bytes) from {}", packet_id, data.len(), peer); +//! } +//! +//! fn connected(&self, io: &NetworkContext, peer: &PeerId) { +//! println!("Connected {}", peer); +//! } +//! +//! fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { +//! println!("Disconnected {}", peer); +//! } +//! } +//! +//! fn main () { +//! let mut service = NetworkService::new(NetworkConfiguration::new_local(), None).expect("Error creating network service"); +//! service.start().expect("Error starting service"); +//! service.register_protocol(Arc::new(MyHandler), *b"myp", 1, &[1u8]); +//! +//! // Wait for quit condition +//! // ... +//! // Drop the service +//! } +//! ``` + +//TODO: use Poll from mio +#![allow(deprecated)] + +extern crate ethcore_io as io; +extern crate ethcore_bytes; +extern crate ethereum_types; +extern crate parking_lot; +extern crate mio; +extern crate tiny_keccak; +extern crate crypto as rcrypto; +extern crate rand; +extern crate time; +extern crate ansi_term; //TODO: remove this +extern crate rustc_hex; +extern crate igd; +extern crate libc; +extern crate slab; +extern crate ethkey; +extern crate ethcrypto as crypto; +extern crate rlp; +extern crate bytes; +extern crate path; +extern crate ethcore_logger; +extern crate ethcore_network as network; +extern crate ipnetwork; +extern crate keccak_hash as hash; +extern crate serde; +extern crate serde_json; +extern crate snappy; + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; +#[macro_use] +extern crate serde_derive; + +#[cfg(test)] +extern crate tempdir; + +mod host; +mod connection; +mod handshake; +mod session; +mod discovery; +mod service; +mod node_table; +mod stats; +mod ip_utils; +mod connection_filter; + +pub use service::NetworkService; +pub use stats::NetworkStats; +pub use connection_filter::{ConnectionFilter, ConnectionDirection}; +pub use host::NetworkContext; + +pub use io::TimerToken; +pub use node_table::{validate_node_url, NodeId}; + +const PROTOCOL_VERSION: u32 = 5; diff --git a/util/network/src/node_table.rs b/util/network-devp2p/src/node_table.rs similarity index 99% rename from util/network/src/node_table.rs rename to util/network-devp2p/src/node_table.rs index ebf6003640f..c3927c694ab 100644 --- a/util/network/src/node_table.rs +++ b/util/network-devp2p/src/node_table.rs @@ -23,8 +23,7 @@ use std::str::FromStr; use std::{fs, mem, slice}; use ethereum_types::H512; use rlp::*; -use error::{Error, ErrorKind}; -use {AllowIP, IpFilter}; +use network::{Error, ErrorKind, AllowIP, IpFilter}; use discovery::{TableUpdates, NodeEntry}; use ip_utils::*; use serde_json; diff --git a/util/network/src/service.rs b/util/network-devp2p/src/service.rs similarity index 97% rename from util/network/src/service.rs rename to util/network-devp2p/src/service.rs index e2da827ec6e..95867f8eb0a 100644 --- a/util/network/src/service.rs +++ b/util/network-devp2p/src/service.rs @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use {NetworkProtocolHandler, NetworkConfiguration, NonReservedPeerMode}; -use error::Error; -use host::{Host, NetworkContext, NetworkIoMessage, PeerId, ProtocolId}; +use network::{Error, NetworkConfiguration, NetworkProtocolHandler, NonReservedPeerMode}; +use network::{NetworkContext, PeerId, ProtocolId, NetworkIoMessage}; +use host::Host; use stats::NetworkStats; use io::*; use parking_lot::RwLock; diff --git a/util/network/src/session.rs b/util/network-devp2p/src/session.rs similarity index 89% rename from util/network/src/session.rs rename to util/network-devp2p/src/session.rs index e5ffc68c6e3..10d625aa0a5 100644 --- a/util/network/src/session.rs +++ b/util/network-devp2p/src/session.rs @@ -16,7 +16,6 @@ use std::{str, io}; use std::net::SocketAddr; -use std::cmp::Ordering; use std::sync::*; use std::collections::HashMap; @@ -28,7 +27,8 @@ use rlp::*; use connection::{EncryptedConnection, Packet, Connection, MAX_PAYLOAD_SIZE}; use handshake::Handshake; use io::{IoContext, StreamToken}; -use error::{Error, ErrorKind, DisconnectReason}; +use network::{Error, ErrorKind, DisconnectReason, SessionInfo, ProtocolId, PeerCapabilityInfo}; +use network::{SessionCapabilityInfo, HostInfo as HostInfoTrait}; use host::*; use node_table::NodeId; use stats::NetworkStats; @@ -90,81 +90,6 @@ pub enum SessionData { Continue, } -/// Shared session information -#[derive(Debug, Clone)] -pub struct SessionInfo { - /// Peer public key - pub id: Option, - /// Peer client ID - pub client_version: String, - /// Peer RLPx protocol version - pub protocol_version: u32, - /// Session protocol capabilities - pub capabilities: Vec, - /// Peer protocol capabilities - pub peer_capabilities: Vec, - /// Peer ping delay in milliseconds - pub ping_ms: Option, - /// True if this session was originated by us. - pub originated: bool, - /// Remote endpoint address of the session - pub remote_address: String, - /// Local endpoint address of the session - pub local_address: String, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PeerCapabilityInfo { - pub protocol: ProtocolId, - pub version: u8, -} - -impl Decodable for PeerCapabilityInfo { - fn decode(rlp: &UntrustedRlp) -> Result { - let p: Vec = rlp.val_at(0)?; - if p.len() != 3 { - return Err(DecoderError::Custom("Invalid subprotocol string length. Should be 3")); - } - let mut p2: ProtocolId = [0u8; 3]; - p2.clone_from_slice(&p); - Ok(PeerCapabilityInfo { - protocol: p2, - version: rlp.val_at(1)? - }) - } -} - -impl ToString for PeerCapabilityInfo { - fn to_string(&self) -> String { - format!("{}/{}", str::from_utf8(&self.protocol[..]).unwrap_or("???"), self.version) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SessionCapabilityInfo { - pub protocol: [u8; 3], - pub version: u8, - pub packet_count: u8, - pub id_offset: u8, -} - -impl PartialOrd for SessionCapabilityInfo { - fn partial_cmp(&self, other: &SessionCapabilityInfo) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for SessionCapabilityInfo { - fn cmp(&self, b: &SessionCapabilityInfo) -> Ordering { - // By protocol id first - if self.protocol != b.protocol { - return self.protocol.cmp(&b.protocol); - } - // By version - self.version.cmp(&b.version) - } -} - const PACKET_HELLO: u8 = 0x80; const PACKET_DISCONNECT: u8 = 0x01; const PACKET_PING: u8 = 0x02; diff --git a/util/network/src/stats.rs b/util/network-devp2p/src/stats.rs similarity index 100% rename from util/network/src/stats.rs rename to util/network-devp2p/src/stats.rs diff --git a/util/network/tests/tests.rs b/util/network-devp2p/tests/tests.rs similarity index 98% rename from util/network/tests/tests.rs rename to util/network-devp2p/tests/tests.rs index 969e1a469f5..e964a9a8b50 100644 --- a/util/network/tests/tests.rs +++ b/util/network-devp2p/tests/tests.rs @@ -21,6 +21,7 @@ extern crate ethcore_bytes; extern crate ethcore_io as io; extern crate ethcore_logger; extern crate ethcore_network; +extern crate ethcore_network_devp2p; extern crate ethkey; use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; @@ -30,6 +31,7 @@ use std::time::*; use parking_lot::Mutex; use ethcore_bytes::Bytes; use ethcore_network::*; +use ethcore_network_devp2p::NetworkService; use ethkey::{Random, Generator}; use io::TimerToken; diff --git a/util/network/Cargo.toml b/util/network/Cargo.toml index c0ced05847d..7f9b30400b5 100644 --- a/util/network/Cargo.toml +++ b/util/network/Cargo.toml @@ -7,37 +7,11 @@ version = "1.11.0" authors = ["Parity Technologies "] [dependencies] -log = "0.3" -mio = "0.6.8" -bytes = "0.4" -rand = "0.4" -time = "0.1.34" -tiny-keccak = "1.3" -rust-crypto = "0.2.34" -slab = "0.2" -igd = "0.6" -libc = "0.2.7" -parking_lot = "0.5" -ansi_term = "0.10" -rustc-hex = "1.0" ethcore-io = { path = "../io" } -ethcore-bytes = { path = "../bytes" } ethereum-types = "0.2" ethkey = { path = "../../ethkey" } ethcrypto = { path = "../../ethcrypto" } rlp = { path = "../rlp" } -path = { path = "../path" } -ethcore-logger = { path ="../../logger" } ipnetwork = "0.12.6" -keccak-hash = { path = "../hash" } snappy = { git = "https://github.com/paritytech/rust-snappy" } -serde = "1.0" -serde_json = "1.0" -serde_derive = "1.0" error-chain = { version = "0.11", default-features = false } - -[dev-dependencies] -tempdir = "0.3" - -[features] -default = [] diff --git a/util/network/src/lib.rs b/util/network/src/lib.rs index 02c30c22892..de1c8f39650 100644 --- a/util/network/src/lib.rs +++ b/util/network/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,111 +14,338 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! Network and general IO module. -//! -//! Example usage for craeting a network service and adding an IO handler: -//! -//! ```rust -//! extern crate ethcore_network as net; -//! use net::*; -//! use std::sync::Arc; -//! -//! struct MyHandler; -//! -//! impl NetworkProtocolHandler for MyHandler { -//! fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) { -//! io.register_timer(0, 1000); -//! } -//! -//! fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { -//! println!("Received {} ({} bytes) from {}", packet_id, data.len(), peer); -//! } -//! -//! fn connected(&self, io: &NetworkContext, peer: &PeerId) { -//! println!("Connected {}", peer); -//! } -//! -//! fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { -//! println!("Disconnected {}", peer); -//! } -//! } -//! -//! fn main () { -//! let mut service = NetworkService::new(NetworkConfiguration::new_local(), None).expect("Error creating network service"); -//! service.start().expect("Error starting service"); -//! service.register_protocol(Arc::new(MyHandler), *b"myp", 1, &[1u8]); -//! -//! // Wait for quit condition -//! // ... -//! // Drop the service -//! } -//! ``` - -//TODO: use Poll from mio -#![allow(deprecated)] #![recursion_limit="128"] extern crate ethcore_io as io; -extern crate ethcore_bytes; +extern crate ethcrypto as crypto; extern crate ethereum_types; -extern crate parking_lot; -extern crate mio; -extern crate tiny_keccak; -extern crate crypto as rcrypto; -extern crate rand; -extern crate time; -extern crate ansi_term; //TODO: remove this -extern crate rustc_hex; -extern crate igd; -extern crate libc; -extern crate slab; extern crate ethkey; -extern crate ethcrypto as crypto; extern crate rlp; -extern crate bytes; -extern crate path; -extern crate ethcore_logger; extern crate ipnetwork; -extern crate keccak_hash as hash; -extern crate serde; -extern crate serde_json; extern crate snappy; #[macro_use] extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate serde_derive; - -#[cfg(test)] -extern crate tempdir; -mod host; -mod connection; -mod handshake; -mod session; -mod discovery; -mod service; mod error; -mod node_table; -mod stats; -mod ip_utils; -mod connection_filter; - -pub use host::{HostInfo, PeerId, PacketId, ProtocolId, NetworkContext, NetworkIoMessage, NetworkConfiguration}; -pub use service::NetworkService; -pub use error::{Error, ErrorKind}; -pub use stats::NetworkStats; -pub use session::SessionInfo; -pub use connection_filter::{ConnectionFilter, ConnectionDirection}; pub use io::TimerToken; -pub use node_table::{validate_node_url, NodeId}; +pub use error::{Error, ErrorKind, DisconnectReason}; + +use std::cmp::Ordering; +use std::collections::HashMap; +use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr}; +use std::str::{self, FromStr}; +use std::sync::Arc; use ipnetwork::{IpNetwork, IpNetworkError}; -use std::str::FromStr; +use io::IoChannel; +use ethkey::Secret; +use ethereum_types::{H256, H512}; +use rlp::{Decodable, DecoderError, UntrustedRlp}; + +/// Protocol handler level packet id +pub type PacketId = u8; +/// Protocol / handler id +pub type ProtocolId = [u8; 3]; + +/// Node public key +pub type NodeId = H512; + +/// Local (temporary) peer session ID. +pub type PeerId = usize; + +/// Messages used to communitate with the event loop from other threads. +#[derive(Clone)] +pub enum NetworkIoMessage { + /// Register a new protocol handler. + AddHandler { + /// Handler shared instance. + handler: Arc, + /// Protocol Id. + protocol: ProtocolId, + /// Supported protocol versions. + versions: Vec, + /// Number of packet IDs reserved by the protocol. + packet_count: u8, + }, + /// Register a new protocol timer + AddTimer { + /// Protocol Id. + protocol: ProtocolId, + /// Timer token. + token: TimerToken, + /// Timer delay in milliseconds. + delay: u64, + }, + /// Initliaze public interface. + InitPublicInterface, + /// Disconnect a peer. + Disconnect(PeerId), + /// Disconnect and temporary disable peer. + DisablePeer(PeerId), + /// Network has been started with the host as the given enode. + NetworkStarted(String), +} + +/// Shared session information +#[derive(Debug, Clone)] +pub struct SessionInfo { + /// Peer public key + pub id: Option, + /// Peer client ID + pub client_version: String, + /// Peer RLPx protocol version + pub protocol_version: u32, + /// Session protocol capabilities + pub capabilities: Vec, + /// Peer protocol capabilities + pub peer_capabilities: Vec, + /// Peer ping delay in milliseconds + pub ping_ms: Option, + /// True if this session was originated by us. + pub originated: bool, + /// Remote endpoint address of the session + pub remote_address: String, + /// Local endpoint address of the session + pub local_address: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PeerCapabilityInfo { + pub protocol: ProtocolId, + pub version: u8, +} + +impl Decodable for PeerCapabilityInfo { + fn decode(rlp: &UntrustedRlp) -> Result { + let p: Vec = rlp.val_at(0)?; + if p.len() != 3 { + return Err(DecoderError::Custom("Invalid subprotocol string length. Should be 3")); + } + let mut p2: ProtocolId = [0u8; 3]; + p2.clone_from_slice(&p); + Ok(PeerCapabilityInfo { + protocol: p2, + version: rlp.val_at(1)? + }) + } +} + +impl ToString for PeerCapabilityInfo { + fn to_string(&self) -> String { + format!("{}/{}", str::from_utf8(&self.protocol[..]).unwrap_or("???"), self.version) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SessionCapabilityInfo { + pub protocol: [u8; 3], + pub version: u8, + pub packet_count: u8, + pub id_offset: u8, +} + +impl PartialOrd for SessionCapabilityInfo { + fn partial_cmp(&self, other: &SessionCapabilityInfo) -> Option { + Some(self.cmp(other)) + } +} -const PROTOCOL_VERSION: u32 = 5; +impl Ord for SessionCapabilityInfo { + fn cmp(&self, b: &SessionCapabilityInfo) -> Ordering { + // By protocol id first + if self.protocol != b.protocol { + return self.protocol.cmp(&b.protocol); + } + // By version + self.version.cmp(&b.version) + } +} + +/// Network service configuration +#[derive(Debug, PartialEq, Clone)] +pub struct NetworkConfiguration { + /// Directory path to store general network configuration. None means nothing will be saved + pub config_path: Option, + /// Directory path to store network-specific configuration. None means nothing will be saved + pub net_config_path: Option, + /// IP address to listen for incoming connections. Listen to all connections by default + pub listen_address: Option, + /// IP address to advertise. Detected automatically if none. + pub public_address: Option, + /// Port for UDP connections, same as TCP by default + pub udp_port: Option, + /// Enable NAT configuration + pub nat_enabled: bool, + /// Enable discovery + pub discovery_enabled: bool, + /// List of initial node addresses + pub boot_nodes: Vec, + /// Use provided node key instead of default + pub use_secret: Option, + /// Minimum number of connected peers to maintain + pub min_peers: u32, + /// Maximum allowed number of peers + pub max_peers: u32, + /// Maximum handshakes + pub max_handshakes: u32, + /// Reserved protocols. Peers with protocol get additional connection slots. + pub reserved_protocols: HashMap, + /// List of reserved node addresses. + pub reserved_nodes: Vec, + /// The non-reserved peer mode. + pub non_reserved_mode: NonReservedPeerMode, + /// IP filter + pub ip_filter: IpFilter, + /// Client identifier + pub client_version: String, +} + +impl Default for NetworkConfiguration { + fn default() -> Self { + NetworkConfiguration::new() + } +} + +impl NetworkConfiguration { + /// Create a new instance of default settings. + pub fn new() -> Self { + NetworkConfiguration { + config_path: None, + net_config_path: None, + listen_address: None, + public_address: None, + udp_port: None, + nat_enabled: true, + discovery_enabled: true, + boot_nodes: Vec::new(), + use_secret: None, + min_peers: 25, + max_peers: 50, + max_handshakes: 64, + reserved_protocols: HashMap::new(), + ip_filter: IpFilter::default(), + reserved_nodes: Vec::new(), + non_reserved_mode: NonReservedPeerMode::Accept, + client_version: "Parity-network".into(), + } + } + + /// Create new default configuration with sepcified listen port. + pub fn new_with_port(port: u16) -> NetworkConfiguration { + let mut config = NetworkConfiguration::new(); + config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port))); + config + } + + /// Create new default configuration for localhost-only connection with random port (usefull for testing) + pub fn new_local() -> NetworkConfiguration { + let mut config = NetworkConfiguration::new(); + config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0))); + config.nat_enabled = false; + config + } +} + +/// IO access point. This is passed to all IO handlers and provides an interface to the IO subsystem. +pub trait NetworkContext { + /// Send a packet over the network to another peer. + fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec) -> Result<(), Error>; + + /// Send a packet over the network to another peer using specified protocol. + fn send_protocol(&self, protocol: ProtocolId, peer: PeerId, packet_id: PacketId, data: Vec) -> Result<(), Error>; + + /// Respond to a current network message. Panics if no there is no packet in the context. If the session is expired returns nothing. + fn respond(&self, packet_id: PacketId, data: Vec) -> Result<(), Error>; + + /// Get an IoChannel. + fn io_channel(&self) -> IoChannel; + + /// Disconnect a peer and prevent it from connecting again. + fn disable_peer(&self, peer: PeerId); + + /// Disconnect peer. Reconnect can be attempted later. + fn disconnect_peer(&self, peer: PeerId); + + /// Check if the session is still active. + fn is_expired(&self) -> bool; + + /// Register a new IO timer. 'IoHandler::timeout' will be called with the token. + fn register_timer(&self, token: TimerToken, ms: u64) -> Result<(), Error>; + + /// Returns peer identification string + fn peer_client_version(&self, peer: PeerId) -> String; + + /// Returns information on p2p session + fn session_info(&self, peer: PeerId) -> Option; + + /// Returns max version for a given protocol. + fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option; + + /// Returns this object's subprotocol name. + fn subprotocol_name(&self) -> ProtocolId; +} + +impl<'a, T> NetworkContext for &'a T where T: ?Sized + NetworkContext { + fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec) -> Result<(), Error> { + (**self).send(peer, packet_id, data) + } + + fn send_protocol(&self, protocol: ProtocolId, peer: PeerId, packet_id: PacketId, data: Vec) -> Result<(), Error> { + (**self).send_protocol(protocol, peer, packet_id, data) + } + + fn respond(&self, packet_id: PacketId, data: Vec) -> Result<(), Error> { + (**self).respond(packet_id, data) + } + + fn io_channel(&self) -> IoChannel { + (**self).io_channel() + } + + fn disable_peer(&self, peer: PeerId) { + (**self).disable_peer(peer) + } + + fn disconnect_peer(&self, peer: PeerId) { + (**self).disconnect_peer(peer) + } + + fn is_expired(&self) -> bool { + (**self).is_expired() + } + + fn register_timer(&self, token: TimerToken, ms: u64) -> Result<(), Error> { + (**self).register_timer(token, ms) + } + + fn peer_client_version(&self, peer: PeerId) -> String { + (**self).peer_client_version(peer) + } + + fn session_info(&self, peer: PeerId) -> Option { + (**self).session_info(peer) + } + + fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option { + (**self).protocol_version(protocol, peer) + } + + fn subprotocol_name(&self) -> ProtocolId { + (**self).subprotocol_name() + } +} + +pub trait HostInfo { + /// Returns public key + fn id(&self) -> &NodeId; + /// Returns secret key + fn secret(&self) -> &Secret; + /// Increments and returns connection nonce. + fn next_nonce(&mut self) -> H256; + /// Returns the client version. + fn client_version(&self) -> &str; +} /// Network IO protocol handler. This needs to be implemented for each new subprotocol. /// All the handler function are called from within IO event loop. @@ -157,7 +384,6 @@ impl NonReservedPeerMode { } #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] pub struct IpFilter { pub predefined: AllowIP, pub custom_allow: Vec, diff --git a/whisper/src/net/mod.rs b/whisper/src/net/mod.rs index c4774b788f7..621da8fa1d7 100644 --- a/whisper/src/net/mod.rs +++ b/whisper/src/net/mod.rs @@ -392,7 +392,7 @@ pub trait Context { fn send(&self, PeerId, u8, Vec); } -impl<'a> Context for NetworkContext<'a> { +impl Context for T where T: ?Sized + NetworkContext { fn disconnect_peer(&self, peer: PeerId) { NetworkContext::disconnect_peer(self, peer); } @@ -437,7 +437,7 @@ impl Network { } /// Post a message to the whisper network to be relayed. - pub fn post_message(&self, message: Message, context: &C) -> bool + pub fn post_message(&self, message: Message, context: &C) -> bool where T: MessageHandler { let ok = self.messages.write().insert(message); @@ -452,7 +452,7 @@ impl Network { } impl Network { - fn rally(&self, io: &C) { + fn rally(&self, io: &C) { // cannot be greater than 16MB (protocol limitation) const MAX_MESSAGES_PACKET_SIZE: usize = 8 * 1024 * 1024; @@ -627,7 +627,7 @@ impl Network { Ok(()) } - fn on_connect(&self, io: &C, peer: &PeerId) { + fn on_connect(&self, io: &C, peer: &PeerId) { trace!(target: "whisper", "Connecting peer {}", peer); let node_key = match io.node_key(*peer) { @@ -660,7 +660,7 @@ impl Network { io.send(*peer, packet::STATUS, ::rlp::EMPTY_LIST_RLP.to_vec()); } - fn on_packet(&self, io: &C, peer: &PeerId, packet_id: u8, data: &[u8]) { + fn on_packet(&self, io: &C, peer: &PeerId, packet_id: u8, data: &[u8]) { let rlp = UntrustedRlp::new(data); let res = match packet_id { packet::STATUS => self.on_status(peer, rlp), @@ -708,7 +708,7 @@ impl ::network::NetworkProtocolHandler for Network { // rally with each peer and handle timeouts. match timer { RALLY_TOKEN => self.rally(io), - other => debug!(target: "whisper", "Timout triggered on unknown token {}", other), + other => debug!(target: "whisper", "Timeout triggered on unknown token {}", other), } } } From c85e4d6f0eff0f3c5140aeed0f9dc7ae2f6a8a74 Mon Sep 17 00:00:00 2001 From: Fredrik Date: Mon, 5 Mar 2018 18:18:21 +0100 Subject: [PATCH 11/11] Change port because testing environment didn't like those ports --- util/network-devp2p/src/discovery.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/network-devp2p/src/discovery.rs b/util/network-devp2p/src/discovery.rs index a04175d053f..5e22e31d53a 100644 --- a/util/network-devp2p/src/discovery.rs +++ b/util/network-devp2p/src/discovery.rs @@ -711,8 +711,8 @@ mod tests { fn test_ping() { let key1 = Random.generate().unwrap(); let key2 = Random.generate().unwrap(); - let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40444").unwrap(), udp_port: 40444 }; - let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40445").unwrap(), udp_port: 40445 }; + let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40344").unwrap(), udp_port: 40344 }; + let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40345").unwrap(), udp_port: 40345 }; let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0, IpFilter::default()); let mut discovery2 = Discovery::new(&key2, ep2.address.clone(), ep2.clone(), 0, IpFilter::default());