From e3c469527489dc0ad2d8bf6afe80d2dad481daa0 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 19 Sep 2016 11:29:50 +0200 Subject: [PATCH 01/69] stub implementations of light client trait --- ethcore/src/light/client.rs | 95 +++++++++++++++++++++++++++++++++++++ ethcore/src/light/mod.rs | 18 +++++++ util/fetch/src/lib.rs | 2 +- 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 ethcore/src/light/client.rs create mode 100644 ethcore/src/light/mod.rs diff --git a/ethcore/src/light/client.rs b/ethcore/src/light/client.rs new file mode 100644 index 000000000..de79ad922 --- /dev/null +++ b/ethcore/src/light/client.rs @@ -0,0 +1,95 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! Light client implementation. Used for raw data queries as well as the header +//! sync. + +use std::sync::Arc; + +use engines::Engine; +use ids::BlockID; +use miner::TransactionQueue; +use service::ClientIoMessage; +use block_import_error::BlockImportError; +use block_status::BlockStatus; +use verification::queue::{Config as QueueConfig, HeaderQueue, QueueInfo, Status}; +use transaction::SignedTransaction; + +use super::provider::{CHTProofRequest, Provider, ProofRequest}; + +use io::IoChannel; +use util::hash::H256; +use util::Bytes; + +/// A light client. +pub struct Client { + engine: Arc, + header_queue: HeaderQueue, + message_channel: IoChannel, + transaction_queue: TransactionQueue, +} + +impl Client { + /// Import a header as rlp-encoded bytes. + fn import_header(&self, bytes: Bytes) -> Result { + let header = ::rlp::decode(&bytes); + + self.header_queue.import(header).map_err(Into::into) + } + + /// Whether the block is already known (but not necessarily part of the canonical chain) + fn is_known(&self, id: BlockID) -> bool { + false + } + + /// Fetch a vector of all pending transactions. + fn pending_transactions(&self) -> Vec { + self.transaction_queue.top_transactions() + } + + /// Inquire about the status of a given block. + fn status(&self, id: BlockID) -> BlockStatus { + BlockStatus::Unknown + } +} + +// stub implementation, can be partially implemented using LRU-caches +// but will almost definitely never be able to provide complete responses. +impl Provider for Client { + fn block_headers(&self, _block: H256, _skip: usize, _max: usize, _reverse: bool) -> Vec { + vec![] + } + + fn block_bodies(&self, _blocks: Vec) -> Vec { + vec![] + } + + fn receipts(&self, _blocks: Vec) -> Vec { + vec![] + } + + fn proofs(&self, _requests: Vec<(H256, ProofRequest)>) -> Vec { + vec![] + } + + fn code(&self, _accounts: Vec<(H256, H256)>) -> Vec { + vec![] + } + + fn header_proofs(&self, _requests: Vec) -> Vec { + vec![] + } +} \ No newline at end of file diff --git a/ethcore/src/light/mod.rs b/ethcore/src/light/mod.rs new file mode 100644 index 000000000..100548b46 --- /dev/null +++ b/ethcore/src/light/mod.rs @@ -0,0 +1,18 @@ +//! Light client logic and implementation. +//! +//! A "light" client stores very little chain-related data locally +//! unlike a full node, which stores all blocks, headers, receipts, and more. +//! +//! This enables the client to have a much lower resource footprint in +//! exchange for the cost of having to ask the network for state data +//! while responding to queries. This makes a light client unsuitable for +//! low-latency applications, but perfectly suitable for simple everyday +//! use-cases like sending transactions from a personal account. +//! +//! It starts by performing a header-only sync, verifying every header in +//! the chain. + +mod provider; +mod client; + +pub use self::provider::{CHTProofRequest, ProofRequest, Provider}; \ No newline at end of file diff --git a/util/fetch/src/lib.rs b/util/fetch/src/lib.rs index 8ec9e0ddd..7ab38604b 100644 --- a/util/fetch/src/lib.rs +++ b/util/fetch/src/lib.rs @@ -26,4 +26,4 @@ extern crate rand; pub mod client; pub mod fetch_file; -pub use self::client::{Client, Fetch, FetchError, FetchResult}; +pub use self::client::{Client, Fetch, FetchError, FetchResult}; \ No newline at end of file From ed06572bd472219afc2f2f5bda9240eb4261a5ef Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 14 Sep 2016 19:23:29 +0200 Subject: [PATCH 02/69] Light provider trait --- ethcore/src/light/mod.rs | 2 +- ethcore/src/light/provider.rs | 57 ++++++++++++++++++++++++++++++ ethcore/src/types/mod.rs.in | 1 + ethcore/src/types/proof_request.rs | 44 +++++++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 ethcore/src/light/provider.rs create mode 100644 ethcore/src/types/proof_request.rs diff --git a/ethcore/src/light/mod.rs b/ethcore/src/light/mod.rs index 100548b46..32d4e9d8a 100644 --- a/ethcore/src/light/mod.rs +++ b/ethcore/src/light/mod.rs @@ -15,4 +15,4 @@ mod provider; mod client; -pub use self::provider::{CHTProofRequest, ProofRequest, Provider}; \ No newline at end of file +pub use self::provider::{CHTProofRequest, ProofRequest, Provider}; diff --git a/ethcore/src/light/provider.rs b/ethcore/src/light/provider.rs new file mode 100644 index 000000000..4833184eb --- /dev/null +++ b/ethcore/src/light/provider.rs @@ -0,0 +1,57 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! A provider for the LES protocol. This is typically a full node, who can +//! give as much data as necessary to its peers. + +pub use proof_request::{CHTProofRequest, ProofRequest}; + +use util::Bytes; +use util::hash::H256; + +/// Defines the operations that a provider for `LES` must fulfill. +/// +/// These are defined at [1], but may be subject to change. +/// +/// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) +pub trait Provider { + /// Provide a list of headers starting at the requested block, + /// possibly in reverse and skipping `skip` at a time. + /// + /// The returned vector may have any length in the range [0, `max`], but the + /// results within must adhere to the `skip` and `reverse` parameters. + fn block_headers(&self, block: H256, skip: usize, max: usize, reverse: bool) -> Vec; + + /// Provide as many as possible of the requested blocks (minus the headers) encoded + /// in RLP format. + fn block_bodies(&self, blocks: Vec) -> Vec; + + /// Provide the receipts as many as possible of the requested blocks. + /// Returns a vector of RLP-encoded lists of receipts. + fn receipts(&self, blocks: Vec) -> Vec; + + /// Provide a set of merkle proofs, as requested. Each request is a + /// block hash and request parameters. + /// + /// Returns a vector to RLP-encoded lists satisfying the requests. + fn proofs(&self, requests: Vec<(H256, ProofRequest)>) -> Vec; + + /// Provide contract code for the specified (block_hash, account_hash) pairs. + fn code(&self, accounts: Vec<(H256, H256)>) -> Vec; + + /// Provide header proofs from the Canonical Hash Tries. + fn header_proofs(&self, requests: Vec) -> Vec; +} \ No newline at end of file diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in index 32c7faabe..c2537135d 100644 --- a/ethcore/src/types/mod.rs.in +++ b/ethcore/src/types/mod.rs.in @@ -33,3 +33,4 @@ pub mod transaction_import; pub mod block_import_error; pub mod restoration_status; pub mod snapshot_manifest; +pub mod proof_request; \ No newline at end of file diff --git a/ethcore/src/types/proof_request.rs b/ethcore/src/types/proof_request.rs new file mode 100644 index 000000000..20bddce21 --- /dev/null +++ b/ethcore/src/types/proof_request.rs @@ -0,0 +1,44 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! Merkle proof request. +use util::hash::H256; + +/// A request for a state merkle proof. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub enum ProofRequest { + /// Request a proof of the given account's (denoted by sha3(address)) + /// node in the state trie. Nodes with depth less than the second item + /// may be omitted. + Account(H256, usize), + + /// Request a proof for a key in the given account's storage trie. + /// Both values are hashes of their actual values. Nodes with depth + /// less than the third item may be omitted. + Storage(H256, H256, usize), +} + +/// A request for a Canonical Hash Trie proof for the given block number. +/// Nodes with depth less than the second item may be omitted. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct CHTProofRequest { + /// The number of the block the proof is requested for. + /// The CHT's number can be deduced from this (`number` / 4096) + pub number: u64, + + /// Nodes with depth less than this can be omitted from the proof. + pub depth: usize, +} \ No newline at end of file From 8d7244c09fe2c796b62f62863fab6627bca964df Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 5 Oct 2016 15:35:31 +0200 Subject: [PATCH 03/69] light client sync stubs --- ethcore/src/light/client.rs | 40 ++++++++----------------------------- ethcore/src/light/mod.rs | 1 + sync/src/lib.rs | 1 + sync/src/light/mod.rs | 27 +++++++++++++++++++++++++ sync/src/sync_io.rs | 4 ++-- 5 files changed, 39 insertions(+), 34 deletions(-) create mode 100644 sync/src/light/mod.rs diff --git a/ethcore/src/light/client.rs b/ethcore/src/light/client.rs index de79ad922..18635aa44 100644 --- a/ethcore/src/light/client.rs +++ b/ethcore/src/light/client.rs @@ -39,57 +39,33 @@ pub struct Client { engine: Arc, header_queue: HeaderQueue, message_channel: IoChannel, - transaction_queue: TransactionQueue, } impl Client { /// Import a header as rlp-encoded bytes. - fn import_header(&self, bytes: Bytes) -> Result { + pub fn import_header(&self, bytes: Bytes) -> Result { let header = ::rlp::decode(&bytes); self.header_queue.import(header).map_err(Into::into) } /// Whether the block is already known (but not necessarily part of the canonical chain) - fn is_known(&self, id: BlockID) -> bool { + pub fn is_known(&self, id: BlockID) -> bool { false } /// Fetch a vector of all pending transactions. - fn pending_transactions(&self) -> Vec { - self.transaction_queue.top_transactions() + pub fn pending_transactions(&self) -> Vec { + vec![] } /// Inquire about the status of a given block. - fn status(&self, id: BlockID) -> BlockStatus { + pub fn status(&self, id: BlockID) -> BlockStatus { BlockStatus::Unknown } -} -// stub implementation, can be partially implemented using LRU-caches -// but will almost definitely never be able to provide complete responses. -impl Provider for Client { - fn block_headers(&self, _block: H256, _skip: usize, _max: usize, _reverse: bool) -> Vec { - vec![] - } - - fn block_bodies(&self, _blocks: Vec) -> Vec { - vec![] - } - - fn receipts(&self, _blocks: Vec) -> Vec { - vec![] - } - - fn proofs(&self, _requests: Vec<(H256, ProofRequest)>) -> Vec { - vec![] - } - - fn code(&self, _accounts: Vec<(H256, H256)>) -> Vec { - vec![] - } - - fn header_proofs(&self, _requests: Vec) -> Vec { - vec![] + /// Get the header queue info. + pub fn queue_info(&self) -> QueueInfo { + self.header_queue.queue_info() } } \ No newline at end of file diff --git a/ethcore/src/light/mod.rs b/ethcore/src/light/mod.rs index 32d4e9d8a..22b446074 100644 --- a/ethcore/src/light/mod.rs +++ b/ethcore/src/light/mod.rs @@ -15,4 +15,5 @@ mod provider; mod client; +pub use self::client::Client; pub use self::provider::{CHTProofRequest, ProofRequest, Provider}; diff --git a/sync/src/lib.rs b/sync/src/lib.rs index d2c6e2583..e13e3b8b1 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -50,6 +50,7 @@ mod chain; mod blocks; mod sync_io; mod snapshot; +mod light; #[cfg(test)] mod tests; diff --git a/sync/src/light/mod.rs b/sync/src/light/mod.rs new file mode 100644 index 000000000..13ba38c5a --- /dev/null +++ b/sync/src/light/mod.rs @@ -0,0 +1,27 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! LES Protocol Version 1 implementation. +//! +//! This uses a "Provider" to answer requests. + +use ethcore::light::{Client, Provider}; + +/// This handles synchronization of the header chain for a light client. +pub struct Chain { + client: Client, + peers: Vec, +} \ No newline at end of file diff --git a/sync/src/sync_io.rs b/sync/src/sync_io.rs index 445939399..867a75f38 100644 --- a/sync/src/sync_io.rs +++ b/sync/src/sync_io.rs @@ -18,7 +18,7 @@ use network::{NetworkContext, PeerId, PacketId, NetworkError}; use ethcore::client::BlockChainClient; use ethcore::snapshot::SnapshotService; -/// IO interface for the syning handler. +/// IO interface for the syncing handler. /// Provides peer connection management and an interface to the blockchain client. // TODO: ratings pub trait SyncIo { @@ -38,7 +38,7 @@ pub trait SyncIo { fn peer_info(&self, peer_id: PeerId) -> String { peer_id.to_string() } - /// Maximum mutuallt supported ETH protocol version + /// Maximum mutually supported ETH protocol version fn eth_protocol_version(&self, peer_id: PeerId) -> u8; /// Returns if the chain block queue empty fn is_chain_queue_empty(&self) -> bool { From 91f0c6896c7a1211163a1f75e2f3a0f7a082d8c9 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 10 Oct 2016 18:48:47 +0200 Subject: [PATCH 04/69] LES boilerplate --- sync/src/light/mod.rs | 294 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 292 insertions(+), 2 deletions(-) diff --git a/sync/src/light/mod.rs b/sync/src/light/mod.rs index 13ba38c5a..59ffceda6 100644 --- a/sync/src/light/mod.rs +++ b/sync/src/light/mod.rs @@ -16,12 +16,302 @@ //! LES Protocol Version 1 implementation. //! -//! This uses a "Provider" to answer requests. +//! This uses a "Provider" to answer requests and syncs to a `Client`. +//! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) use ethcore::light::{Client, Provider}; +use io::TimerToken; +use network::{NetworkProtocolHandler, NetworkService, NetworkContext, NetworkError, PeerId}; +use rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View}; +use util::hash::H256; + +use std::collections::HashMap; + +const TIMEOUT: TimerToken = 0; +const TIMEOUT_INTERVAL_MS: u64 = 1000; + +// LPV1 +const PROTOCOL_VERSION: u32 = 1; + +// TODO [rob] make configurable. +const PROTOCOL_ID: [u8; 3] = *b"les"; + +// TODO [rob] Buffer flow. + +// packet ID definitions. +mod packet { + // the status packet. + pub const STATUS: u8 = 0x00; + + // broadcast that new block hashes have appeared. + pub const NEW_BLOCK_HASHES: u8 = 0x01; + + // request and response for block headers + pub const GET_BLOCK_HEADERS: u8 = 0x02; + pub const BLOCK_HEADERS: u8 = 0x03; + + // request and response for block bodies + pub const GET_BLOCK_BODIES: u8 = 0x04; + pub const BLOCK_BODIES: u8 = 0x05; + + // request and response for transaction receipts. + pub const GET_RECEIPTS: u8 = 0x06; + pub const RECEIPTS: u8 = 0x07; + + // request and response for merkle proofs. + pub const GET_PROOFS: u8 = 0x08; + pub const PROOFS: u8 = 0x09; + + // request and response for contract code. + pub const GET_CONTRACT_CODES: u8 = 0x0a; + pub const CONTRACT_CODES: u8 = 0x0b; + + // relay transactions to peers. + pub const SEND_TRANSACTIONS: u8 = 0x0c; + + // request and response for header proofs in a CHT. + pub const GET_HEADER_PROOFS: u8 = 0x0d; + pub const HEADER_PROOFS: u8 = 0x0e; + + // broadcast dynamic capabilities. + pub const CAPABILITIES: u8 = 0x0f; + + // request and response for block-level state deltas. + pub const GET_BLOCK_DELTAS: u8 = 0x10; + pub const BLOCK_DELTAS: u8 = 0x11; + + // request and response for transaction proofs. + pub const GET_TRANSACTION_PROOFS: u8 = 0x12; + pub const TRANSACTION_PROOFS: u8 = 0x13; +} +// which direction a request should go in. +enum Direction { + Forwards, + Reverse, +} + +// A request made to a peer. +enum Request { + // a request for headers: + // (number, hash), maximum, skip, direction. + Headers((u64, H256), u64, u64, Direction), + // a request for block bodies by hashes. + Bodies(Vec), + // a request for tx receipts by hashes. + Receipts(Vec), + // a request for contract code by (block hash, address hash). + Code(Vec<(H256, H256)>), +} + +// data about each peer. +struct Peer { + pending_requests: HashMap, // requests pending for peer. + buffer: u64, // remaining buffer value. +} /// This handles synchronization of the header chain for a light client. pub struct Chain { client: Client, - peers: Vec, + genesis_hash: H256, + mainnet: bool, + peers: RwLock>, +} + +impl Chain { + // called when a peer connects. + fn on_connect(&self, peer: &PeerId, io: &NetworkContext) { + let peer = *peer; + match self.send_status(peer, io) { + Ok(()) => { + self.peers.write().insert(peer, Peer); + } + Err(e) => { + trace!(target: "les", "Error while sending status: {}", e); + io.disable_peer(peer); + } + } + } + + // called when a peer disconnects. + fn on_disconnect(&self, peer: PeerId) { + self.peers.write().remove(peer); + } + + fn send_status(&self, peer: PeerId, io: &NetworkContext) -> Result<(), NetworkError> { + let chain_info = self.client.chain_info(); + + // TODO [rob] use optional keys too. + let mut stream = RlpStream::new_list(6); + stream + .new_list(2) + .append("protocolVersion") + .append(&PROTOCOL_VERSION) + .new_list(2) + .append("networkId") + .append(&(self.mainnet as u8)) + .new_list(2) + .append("headTd") + .append(&chain_info.total_difficulty) + .new_list(2) + .append("headHash") + .append(&chain_info.best_block_hash) + .new_list(2) + .append("headNum") + .append(&chain_info.best_block_number) + .new_list(2) + .append("genesisHash") + .append(&self.genesis_hash); + + io.send(peer, packet::STATUS, stream.out()) + } + + fn status(&self, peer: &PeerId, io: &NetworkContext) { + unimplemented!() + } + + // Handle a new block hashes message. + fn new_block_hashes(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + const MAX_NEW_HASHES: usize = 256; + + unimplemented!() + } + + // Handle a request for block headers. + fn get_block_headers(&self, peers: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive a response for block headers. + fn block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Handle a request for block bodies. + fn get_block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive a response for block bodies. + fn block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Handle a request for proofs. + fn get_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive a response for proofs. + fn proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Handle a request for contract code. + fn get_contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive a response for contract code. + fn contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Handle a request for header proofs + fn get_header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive a response for header proofs + fn header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive a set of transactions to relay. + fn relay_transactions(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive updated capabilities from a peer. + fn capabilities(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Handle a request for block deltas. + fn get_block_deltas(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive block deltas. + fn block_deltas(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Handle a request for transaction proofs. + fn get_transaction_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive transaction proofs. + fn transaction_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } +} + +impl NetworkProtocolHandler for Chain { + fn initialize(&self, io: &NetworkContext) { + io.register_timer(TIMEOUT, TIMEOUT_INTERVAL_MS).expect("Error registering sync timer."); + } + + fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { + let rlp = UntrustedRlp::new(data); + match packet_id { + packet::STATUS => self.status(peer, io, rlp), + packet::NEW_BLOCK_HASHES => self.new_block_hashes(peer, io, rlp), + + packet::GET_BLOCK_HEADERS => self.get_block_headers(peer, io, rlp), + packet::BLOCK_HEADERS => self.block_headers(peer, io, rlp), + + packet::GET_BLOCK_BODIES => self.get_block_bodies(peer, io, rlp), + packet::BLOCK_BODIES => self.block_bodies(peer, io, rlp), + + packet::GET_RECEIPTS => self.get_receipts(peer, io, rlp), + packet::RECEIPTS => self.receipt(peer, io, rlp), + + packet::GET_PROOFS => self.get_proofs(peer, io, rlp), + packet::PROOFS => self.proofs(peer, io, rlp), + + packet::GET_CONTRACT_CODES => self.get_contract_code(peer, io, rlp), + packet::CONTRACT_CODES => self.contract_code(peer, io, rlp), + + packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), + packet::CAPABILITIES => self.capabilities(peer, io, rlp), + + packet::GET_HEADER_PROOFS => self.get_header_proofs(peer, io, rlp), + packet::HEADER_PROOFS => self.header_proofs(peer, io, rlp), + + packet::GET_BLOCK_DELTAS => self.get_block_deltas(peer, io, rlp), + packet::BLOCK_DELTAS => self.block_deltas(peer, io, rlp), + + packet::GET_TRANSACTION_PROOFS => self.get_transaction_proofs(peer, io, rlp), + packet::TRANSACTION_PROOFS => self.transaction_proofs(peer, io, rlp), + } + } + + fn connected(&self, io: &NetworkContext, peer: &PeerId) { + self.on_connect(peer, io); + } + + fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { + self.on_disconnect(peer, io); + } + + fn timeout(&self, io: &NetworkContext, timer: TimerToken) { + match timer { + TIMEOUT => { + // broadcast transactions to peers. + // update buffer flow. + } + _ => warn!(target: "les", "received timeout on unknown token {}", timer), + } + } } \ No newline at end of file From a7e420d9ecd088b49337de1f771df50f8af390ba Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 27 Oct 2016 15:09:17 +0200 Subject: [PATCH 05/69] stub implementation of provider for client --- ethcore/src/lib.rs | 1 + ethcore/src/light/client.rs | 32 +++++++++++++++++++++++++++++++- ethcore/src/light/mod.rs | 21 +++++++++++++++++++-- ethcore/src/light/provider.rs | 6 ++++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 9985dc58e..cd32da999 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -136,6 +136,7 @@ pub mod miner; pub mod snapshot; pub mod action_params; pub mod db; +pub mod light; #[macro_use] pub mod evm; mod cache_manager; diff --git a/ethcore/src/light/client.rs b/ethcore/src/light/client.rs index 18635aa44..a4ff2531f 100644 --- a/ethcore/src/light/client.rs +++ b/ethcore/src/light/client.rs @@ -34,7 +34,7 @@ use io::IoChannel; use util::hash::H256; use util::Bytes; -/// A light client. +/// Light client implementation. pub struct Client { engine: Arc, header_queue: HeaderQueue, @@ -68,4 +68,34 @@ impl Client { pub fn queue_info(&self) -> QueueInfo { self.header_queue.queue_info() } +} + +impl Provider for Client { + fn block_headers(&self, block: H256, skip: usize, max: usize, reverse: bool) -> Vec { + Vec::new() + } + + fn block_bodies(&self, blocks: Vec) -> Vec { + Vec::new() + } + + fn receipts(&self, blocks: Vec) -> Vec { + Vec::new() + } + + fn proofs(&self, requests: Vec<(H256, ProofRequest)>) -> Vec { + Vec::new() + } + + fn code(&self, accounts: Vec<(H256, H256)>) -> Vec { + Vec::new() + } + + fn header_proofs(&self, requests: Vec) -> Vec { + Vec::new() + } + + fn pending_transactions(&self) -> Vec { + Vec::new() + } } \ No newline at end of file diff --git a/ethcore/src/light/mod.rs b/ethcore/src/light/mod.rs index 22b446074..863f35433 100644 --- a/ethcore/src/light/mod.rs +++ b/ethcore/src/light/mod.rs @@ -1,3 +1,19 @@ +// Copyright 2015, 2016 Ethcore (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 . + //! Light client logic and implementation. //! //! A "light" client stores very little chain-related data locally @@ -12,8 +28,9 @@ //! It starts by performing a header-only sync, verifying every header in //! the chain. -mod provider; mod client; +mod provider; +mod sync; pub use self::client::Client; -pub use self::provider::{CHTProofRequest, ProofRequest, Provider}; +pub use self::provider::{CHTProofRequest, ProofRequest, Provider}; \ No newline at end of file diff --git a/ethcore/src/light/provider.rs b/ethcore/src/light/provider.rs index 4833184eb..05859c78b 100644 --- a/ethcore/src/light/provider.rs +++ b/ethcore/src/light/provider.rs @@ -19,12 +19,15 @@ pub use proof_request::{CHTProofRequest, ProofRequest}; +use transaction::SignedTransaction; + use util::Bytes; use util::hash::H256; /// Defines the operations that a provider for `LES` must fulfill. /// /// These are defined at [1], but may be subject to change. +/// Requests which can't be fulfilled should return an empty RLP list. /// /// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) pub trait Provider { @@ -54,4 +57,7 @@ pub trait Provider { /// Provide header proofs from the Canonical Hash Tries. fn header_proofs(&self, requests: Vec) -> Vec; + + /// Provide pending transactions. + fn pending_transactions(&self) -> Vec; } \ No newline at end of file From 3a5843c40f208ab9715e3507ffcf30c46b5ec694 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 27 Oct 2016 15:45:59 +0200 Subject: [PATCH 06/69] skeleton and request traits --- ethcore/src/light/client.rs | 5 ++ ethcore/src/light/sync.rs | 33 +++++++++ sync/src/chain.rs | 12 ++-- sync/src/light/mod.rs | 53 ++++++++------ sync/src/light/request.rs | 138 ++++++++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+), 28 deletions(-) create mode 100644 ethcore/src/light/sync.rs create mode 100644 sync/src/light/request.rs diff --git a/ethcore/src/light/client.rs b/ethcore/src/light/client.rs index a4ff2531f..7f061e3ab 100644 --- a/ethcore/src/light/client.rs +++ b/ethcore/src/light/client.rs @@ -68,6 +68,11 @@ impl Client { pub fn queue_info(&self) -> QueueInfo { self.header_queue.queue_info() } + + /// Get the chain info. + pub fn chain_info(&self) -> ChainInfo { + + } } impl Provider for Client { diff --git a/ethcore/src/light/sync.rs b/ethcore/src/light/sync.rs new file mode 100644 index 000000000..15ed5673c --- /dev/null +++ b/ethcore/src/light/sync.rs @@ -0,0 +1,33 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! A light sync target. + +use block_import_error::BlockImportError; +use client::BlockChainInfo; + +use util::hash::H256; + +pub trait Sync { + /// Whether syncing is enabled. + fn enabled(&self) -> bool; + + /// Current chain info. + fn chain_info(&self) -> BlockChainInfo; + + /// Import a header. + fn import_header(&self, header_bytes: Vec) -> Result; +} \ No newline at end of file diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 916e7424e..344ed8c41 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -209,8 +209,8 @@ pub struct SyncStatus { impl SyncStatus { /// Indicates if snapshot download is in progress pub fn is_snapshot_syncing(&self) -> bool { - self.state == SyncState::SnapshotManifest - || self.state == SyncState::SnapshotData + self.state == SyncState::SnapshotManifest + || self.state == SyncState::SnapshotData || self.state == SyncState::SnapshotWaiting } @@ -453,7 +453,7 @@ impl ChainSync { self.init_downloaders(io.chain()); self.reset_and_continue(io); } - + /// Restart sync after bad block has been detected. May end up re-downloading up to QUEUE_SIZE blocks fn init_downloaders(&mut self, chain: &BlockChainClient) { // Do not assume that the block queue/chain still has our last_imported_block @@ -1150,9 +1150,9 @@ impl ChainSync { } }, BlockSet::OldBlocks => { - if self.old_blocks.as_mut().map_or(false, |downloader| { downloader.collect_blocks(io, false) == Err(DownloaderImportError::Invalid) }) { - self.restart(io); - } else if self.old_blocks.as_ref().map_or(false, |downloader| { downloader.is_complete() }) { + if self.old_blocks.as_mut().map_or(false, |downloader| { downloader.collect_blocks(io, false) == Err(DownloaderImportError::Invalid) }) { + self.restart(io); + } else if self.old_blocks.as_ref().map_or(false, |downloader| { downloader.is_complete() }) { trace!(target: "sync", "Background block download is complete"); self.old_blocks = None; } diff --git a/sync/src/light/mod.rs b/sync/src/light/mod.rs index 59ffceda6..fe60c30e5 100644 --- a/sync/src/light/mod.rs +++ b/sync/src/light/mod.rs @@ -24,8 +24,10 @@ use io::TimerToken; use network::{NetworkProtocolHandler, NetworkService, NetworkContext, NetworkError, PeerId}; use rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; +use parking_lot::{Mutex, RwLock}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; +use std::sync::atomic::{AtomicUsize, Ordering}; const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; @@ -84,29 +86,18 @@ mod packet { pub const GET_TRANSACTION_PROOFS: u8 = 0x12; pub const TRANSACTION_PROOFS: u8 = 0x13; } -// which direction a request should go in. -enum Direction { - Forwards, - Reverse, -} -// A request made to a peer. -enum Request { - // a request for headers: - // (number, hash), maximum, skip, direction. - Headers((u64, H256), u64, u64, Direction), - // a request for block bodies by hashes. - Bodies(Vec), - // a request for tx receipts by hashes. - Receipts(Vec), - // a request for contract code by (block hash, address hash). - Code(Vec<(H256, H256)>), +struct Request; + +struct Requested { + timestamp: usize, + total: Request, } // data about each peer. struct Peer { - pending_requests: HashMap, // requests pending for peer. buffer: u64, // remaining buffer value. + current_asking: HashSet, // pending request ids. } /// This handles synchronization of the header chain for a light client. @@ -115,15 +106,25 @@ pub struct Chain { genesis_hash: H256, mainnet: bool, peers: RwLock>, + pending_requests: RwLock>, + req_id: AtomicUsize, } impl Chain { + // make a request to a given peer. + fn request_from(&self, peer: &PeerId, req: Request) { + unimplemented!() + } + // called when a peer connects. fn on_connect(&self, peer: &PeerId, io: &NetworkContext) { let peer = *peer; match self.send_status(peer, io) { Ok(()) => { - self.peers.write().insert(peer, Peer); + self.peers.write().insert(peer, Peer { + buffer: 0, + current_asking: HashSet::new(), + }); } Err(e) => { trace!(target: "les", "Error while sending status: {}", e); @@ -133,8 +134,9 @@ impl Chain { } // called when a peer disconnects. - fn on_disconnect(&self, peer: PeerId) { - self.peers.write().remove(peer); + fn on_disconnect(&self, peer: PeerId, io: &NetworkContext) { + // TODO: reassign all requests assigned to this peer. + self.peers.write().remove(&peer); } fn send_status(&self, peer: PeerId, io: &NetworkContext) -> Result<(), NetworkError> { @@ -165,6 +167,11 @@ impl Chain { io.send(peer, packet::STATUS, stream.out()) } + /// Check on the status of all pending requests. + fn check_pending_requests(&self) { + unimplemented!() + } + fn status(&self, peer: &PeerId, io: &NetworkContext) { unimplemented!() } @@ -177,7 +184,9 @@ impl Chain { } // Handle a request for block headers. - fn get_block_headers(&self, peers: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + const MAX_HEADERS: usize = 512; + unimplemented!() } diff --git a/sync/src/light/request.rs b/sync/src/light/request.rs new file mode 100644 index 000000000..112cbcc42 --- /dev/null +++ b/sync/src/light/request.rs @@ -0,0 +1,138 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! LES request types. + +use util::bigint::prelude::*; +use rlp::*; + +/// An LES request. This defines its data format, and the format of its response type. +pub trait Request: Sized { + /// The response type of this request. + type Response: Response; + + /// The error type when decoding a response. + type Error; + + /// Whether this request is empty. + fn is_empty(&self) -> bool; + + /// The remainder of this request unfulfilled by the response. Required to return + /// an equivalent request when provided with an empty response. + fn remainder(&self, res: &Self::Response) -> Self; + + /// Attempt to parse raw data into a response object + /// or an error. Behavior undefined if the raw data didn't originate from + /// this request. + fn parse_response(&self, raw: &[u8]) -> Result; +} + +/// Request responses. These must have a combination operation used to fill the gaps +/// in one response with data from another. +pub trait Response: Sized { + /// Combine the two responses into one. This can only be relied on to behave correctly + /// if `other` is a response to a sub-request of the request this response was + /// produced from. + fn combine(&mut self, other: Self); +} + +/// A request for block bodies. +pub struct BlockBodies { + hashes: Vec, +} + +/// A response for block bodies. +pub struct BlockBodiesResponse { + bodies: Vec<(H256, Vec)>, +} + +impl Request for BlockBodies { + type Response = BlockBodiesResponse; + type Error = ::rlp::DecoderError; + + fn is_empty(&self) -> bool { self.hashes.is_empty() } + + fn remainder(&self, res: &Self::Response) -> Self { + let mut remaining = Vec::new(); + + let bodies = res.bodies.iter().map(|&(_, ref b) b).chain(::std::iter::repeat(&Vec::new())); + for (hash, body) in self.hashes.iter().zip(bodies) { + if body.is_empty() { + remaining.push(hash); + } + } + + BlockBodies { + hashes: remaining, + } + } + + fn parse_response(&self, raw: &[u8]) -> Result { + use ethcore::transaction::SignedTransaction; + use ethcore::header::Header; + + let rlp = UntrustedRlp::new(raw); + + let mut bodies = Vec::with_capacity(self.hashes.len()); + + let items = rlp.iter(); + for hash in self.hashes.iter().cloned() { + let res_bytes = match items.next() { + Some(rlp) => { + // perform basic block verification. + // TODO: custom error type? + try!(rlp.val_at::>(0) + .and_then(|_| rlp.val_at::>(1))); + + try!(rlp.data()).to_owned() + } + None => Vec::new(), + }; + + bodies.push((hash, res_bytes)); + } + + Ok(BlockBodiesResponse { + bodies: bodies, + }) + } +} + +impl Response for BlockBodiesResponse { + fn identity() -> Self { + BlockBodiesResponse { + bodies: Vec::new(), + } + } + + fn combine(&mut self, other: Self) { + let other_iter = other.bodies.into_iter(); + + 'a: + for &mut (ref my_hash, ref mut my_body) in self.bodies.iter_mut() { + loop { + match other_iter.next() { + Some((hash, body)) if hash == my_hash && !body.is_empty() => { + *my_body = body.to_owned(); + break + } + Some(_) => continue, + None => break 'a, + } + } + } + } +} \ No newline at end of file From eef9a355af9a69863a60e1e611837d1b4151e7fd Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 4 Nov 2016 17:19:01 +0100 Subject: [PATCH 07/69] request definitions --- ethcore/src/light/client.rs | 10 +- sync/src/light/mod.rs | 24 +++- sync/src/light/request.rs | 237 ++++++++++++++++++++---------------- 3 files changed, 156 insertions(+), 115 deletions(-) diff --git a/ethcore/src/light/client.rs b/ethcore/src/light/client.rs index 7f061e3ab..ae882b1a8 100644 --- a/ethcore/src/light/client.rs +++ b/ethcore/src/light/client.rs @@ -27,18 +27,19 @@ use block_import_error::BlockImportError; use block_status::BlockStatus; use verification::queue::{Config as QueueConfig, HeaderQueue, QueueInfo, Status}; use transaction::SignedTransaction; +use types::blockchain_info::BlockChainInfo; use super::provider::{CHTProofRequest, Provider, ProofRequest}; use io::IoChannel; use util::hash::H256; -use util::Bytes; +use util::{Bytes, Mutex}; /// Light client implementation. pub struct Client { engine: Arc, header_queue: HeaderQueue, - message_channel: IoChannel, + message_channel: Mutex>, } impl Client { @@ -70,11 +71,12 @@ impl Client { } /// Get the chain info. - pub fn chain_info(&self) -> ChainInfo { - + pub fn chain_info(&self) -> BlockChainInfo { + unimplemented!() } } +// dummy implementation -- may draw from canonical cache further on. impl Provider for Client { fn block_headers(&self, block: H256, skip: usize, max: usize, reverse: bool) -> Vec { Vec::new() diff --git a/sync/src/light/mod.rs b/sync/src/light/mod.rs index fe60c30e5..4c32d52ae 100644 --- a/sync/src/light/mod.rs +++ b/sync/src/light/mod.rs @@ -29,6 +29,10 @@ use parking_lot::{Mutex, RwLock}; use std::collections::{HashMap, HashSet}; use std::sync::atomic::{AtomicUsize, Ordering}; +use self::request::Request; + +mod request; + const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; @@ -87,11 +91,9 @@ mod packet { pub const TRANSACTION_PROOFS: u8 = 0x13; } -struct Request; - struct Requested { timestamp: usize, - total: Request, + req: Request, } // data about each peer. @@ -172,7 +174,7 @@ impl Chain { unimplemented!() } - fn status(&self, peer: &PeerId, io: &NetworkContext) { + fn status(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { unimplemented!() } @@ -205,6 +207,16 @@ impl Chain { unimplemented!() } + // Handle a request for receipts. + fn get_receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + + // Receive a response for receipts. + fn receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + unimplemented!() + } + // Handle a request for proofs. fn get_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { unimplemented!() @@ -284,7 +296,7 @@ impl NetworkProtocolHandler for Chain { packet::BLOCK_BODIES => self.block_bodies(peer, io, rlp), packet::GET_RECEIPTS => self.get_receipts(peer, io, rlp), - packet::RECEIPTS => self.receipt(peer, io, rlp), + packet::RECEIPTS => self.receipts(peer, io, rlp), packet::GET_PROOFS => self.get_proofs(peer, io, rlp), packet::PROOFS => self.proofs(peer, io, rlp), @@ -311,7 +323,7 @@ impl NetworkProtocolHandler for Chain { } fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { - self.on_disconnect(peer, io); + self.on_disconnect(*peer, io); } fn timeout(&self, io: &NetworkContext, timer: TimerToken) { diff --git a/sync/src/light/request.rs b/sync/src/light/request.rs index 112cbcc42..bbb91e415 100644 --- a/sync/src/light/request.rs +++ b/sync/src/light/request.rs @@ -14,125 +14,152 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! LES request types. +/// LES request types. -use util::bigint::prelude::*; -use rlp::*; +use ethcore::transaction::Transaction; +use util::{Address, H256}; -/// An LES request. This defines its data format, and the format of its response type. -pub trait Request: Sized { - /// The response type of this request. - type Response: Response; - - /// The error type when decoding a response. - type Error; - - /// Whether this request is empty. - fn is_empty(&self) -> bool; - - /// The remainder of this request unfulfilled by the response. Required to return - /// an equivalent request when provided with an empty response. - fn remainder(&self, res: &Self::Response) -> Self; - - /// Attempt to parse raw data into a response object - /// or an error. Behavior undefined if the raw data didn't originate from - /// this request. - fn parse_response(&self, raw: &[u8]) -> Result; +/// A request for block headers. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Headers { + /// Block information for the request being made. + pub block: (u64, H256), + /// The maximum amount of headers which can be returned. + pub max: u64, + /// The amount of headers to skip between each response entry. + pub skip: u64, + /// Whether the headers should proceed in falling number from the initial block. + pub reverse: bool, } -/// Request responses. These must have a combination operation used to fill the gaps -/// in one response with data from another. -pub trait Response: Sized { - /// Combine the two responses into one. This can only be relied on to behave correctly - /// if `other` is a response to a sub-request of the request this response was - /// produced from. - fn combine(&mut self, other: Self); +/// A request for specific block bodies. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Bodies { + /// Hashes which bodies are being requested for. + pub block_hashes: Vec } -/// A request for block bodies. -pub struct BlockBodies { - hashes: Vec, +/// A request for transaction receipts. +/// +/// This request is answered with a list of transaction receipts for each block +/// requested. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Receipts { + /// Block hashes to return receipts for. + pub block_hashes: Vec, } -/// A response for block bodies. -pub struct BlockBodiesResponse { - bodies: Vec<(H256, Vec)>, +/// A request for state proofs. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StateProofs { + /// Block hash to query state from. + pub block: H256, + /// Key of the state trie -- corresponds to account hash. + pub key1: H256, + /// Key in that account's storage trie; if empty, then the account RLP should be + /// returned. + pub key2: Option, + /// if greater than zero, trie nodes beyond this level may be omitted. + pub from_level: u32, // could even safely be u8; trie w/ 32-byte key can be at most 64-levels deep. } -impl Request for BlockBodies { - type Response = BlockBodiesResponse; - type Error = ::rlp::DecoderError; - - fn is_empty(&self) -> bool { self.hashes.is_empty() } - - fn remainder(&self, res: &Self::Response) -> Self { - let mut remaining = Vec::new(); - - let bodies = res.bodies.iter().map(|&(_, ref b) b).chain(::std::iter::repeat(&Vec::new())); - for (hash, body) in self.hashes.iter().zip(bodies) { - if body.is_empty() { - remaining.push(hash); - } - } - - BlockBodies { - hashes: remaining, - } - } - - fn parse_response(&self, raw: &[u8]) -> Result { - use ethcore::transaction::SignedTransaction; - use ethcore::header::Header; - - let rlp = UntrustedRlp::new(raw); - - let mut bodies = Vec::with_capacity(self.hashes.len()); - - let items = rlp.iter(); - for hash in self.hashes.iter().cloned() { - let res_bytes = match items.next() { - Some(rlp) => { - // perform basic block verification. - // TODO: custom error type? - try!(rlp.val_at::>(0) - .and_then(|_| rlp.val_at::>(1))); - - try!(rlp.data()).to_owned() - } - None => Vec::new(), - }; - - bodies.push((hash, res_bytes)); - } - - Ok(BlockBodiesResponse { - bodies: bodies, - }) - } +/// A request for contract code. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ContractCodes { + /// Block hash and account key (== sha3(address)) pairs to fetch code for. + pub code_requests: Vec<(H256, H256)>, } -impl Response for BlockBodiesResponse { - fn identity() -> Self { - BlockBodiesResponse { - bodies: Vec::new(), - } - } +/// A request for header proofs from the Canonical Hash Trie. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HeaderProofs { + /// Number of the CHT. + pub cht_number: u64, + /// Block number requested. + pub block_number: u64, + /// If greater than zero, trie nodes beyond this level may be omitted. + pub from_level: u32, +} - fn combine(&mut self, other: Self) { - let other_iter = other.bodies.into_iter(); +/// A request for block deltas -- merkle proofs of all changed trie nodes and code. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlockDeltas { + /// Block hashes deltas are being requested for. + pub block_hashes: Vec, +} - 'a: - for &mut (ref my_hash, ref mut my_body) in self.bodies.iter_mut() { - loop { - match other_iter.next() { - Some((hash, body)) if hash == my_hash && !body.is_empty() => { - *my_body = body.to_owned(); - break - } - Some(_) => continue, - None => break 'a, - } - } +/// A request for a single transaction merkle proof. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TransactionProof { + /// The block hash to use the initial state from. + pub block_hash: H256, + /// The address to treat as the sender of the transaction. + pub sender: Address, + /// The raw transaction request itself. + pub transaction: Transaction, +} + +/// A request for transaction merkle proofs. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TransactionProofs { + /// Transaction proof requests. + pub tx_reqs: Vec, +} + +/// Kinds of requests. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Kind { + /// Requesting headers. + Headers, + /// Requesting block bodies. + Bodies, + /// Requesting transaction receipts. + Receipts, + /// Requesting proofs of state trie nodes. + StateProofs, + /// Requesting contract code by hash. + Codes, + /// Requesting header proofs (from the CHT). + HeaderProofs, + /// Requesting block deltas. + Deltas, + /// Requesting merkle proofs for transactions. + TransactionProofs, +} + +/// Encompasses all possible types of requests in a single structure. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Request { + /// Requesting headers. + Headers(Headers), + /// Requesting block bodies. + Bodies(Bodies), + /// Requesting transaction receipts. + Receipts(Receipts), + /// Requesting state proofs. + StateProofs(StateProofs), + /// Requesting contract codes. + Codes(ContractCodes), + /// Requesting header proofs. + HeaderProofs(HeaderProofs), + /// Requesting block deltas. + Deltas(BlockDeltas), + /// Requesting transaction proofs. + TransactionProofs(TransactionProofs), +} + +impl Request { + /// Get the kind of request this is. + pub fn kind(&self) -> Kind { + match *self { + Request::Headers(_) => Kind::Headers, + Request::Bodies(_) => Kind::Bodies, + Request::Receipts(_) => Kind::Receipts, + Request::StateProofs(_) => Kind::StateProofs, + Request::Codes(_) => Kind::Codes, + Request::HeaderProofs(_) => Kind::HeaderProofs, + Request::Deltas(_) => Kind::Deltas, + Request::TransactionProofs(_) => Kind::TransactionProofs, } } } \ No newline at end of file From 5d011fe57799cfb8fe7ade5d58ae34c5eabc85d1 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 4 Nov 2016 17:25:26 +0100 Subject: [PATCH 08/69] new_list -> begin_list --- sync/src/light/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sync/src/light/mod.rs b/sync/src/light/mod.rs index 4c32d52ae..d7d1a84f7 100644 --- a/sync/src/light/mod.rs +++ b/sync/src/light/mod.rs @@ -147,22 +147,22 @@ impl Chain { // TODO [rob] use optional keys too. let mut stream = RlpStream::new_list(6); stream - .new_list(2) + .begin_list(2) .append("protocolVersion") .append(&PROTOCOL_VERSION) - .new_list(2) + .begin_list(2) .append("networkId") .append(&(self.mainnet as u8)) - .new_list(2) + .begin_list(2) .append("headTd") .append(&chain_info.total_difficulty) - .new_list(2) + .begin_list(2) .append("headHash") .append(&chain_info.best_block_hash) - .new_list(2) + .begin_list(2) .append("headNum") .append(&chain_info.best_block_number) - .new_list(2) + .begin_list(2) .append("genesisHash") .append(&self.genesis_hash); From 90a2c379779699d3e63ff92ae8faed9382b372fd Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 4 Nov 2016 17:35:31 +0100 Subject: [PATCH 09/69] handle unknown packet --- sync/src/light/mod.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sync/src/light/mod.rs b/sync/src/light/mod.rs index d7d1a84f7..a4aa0dcf7 100644 --- a/sync/src/light/mod.rs +++ b/sync/src/light/mod.rs @@ -148,22 +148,22 @@ impl Chain { let mut stream = RlpStream::new_list(6); stream .begin_list(2) - .append("protocolVersion") + .append(&"protocolVersion") .append(&PROTOCOL_VERSION) .begin_list(2) - .append("networkId") + .append(&"networkId") .append(&(self.mainnet as u8)) .begin_list(2) - .append("headTd") + .append(&"headTd") .append(&chain_info.total_difficulty) .begin_list(2) - .append("headHash") + .append(&"headHash") .append(&chain_info.best_block_hash) .begin_list(2) - .append("headNum") + .append(&"headNum") .append(&chain_info.best_block_number) .begin_list(2) - .append("genesisHash") + .append(&"genesisHash") .append(&self.genesis_hash); io.send(peer, packet::STATUS, stream.out()) @@ -315,6 +315,11 @@ impl NetworkProtocolHandler for Chain { packet::GET_TRANSACTION_PROOFS => self.get_transaction_proofs(peer, io, rlp), packet::TRANSACTION_PROOFS => self.transaction_proofs(peer, io, rlp), + + other => { + debug!(target: "les", "Disconnecting peer {} on unexpected packet {}", peer, other); + io.disconnect_peer(*peer); + } } } @@ -330,7 +335,6 @@ impl NetworkProtocolHandler for Chain { match timer { TIMEOUT => { // broadcast transactions to peers. - // update buffer flow. } _ => warn!(target: "les", "received timeout on unknown token {}", timer), } From edf17d00c43a3ac38d33aadf91fd8ea5b4adc0af Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 4 Nov 2016 18:40:31 +0100 Subject: [PATCH 10/69] revise light implementation strategy --- ethcore/src/light/client.rs | 12 +++++----- ethcore/src/light/provider.rs | 8 +++++-- sync/src/light/mod.rs | 41 ++++++++++++++++++++++++++++++----- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/ethcore/src/light/client.rs b/ethcore/src/light/client.rs index ae882b1a8..9f1b00e71 100644 --- a/ethcore/src/light/client.rs +++ b/ethcore/src/light/client.rs @@ -69,16 +69,16 @@ impl Client { pub fn queue_info(&self) -> QueueInfo { self.header_queue.queue_info() } - - /// Get the chain info. - pub fn chain_info(&self) -> BlockChainInfo { - unimplemented!() - } } // dummy implementation -- may draw from canonical cache further on. impl Provider for Client { - fn block_headers(&self, block: H256, skip: usize, max: usize, reverse: bool) -> Vec { + /// Get the chain info. + fn chain_info(&self) -> BlockChainInfo { + unimplemented!() + } + + fn block_headers(&self, block: (u64, H256), skip: usize, max: usize, reverse: bool) -> Vec { Vec::new() } diff --git a/ethcore/src/light/provider.rs b/ethcore/src/light/provider.rs index 05859c78b..10d3eed88 100644 --- a/ethcore/src/light/provider.rs +++ b/ethcore/src/light/provider.rs @@ -20,6 +20,7 @@ pub use proof_request::{CHTProofRequest, ProofRequest}; use transaction::SignedTransaction; +use blockchain_info::BlockChainInfo; use util::Bytes; use util::hash::H256; @@ -30,13 +31,16 @@ use util::hash::H256; /// Requests which can't be fulfilled should return an empty RLP list. /// /// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) -pub trait Provider { +pub trait Provider: Sync { + /// Provide current blockchain info. + fn chain_info(&self) -> BlockChainInfo; + /// Provide a list of headers starting at the requested block, /// possibly in reverse and skipping `skip` at a time. /// /// The returned vector may have any length in the range [0, `max`], but the /// results within must adhere to the `skip` and `reverse` parameters. - fn block_headers(&self, block: H256, skip: usize, max: usize, reverse: bool) -> Vec; + fn block_headers(&self, block: (u64, H256), skip: usize, max: usize, reverse: bool) -> Vec; /// Provide as many as possible of the requested blocks (minus the headers) encoded /// in RLP format. diff --git a/sync/src/light/mod.rs b/sync/src/light/mod.rs index a4aa0dcf7..19dac8561 100644 --- a/sync/src/light/mod.rs +++ b/sync/src/light/mod.rs @@ -91,6 +91,22 @@ mod packet { pub const TRANSACTION_PROOFS: u8 = 0x13; } +// helper macro for disconnecting peer on error while returning +// the value if ok. +// requires that error types are debug. +macro_rules! try_dc { + ($io: expr, $peer: expr, $e: expr) => { + match $e { + Ok(x) => x, + Err(e) => { + debug!(target: "les", "disconnecting peer {} due to error {:?}", $peer, e); + $io.disconnect_peer($peer); + return; + } + } + } +} + struct Requested { timestamp: usize, req: Request, @@ -102,9 +118,14 @@ struct Peer { current_asking: HashSet, // pending request ids. } -/// This handles synchronization of the header chain for a light client. -pub struct Chain { - client: Client, +/// This is an implementation of the light ethereum network protocol, abstracted +/// over a `Provider` of data and a p2p network. +/// +/// This is simply designed for request-response purposes. Higher level uses +/// of the protocol, such as synchronization, will function as wrappers around +/// this system. +pub struct LightProtocol { + provider: Box, genesis_hash: H256, mainnet: bool, peers: RwLock>, @@ -112,7 +133,7 @@ pub struct Chain { req_id: AtomicUsize, } -impl Chain { +impl LightProtocol { // make a request to a given peer. fn request_from(&self, peer: &PeerId, req: Request) { unimplemented!() @@ -189,6 +210,14 @@ impl Chain { fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { const MAX_HEADERS: usize = 512; + let req_id: u64 = try_dc!(io, peer, data.val_at(0)); + let block = try_dc!(io, peer, data.at(1).and_then(|block_list| { + (try!(block_list.val_at(0)), try!(block_list.val_at(1)) + })); + let max = ::std::cmp::min(MAX_HEADERS, try_dc!(io, peer, data.val_at(2))); + let reverse = try_dc!(io, peer, data.val_at(3)); + + let headers = self.provider.block_headers() unimplemented!() } @@ -199,6 +228,8 @@ impl Chain { // Handle a request for block bodies. fn get_block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + const MAX_BODIES: usize = 512; + unimplemented!() } @@ -278,7 +309,7 @@ impl Chain { } } -impl NetworkProtocolHandler for Chain { +impl NetworkProtocolHandler for LightProtocol { fn initialize(&self, io: &NetworkContext) { io.register_timer(TIMEOUT, TIMEOUT_INTERVAL_MS).expect("Error registering sync timer."); } From 5cabb3008f30e20d3105e2d761f4aca4304f8542 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 4 Nov 2016 19:21:48 +0100 Subject: [PATCH 11/69] make verification module public --- ethcore/src/lib.rs | 3 +-- ethcore/src/verification/canon_verifier.rs | 3 +++ ethcore/src/verification/mod.rs | 3 +++ ethcore/src/verification/noop_verifier.rs | 3 +++ ethcore/src/verification/verification.rs | 12 ++++++------ ethcore/src/verification/verifier.rs | 4 ++++ 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index c42abd34b..bf3e59171 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -137,7 +137,7 @@ pub mod miner; pub mod snapshot; pub mod action_params; pub mod db; -pub mod light; +pub mod verification; #[macro_use] pub mod evm; mod cache_manager; @@ -151,7 +151,6 @@ mod account_db; mod builtin; mod executive; mod externalities; -mod verification; mod blockchain; mod types; mod factory; diff --git a/ethcore/src/verification/canon_verifier.rs b/ethcore/src/verification/canon_verifier.rs index cc6bc448a..b5b01279e 100644 --- a/ethcore/src/verification/canon_verifier.rs +++ b/ethcore/src/verification/canon_verifier.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Canonical verifier. + use blockchain::BlockProvider; use engines::Engine; use error::Error; @@ -21,6 +23,7 @@ use header::Header; use super::Verifier; use super::verification; +/// A canonial verifier -- this does full verification. pub struct CanonVerifier; impl Verifier for CanonVerifier { diff --git a/ethcore/src/verification/mod.rs b/ethcore/src/verification/mod.rs index 239c88597..55663052b 100644 --- a/ethcore/src/verification/mod.rs +++ b/ethcore/src/verification/mod.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Block verification utilities. + pub mod verification; pub mod verifier; pub mod queue; @@ -44,6 +46,7 @@ impl Default for VerifierType { } } +/// Create a new verifier based on type. pub fn new(v: VerifierType) -> Box { match v { VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier), diff --git a/ethcore/src/verification/noop_verifier.rs b/ethcore/src/verification/noop_verifier.rs index fb798be46..7db688a85 100644 --- a/ethcore/src/verification/noop_verifier.rs +++ b/ethcore/src/verification/noop_verifier.rs @@ -14,12 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! No-op verifier. + use blockchain::BlockProvider; use engines::Engine; use error::Error; use header::Header; use super::Verifier; +/// A no-op verifier -- this will verify everything it's given immediately. #[allow(dead_code)] pub struct NoopVerifier; diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 1b8eddfe8..4ebd8aebd 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -/// Block and transaction verification functions -/// -/// Block verification is done in 3 steps -/// 1. Quick verification upon adding to the block queue -/// 2. Signatures verification done in the queue. -/// 3. Final verification against the blockchain done before enactment. +//! Block and transaction verification functions +//! +//! Block verification is done in 3 steps +//! 1. Quick verification upon adding to the block queue +//! 2. Signatures verification done in the queue. +//! 3. Final verification against the blockchain done before enactment. use util::*; use engines::Engine; diff --git a/ethcore/src/verification/verifier.rs b/ethcore/src/verification/verifier.rs index 7f57407f7..05d488f95 100644 --- a/ethcore/src/verification/verifier.rs +++ b/ethcore/src/verification/verifier.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! A generic verifier trait. + use blockchain::BlockProvider; use engines::Engine; use error::Error; @@ -21,6 +23,8 @@ use header::Header; /// Should be used to verify blocks. pub trait Verifier: Send + Sync { + /// Verify a block relative to its parent and uncles. fn verify_block_family(&self, header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error>; + /// Do a final verification check for an enacted header vs its expected counterpart. fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>; } From c1a6dbe75f617e2e0f98f16801a5a1c5b0ab1d67 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 4 Nov 2016 19:40:11 +0100 Subject: [PATCH 12/69] Move all light client work to own crate --- ethcore/light/Cargo.toml | 15 +++++++ ethcore/{src/light => light/src}/client.rs | 39 +++++++++------- .../{src/light/mod.rs => light/src/lib.rs} | 17 ++++--- .../light => ethcore/light/src/net}/mod.rs | 21 ++++----- ethcore/{src/light => light/src}/provider.rs | 26 ++++++----- .../light => ethcore/light/src}/request.rs | 4 +- ethcore/src/light/sync.rs | 33 -------------- ethcore/src/types/mod.rs.in | 1 - ethcore/src/types/proof_request.rs | 44 ------------------- sync/src/lib.rs | 1 - 10 files changed, 75 insertions(+), 126 deletions(-) create mode 100644 ethcore/light/Cargo.toml rename ethcore/{src/light => light/src}/client.rs (70%) rename ethcore/{src/light/mod.rs => light/src/lib.rs} (84%) rename {sync/src/light => ethcore/light/src/net}/mod.rs (95%) rename ethcore/{src/light => light/src}/provider.rs (78%) rename {sync/src/light => ethcore/light/src}/request.rs (98%) delete mode 100644 ethcore/src/light/sync.rs delete mode 100644 ethcore/src/types/proof_request.rs diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml new file mode 100644 index 000000000..dcfb4760a --- /dev/null +++ b/ethcore/light/Cargo.toml @@ -0,0 +1,15 @@ +[package] +description = "Parity LES primitives" +homepage = "https://ethcore.io" +license = "GPL-3.0" +name = "ethcore-light" +version = "1.5.0" +authors = ["Ethcore "] + +[dependencies] +log = "0.3" +ethcore = { path = ".." } +ethcore-util = { path = "../../util" } +ethcore-network = { path = "../../util/network" } +ethcore-io = { path = "../../util/io" } +rlp = { path = "../../util/rlp" } \ No newline at end of file diff --git a/ethcore/src/light/client.rs b/ethcore/light/src/client.rs similarity index 70% rename from ethcore/src/light/client.rs rename to ethcore/light/src/client.rs index 9f1b00e71..2d9b643bc 100644 --- a/ethcore/src/light/client.rs +++ b/ethcore/light/src/client.rs @@ -19,22 +19,23 @@ use std::sync::Arc; -use engines::Engine; -use ids::BlockID; -use miner::TransactionQueue; -use service::ClientIoMessage; -use block_import_error::BlockImportError; -use block_status::BlockStatus; -use verification::queue::{Config as QueueConfig, HeaderQueue, QueueInfo, Status}; -use transaction::SignedTransaction; -use types::blockchain_info::BlockChainInfo; - -use super::provider::{CHTProofRequest, Provider, ProofRequest}; +use ethcore::engines::Engine; +use ethcore::ids::BlockID; +use ethcore::miner::TransactionQueue; +use ethcore::service::ClientIoMessage; +use ethcore::block_import_error::BlockImportError; +use ethcore::block_status::BlockStatus; +use ethcore::verification::queue::{Config as QueueConfig, HeaderQueue, QueueInfo, Status}; +use ethcore::transaction::SignedTransaction; +use ethcore::blockchain_info::BlockChainInfo; use io::IoChannel; use util::hash::H256; use util::{Bytes, Mutex}; +use provider::Provider; +use request; + /// Light client implementation. pub struct Client { engine: Arc, @@ -78,27 +79,31 @@ impl Provider for Client { unimplemented!() } - fn block_headers(&self, block: (u64, H256), skip: usize, max: usize, reverse: bool) -> Vec { + fn block_headers(&self, _req: request::Headers) -> Vec { Vec::new() } - fn block_bodies(&self, blocks: Vec) -> Vec { + fn block_bodies(&self, _req: request::Bodies) -> Vec { Vec::new() } - fn receipts(&self, blocks: Vec) -> Vec { + fn receipts(&self, _req: request::Receipts) -> Vec { Vec::new() } - fn proofs(&self, requests: Vec<(H256, ProofRequest)>) -> Vec { + fn proofs(&self, _req: request::StateProofs) -> Vec { Vec::new() } - fn code(&self, accounts: Vec<(H256, H256)>) -> Vec { + fn code(&self, _req: request::ContractCodes) -> Vec { Vec::new() } - fn header_proofs(&self, requests: Vec) -> Vec { + fn header_proofs(&self, _req: request::HeaderProofs) -> Vec { + Vec::new() + } + + fn block_deltas(&self, _req: request::BlockDeltas) -> Vec { Vec::new() } diff --git a/ethcore/src/light/mod.rs b/ethcore/light/src/lib.rs similarity index 84% rename from ethcore/src/light/mod.rs rename to ethcore/light/src/lib.rs index 863f35433..1bfb6569a 100644 --- a/ethcore/src/light/mod.rs +++ b/ethcore/light/src/lib.rs @@ -28,9 +28,16 @@ //! It starts by performing a header-only sync, verifying every header in //! the chain. -mod client; -mod provider; -mod sync; +pub mod client; +pub mod net; +pub mod provider; +pub mod request; -pub use self::client::Client; -pub use self::provider::{CHTProofRequest, ProofRequest, Provider}; \ No newline at end of file +extern crate ethcore_util as util; +extern crate ethcore_network as network; +extern crate ethcore_io as io; +extern crate ethcore; +extern crate rlp; + +#[macro_use] +extern crate log; \ No newline at end of file diff --git a/sync/src/light/mod.rs b/ethcore/light/src/net/mod.rs similarity index 95% rename from sync/src/light/mod.rs rename to ethcore/light/src/net/mod.rs index 19dac8561..ac0d1f98d 100644 --- a/sync/src/light/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -19,19 +19,17 @@ //! This uses a "Provider" to answer requests and syncs to a `Client`. //! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) -use ethcore::light::{Client, Provider}; use io::TimerToken; use network::{NetworkProtocolHandler, NetworkService, NetworkContext, NetworkError, PeerId}; use rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; -use parking_lot::{Mutex, RwLock}; +use util::{Mutex, RwLock}; use std::collections::{HashMap, HashSet}; use std::sync::atomic::{AtomicUsize, Ordering}; -use self::request::Request; - -mod request; +use provider::Provider; +use request::Request; const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; @@ -163,7 +161,7 @@ impl LightProtocol { } fn send_status(&self, peer: PeerId, io: &NetworkContext) -> Result<(), NetworkError> { - let chain_info = self.client.chain_info(); + let chain_info = self.provider.chain_info(); // TODO [rob] use optional keys too. let mut stream = RlpStream::new_list(6); @@ -210,14 +208,13 @@ impl LightProtocol { fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { const MAX_HEADERS: usize = 512; - let req_id: u64 = try_dc!(io, peer, data.val_at(0)); - let block = try_dc!(io, peer, data.at(1).and_then(|block_list| { - (try!(block_list.val_at(0)), try!(block_list.val_at(1)) + let req_id: u64 = try_dc!(io, *peer, data.val_at(0)); + let block: (u64, H256) = try_dc!(io, *peer, data.at(1).and_then(|block_list| { + Ok((try!(block_list.val_at(0)), try!(block_list.val_at(1)))) })); - let max = ::std::cmp::min(MAX_HEADERS, try_dc!(io, peer, data.val_at(2))); - let reverse = try_dc!(io, peer, data.val_at(3)); + let max = ::std::cmp::min(MAX_HEADERS, try_dc!(io, *peer, data.val_at(2))); + let reverse: bool = try_dc!(io, *peer, data.val_at(3)); - let headers = self.provider.block_headers() unimplemented!() } diff --git a/ethcore/src/light/provider.rs b/ethcore/light/src/provider.rs similarity index 78% rename from ethcore/src/light/provider.rs rename to ethcore/light/src/provider.rs index 10d3eed88..de0fe3e2e 100644 --- a/ethcore/src/light/provider.rs +++ b/ethcore/light/src/provider.rs @@ -17,21 +17,20 @@ //! A provider for the LES protocol. This is typically a full node, who can //! give as much data as necessary to its peers. -pub use proof_request::{CHTProofRequest, ProofRequest}; - -use transaction::SignedTransaction; -use blockchain_info::BlockChainInfo; - +use ethcore::transaction::SignedTransaction; +use ethcore::blockchain_info::BlockChainInfo; use util::Bytes; use util::hash::H256; +use request; + /// Defines the operations that a provider for `LES` must fulfill. /// /// These are defined at [1], but may be subject to change. /// Requests which can't be fulfilled should return an empty RLP list. /// /// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) -pub trait Provider: Sync { +pub trait Provider: Send + Sync { /// Provide current blockchain info. fn chain_info(&self) -> BlockChainInfo; @@ -40,27 +39,30 @@ pub trait Provider: Sync { /// /// The returned vector may have any length in the range [0, `max`], but the /// results within must adhere to the `skip` and `reverse` parameters. - fn block_headers(&self, block: (u64, H256), skip: usize, max: usize, reverse: bool) -> Vec; + fn block_headers(&self, req: request::Headers) -> Vec; /// Provide as many as possible of the requested blocks (minus the headers) encoded /// in RLP format. - fn block_bodies(&self, blocks: Vec) -> Vec; + fn block_bodies(&self, req: request::Bodies) -> Vec; /// Provide the receipts as many as possible of the requested blocks. /// Returns a vector of RLP-encoded lists of receipts. - fn receipts(&self, blocks: Vec) -> Vec; + fn receipts(&self, req: request::Receipts) -> Vec; /// Provide a set of merkle proofs, as requested. Each request is a /// block hash and request parameters. /// /// Returns a vector to RLP-encoded lists satisfying the requests. - fn proofs(&self, requests: Vec<(H256, ProofRequest)>) -> Vec; + fn proofs(&self, req: request::StateProofs) -> Vec; /// Provide contract code for the specified (block_hash, account_hash) pairs. - fn code(&self, accounts: Vec<(H256, H256)>) -> Vec; + fn code(&self, req: request::ContractCodes) -> Vec; /// Provide header proofs from the Canonical Hash Tries. - fn header_proofs(&self, requests: Vec) -> Vec; + fn header_proofs(&self, req: request::HeaderProofs) -> Vec; + + /// Provide block deltas. + fn block_deltas(&self, req: request::BlockDeltas) -> Vec; /// Provide pending transactions. fn pending_transactions(&self) -> Vec; diff --git a/sync/src/light/request.rs b/ethcore/light/src/request.rs similarity index 98% rename from sync/src/light/request.rs rename to ethcore/light/src/request.rs index bbb91e415..63ec07068 100644 --- a/sync/src/light/request.rs +++ b/ethcore/light/src/request.rs @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -/// LES request types. +//! LES request types. + +// TODO: make IPC compatible. use ethcore::transaction::Transaction; use util::{Address, H256}; diff --git a/ethcore/src/light/sync.rs b/ethcore/src/light/sync.rs deleted file mode 100644 index 15ed5673c..000000000 --- a/ethcore/src/light/sync.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2015, 2016 Ethcore (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 . - -//! A light sync target. - -use block_import_error::BlockImportError; -use client::BlockChainInfo; - -use util::hash::H256; - -pub trait Sync { - /// Whether syncing is enabled. - fn enabled(&self) -> bool; - - /// Current chain info. - fn chain_info(&self) -> BlockChainInfo; - - /// Import a header. - fn import_header(&self, header_bytes: Vec) -> Result; -} \ No newline at end of file diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in index b00a64f85..6ef67009a 100644 --- a/ethcore/src/types/mod.rs.in +++ b/ethcore/src/types/mod.rs.in @@ -33,5 +33,4 @@ pub mod transaction_import; pub mod block_import_error; pub mod restoration_status; pub mod snapshot_manifest; -pub mod proof_request; pub mod mode; diff --git a/ethcore/src/types/proof_request.rs b/ethcore/src/types/proof_request.rs deleted file mode 100644 index 20bddce21..000000000 --- a/ethcore/src/types/proof_request.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015, 2016 Ethcore (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 . - -//! Merkle proof request. -use util::hash::H256; - -/// A request for a state merkle proof. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub enum ProofRequest { - /// Request a proof of the given account's (denoted by sha3(address)) - /// node in the state trie. Nodes with depth less than the second item - /// may be omitted. - Account(H256, usize), - - /// Request a proof for a key in the given account's storage trie. - /// Both values are hashes of their actual values. Nodes with depth - /// less than the third item may be omitted. - Storage(H256, H256, usize), -} - -/// A request for a Canonical Hash Trie proof for the given block number. -/// Nodes with depth less than the second item may be omitted. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct CHTProofRequest { - /// The number of the block the proof is requested for. - /// The CHT's number can be deduced from this (`number` / 4096) - pub number: u64, - - /// Nodes with depth less than this can be omitted from the proof. - pub depth: usize, -} \ No newline at end of file diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 4983b3c90..532c05711 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -51,7 +51,6 @@ mod blocks; mod block_sync; mod sync_io; mod snapshot; -mod light; #[cfg(test)] mod tests; From 52abbc06435eec38578cf4bbf0742174c2aa2d36 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 4 Nov 2016 23:50:56 +0100 Subject: [PATCH 13/69] experiment with answering requests --- ethcore/light/src/net/mod.rs | 28 ++++++++++++++++++++-------- ethcore/light/src/provider.rs | 1 - 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index ac0d1f98d..390c6a35b 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -29,7 +29,7 @@ use std::collections::{HashMap, HashSet}; use std::sync::atomic::{AtomicUsize, Ordering}; use provider::Provider; -use request::Request; +use request::{self, Request}; const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; @@ -206,16 +206,28 @@ impl LightProtocol { // Handle a request for block headers. fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { - const MAX_HEADERS: usize = 512; + const MAX_HEADERS: u64 = 512; let req_id: u64 = try_dc!(io, *peer, data.val_at(0)); - let block: (u64, H256) = try_dc!(io, *peer, data.at(1).and_then(|block_list| { - Ok((try!(block_list.val_at(0)), try!(block_list.val_at(1)))) - })); - let max = ::std::cmp::min(MAX_HEADERS, try_dc!(io, *peer, data.val_at(2))); - let reverse: bool = try_dc!(io, *peer, data.val_at(3)); + let req = request::Headers { + block: try_dc!(io, *peer, data.at(1).and_then(|block_list| { + Ok((try!(block_list.val_at(0)), try!(block_list.val_at(1)))) + })), + max: ::std::cmp::min(MAX_HEADERS, try_dc!(io, *peer, data.val_at(2))), + skip: try_dc!(io, *peer, data.val_at(3)), + reverse: try_dc!(io, *peer, data.val_at(4)), + }; - unimplemented!() + let res = self.provider.block_headers(req); + + let mut res_stream = RlpStream::new_list(2 + res.len()); + res_stream.append(&req_id); + res_stream.append(&0u64); // TODO: Buffer Flow. + for raw_header in res { + res_stream.append_raw(&raw_header, 1); + } + + try_dc!(io, *peer, io.respond(packet::BLOCK_HEADERS, res_stream.out())) } // Receive a response for block headers. diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index de0fe3e2e..6d4fb2ea0 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -20,7 +20,6 @@ use ethcore::transaction::SignedTransaction; use ethcore::blockchain_info::BlockChainInfo; use util::Bytes; -use util::hash::H256; use request; From 44e36596c9c0e8d606ffb89b6fa171b3ff04f53d Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Sun, 6 Nov 2016 19:04:30 +0100 Subject: [PATCH 14/69] buffer flow scaffolding --- ethcore/light/src/lib.rs | 4 +-- ethcore/light/src/net/buffer_flow.rs | 42 ++++++++++++++++++++++++++++ ethcore/light/src/net/mod.rs | 4 +-- 3 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 ethcore/light/src/net/buffer_flow.rs diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index 1bfb6569a..63586f5a2 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -25,8 +25,8 @@ //! low-latency applications, but perfectly suitable for simple everyday //! use-cases like sending transactions from a personal account. //! -//! It starts by performing a header-only sync, verifying every header in -//! the chain. +//! It starts by performing a header-only sync, verifying random samples +//! of members of the chain to varying degrees. pub mod client; pub mod net; diff --git a/ethcore/light/src/net/buffer_flow.rs b/ethcore/light/src/net/buffer_flow.rs new file mode 100644 index 000000000..ece9f7057 --- /dev/null +++ b/ethcore/light/src/net/buffer_flow.rs @@ -0,0 +1,42 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! LES buffer flow management. +//! +//! Every request in the LES protocol leads to a reduction +//! of the requester's buffer value as a rate-limiting mechanism. +//! This buffer value will recharge at a set rate. +//! +//! This module provides an interface for configuration of buffer +//! flow costs and recharge rates. + +use request::{self, Request}; + +/// Manages buffer flow costs for specific requests. +pub struct FlowManager; + +impl FlowManager { + /// Estimate the maximum cost of this request. + pub fn estimate_cost(&self, req: &request::Request) -> usize { + unimplemented!() + } + + /// Get an exact cost based on request kind and amount of requests fulfilled. + pub fn exact_cost(&self, kind: request::Kind, amount: usize) -> usize { + unimplemented!() + } +} + diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 390c6a35b..efbc391e1 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -31,6 +31,8 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use provider::Provider; use request::{self, Request}; +mod buffer_flow; + const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; @@ -40,8 +42,6 @@ const PROTOCOL_VERSION: u32 = 1; // TODO [rob] make configurable. const PROTOCOL_ID: [u8; 3] = *b"les"; -// TODO [rob] Buffer flow. - // packet ID definitions. mod packet { // the status packet. From d573ef3cc27d00509ccc36ca731278fedb143f7b Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Sun, 6 Nov 2016 19:05:19 +0100 Subject: [PATCH 15/69] remove LESv2 requests --- ethcore/light/src/client.rs | 4 ---- ethcore/light/src/net/mod.rs | 13 +++++++------ ethcore/light/src/provider.rs | 3 --- ethcore/light/src/request.rs | 35 ----------------------------------- 4 files changed, 7 insertions(+), 48 deletions(-) diff --git a/ethcore/light/src/client.rs b/ethcore/light/src/client.rs index 2d9b643bc..81355bf0f 100644 --- a/ethcore/light/src/client.rs +++ b/ethcore/light/src/client.rs @@ -103,10 +103,6 @@ impl Provider for Client { Vec::new() } - fn block_deltas(&self, _req: request::BlockDeltas) -> Vec { - Vec::new() - } - fn pending_transactions(&self) -> Vec { Vec::new() } diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index efbc391e1..9b707db2f 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -23,13 +23,14 @@ use io::TimerToken; use network::{NetworkProtocolHandler, NetworkService, NetworkContext, NetworkError, PeerId}; use rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; -use util::{Mutex, RwLock}; +use util::{U256, Mutex, RwLock}; use std::collections::{HashMap, HashSet}; use std::sync::atomic::{AtomicUsize, Ordering}; use provider::Provider; use request::{self, Request}; +use self::buffer_flow::FlowManager; mod buffer_flow; @@ -47,8 +48,8 @@ mod packet { // the status packet. pub const STATUS: u8 = 0x00; - // broadcast that new block hashes have appeared. - pub const NEW_BLOCK_HASHES: u8 = 0x01; + // announcement of new block hashes or capabilities. + pub const ANNOUNCE: u8 = 0x01; // request and response for block headers pub const GET_BLOCK_HEADERS: u8 = 0x02; @@ -197,8 +198,8 @@ impl LightProtocol { unimplemented!() } - // Handle a new block hashes message. - fn new_block_hashes(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + // Handle an announcement. + fn announcement(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { const MAX_NEW_HASHES: usize = 256; unimplemented!() @@ -327,7 +328,7 @@ impl NetworkProtocolHandler for LightProtocol { let rlp = UntrustedRlp::new(data); match packet_id { packet::STATUS => self.status(peer, io, rlp), - packet::NEW_BLOCK_HASHES => self.new_block_hashes(peer, io, rlp), + packet::ANNOUNCE => self.announcement(peer, io, rlp), packet::GET_BLOCK_HEADERS => self.get_block_headers(peer, io, rlp), packet::BLOCK_HEADERS => self.block_headers(peer, io, rlp), diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 6d4fb2ea0..d6458bedd 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -60,9 +60,6 @@ pub trait Provider: Send + Sync { /// Provide header proofs from the Canonical Hash Tries. fn header_proofs(&self, req: request::HeaderProofs) -> Vec; - /// Provide block deltas. - fn block_deltas(&self, req: request::BlockDeltas) -> Vec; - /// Provide pending transactions. fn pending_transactions(&self) -> Vec; } \ No newline at end of file diff --git a/ethcore/light/src/request.rs b/ethcore/light/src/request.rs index 63ec07068..f5c594970 100644 --- a/ethcore/light/src/request.rs +++ b/ethcore/light/src/request.rs @@ -83,31 +83,6 @@ pub struct HeaderProofs { pub from_level: u32, } -/// A request for block deltas -- merkle proofs of all changed trie nodes and code. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BlockDeltas { - /// Block hashes deltas are being requested for. - pub block_hashes: Vec, -} - -/// A request for a single transaction merkle proof. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TransactionProof { - /// The block hash to use the initial state from. - pub block_hash: H256, - /// The address to treat as the sender of the transaction. - pub sender: Address, - /// The raw transaction request itself. - pub transaction: Transaction, -} - -/// A request for transaction merkle proofs. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TransactionProofs { - /// Transaction proof requests. - pub tx_reqs: Vec, -} - /// Kinds of requests. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Kind { @@ -123,10 +98,6 @@ pub enum Kind { Codes, /// Requesting header proofs (from the CHT). HeaderProofs, - /// Requesting block deltas. - Deltas, - /// Requesting merkle proofs for transactions. - TransactionProofs, } /// Encompasses all possible types of requests in a single structure. @@ -144,10 +115,6 @@ pub enum Request { Codes(ContractCodes), /// Requesting header proofs. HeaderProofs(HeaderProofs), - /// Requesting block deltas. - Deltas(BlockDeltas), - /// Requesting transaction proofs. - TransactionProofs(TransactionProofs), } impl Request { @@ -160,8 +127,6 @@ impl Request { Request::StateProofs(_) => Kind::StateProofs, Request::Codes(_) => Kind::Codes, Request::HeaderProofs(_) => Kind::HeaderProofs, - Request::Deltas(_) => Kind::Deltas, - Request::TransactionProofs(_) => Kind::TransactionProofs, } } } \ No newline at end of file From 051effe9f8ceca371eaf4ca7c9c7898c956d211a Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 7 Nov 2016 15:40:34 +0100 Subject: [PATCH 16/69] buffer flow basics, implement cost table --- ethcore/light/Cargo.toml | 3 +- ethcore/light/src/lib.rs | 1 + ethcore/light/src/net/buffer_flow.rs | 244 ++++++++++++++++++++++++++- ethcore/light/src/net/mod.rs | 85 +--------- ethcore/light/src/request.rs | 22 ++- 5 files changed, 258 insertions(+), 97 deletions(-) diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index dcfb4760a..daf141de7 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -12,4 +12,5 @@ ethcore = { path = ".." } ethcore-util = { path = "../../util" } ethcore-network = { path = "../../util/network" } ethcore-io = { path = "../../util/io" } -rlp = { path = "../../util/rlp" } \ No newline at end of file +rlp = { path = "../../util/rlp" } +time = "0.1" \ No newline at end of file diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index 63586f5a2..f6d05c65c 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -38,6 +38,7 @@ extern crate ethcore_network as network; extern crate ethcore_io as io; extern crate ethcore; extern crate rlp; +extern crate time; #[macro_use] extern crate log; \ No newline at end of file diff --git a/ethcore/light/src/net/buffer_flow.rs b/ethcore/light/src/net/buffer_flow.rs index ece9f7057..deed1cded 100644 --- a/ethcore/light/src/net/buffer_flow.rs +++ b/ethcore/light/src/net/buffer_flow.rs @@ -24,19 +24,245 @@ //! flow costs and recharge rates. use request::{self, Request}; +use super::packet; -/// Manages buffer flow costs for specific requests. -pub struct FlowManager; +use rlp::*; +use util::U256; +use time::{Duration, SteadyTime}; -impl FlowManager { - /// Estimate the maximum cost of this request. - pub fn estimate_cost(&self, req: &request::Request) -> usize { - unimplemented!() +/// A request cost specification. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Cost(pub U256, pub U256); + +/// An error: insufficient buffer. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InsufficientBuffer; + +/// Buffer value. +/// +/// Produced and recharged using `FlowParams`. +/// Definitive updates can be made as well -- these will reset the recharge +/// point to the time of the update. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Buffer { + estimate: U256, + recharge_point: SteadyTime, +} + +impl Buffer { + /// Make a definitive update. + /// This will be the value obtained after receiving + /// a response to a request. + pub fn update_to(&mut self, value: U256) { + self.estimate = value; + self.recharge_point = SteadyTime::now(); } - /// Get an exact cost based on request kind and amount of requests fulfilled. - pub fn exact_cost(&self, kind: request::Kind, amount: usize) -> usize { - unimplemented!() + /// Attempt to apply the given cost to the buffer. + /// If successful, the cost will be deducted successfully. + /// If unsuccessful, the structure will be unaltered an an + /// error will be produced. + pub fn deduct_cost(&mut self, cost: U256) -> Result<(), InsufficientBuffer> { + match cost > self.estimate { + true => Err(InsufficientBuffer), + false => { + self.estimate = self.estimate - cost; + Ok(()) + } + } } } +/// A cost table, mapping requests to base and per-request costs. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CostTable { + headers: Cost, + bodies: Cost, + receipts: Cost, + state_proofs: Cost, + contract_codes: Cost, + header_proofs: Cost, +} + +impl Default for CostTable { + fn default() -> Self { + // arbitrarily chosen constants. + CostTable { + headers: Cost(100000.into(), 10000.into()), + bodies: Cost(150000.into(), 15000.into()), + receipts: Cost(50000.into(), 5000.into()), + state_proofs: Cost(250000.into(), 25000.into()), + contract_codes: Cost(200000.into(), 20000.into()), + header_proofs: Cost(150000.into(), 15000.into()), + } + } +} + +impl RlpEncodable for CostTable { + fn rlp_append(&self, s: &mut RlpStream) { + fn append_cost(s: &mut RlpStream, msg_id: u8, cost: &Cost) { + s.begin_list(3) + .append(&msg_id) + .append(&cost.0) + .append(&cost.1); + } + + s.begin_list(6); + + append_cost(s, packet::GET_BLOCK_HEADERS, &self.headers); + append_cost(s, packet::GET_BLOCK_BODIES, &self.bodies); + append_cost(s, packet::GET_RECEIPTS, &self.receipts); + append_cost(s, packet::GET_PROOFS, &self.state_proofs); + append_cost(s, packet::GET_CONTRACT_CODES, &self.contract_codes); + append_cost(s, packet::GET_HEADER_PROOFS, &self.header_proofs); + } +} + +impl RlpDecodable for CostTable { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + + let mut headers = None; + let mut bodies = None; + let mut receipts = None; + let mut state_proofs = None; + let mut contract_codes = None; + let mut header_proofs = None; + + for row in rlp.iter() { + let msg_id: u8 = try!(row.val_at(0)); + let cost = { + let base = try!(row.val_at(1)); + let per = try!(row.val_at(2)); + + Cost(base, per) + }; + + match msg_id { + packet::GET_BLOCK_HEADERS => headers = Some(cost), + packet::GET_BLOCK_BODIES => bodies = Some(cost), + packet::GET_RECEIPTS => receipts = Some(cost), + packet::GET_PROOFS => state_proofs = Some(cost), + packet::GET_CONTRACT_CODES => contract_codes = Some(cost), + packet::GET_HEADER_PROOFS => header_proofs = Some(cost), + _ => return Err(DecoderError::Custom("Unrecognized message in cost table")), + } + } + + Ok(CostTable { + headers: try!(headers.ok_or(DecoderError::Custom("No headers cost specified"))), + bodies: try!(bodies.ok_or(DecoderError::Custom("No bodies cost specified"))), + receipts: try!(receipts.ok_or(DecoderError::Custom("No receipts cost specified"))), + state_proofs: try!(state_proofs.ok_or(DecoderError::Custom("No proofs cost specified"))), + contract_codes: try!(contract_codes.ok_or(DecoderError::Custom("No contract codes specified"))), + header_proofs: try!(header_proofs.ok_or(DecoderError::Custom("No header proofs cost specified"))), + }) + } +} + +/// A buffer-flow manager handles costs, recharge, limits +#[derive(Debug, Clone, PartialEq)] +pub struct FlowParams { + costs: CostTable, + limit: U256, + recharge: U256, +} + +impl FlowParams { + /// Create new flow parameters from a request cost table, + /// buffer limit, and (minimum) rate of recharge. + pub fn new(costs: CostTable, limit: U256, recharge: U256) -> Self { + FlowParams { + costs: costs, + limit: limit, + recharge: recharge, + } + } + + /// Estimate the maximum cost of the request. + pub fn max_cost(&self, req: &Request) -> U256 { + let amount = match *req { + Request::Headers(ref req) => req.max as usize, + Request::Bodies(ref req) => req.block_hashes.len(), + Request::Receipts(ref req) => req.block_hashes.len(), + Request::StateProofs(ref req) => req.requests.len(), + Request::Codes(ref req) => req.code_requests.len(), + Request::HeaderProofs(ref req) => req.requests.len(), + }; + + self.actual_cost(req.kind(), amount) + } + + /// Compute the actual cost of a request, given the kind of request + /// and number of requests made. + pub fn actual_cost(&self, kind: request::Kind, amount: usize) -> U256 { + let cost = match kind { + request::Kind::Headers => &self.costs.headers, + request::Kind::Bodies => &self.costs.bodies, + request::Kind::Receipts => &self.costs.receipts, + request::Kind::StateProofs => &self.costs.state_proofs, + request::Kind::Codes => &self.costs.contract_codes, + request::Kind::HeaderProofs => &self.costs.header_proofs, + }; + + let amount: U256 = amount.into(); + cost.0 + (amount * cost.1) + } + + /// Create initial buffer parameter. + pub fn create_buffer(&self) -> Buffer { + Buffer { + estimate: self.limit, + recharge_point: SteadyTime::now(), + } + } + + /// Recharge the buffer based on time passed since last + /// update. + pub fn recharge(&self, buf: &mut Buffer) { + let now = SteadyTime::now(); + + // recompute and update only in terms of full seconds elapsed + // in order to keep the estimate as an underestimate. + let elapsed = (now - buf.recharge_point).num_seconds(); + buf.recharge_point = buf.recharge_point + Duration::seconds(elapsed); + + let elapsed: U256 = elapsed.into(); + + buf.estimate = ::std::cmp::min(self.limit, buf.estimate + (elapsed * self.recharge)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use util::U256; + + #[test] + fn should_serialize_cost_table() { + let costs = CostTable::default(); + let serialized = ::rlp::encode(&costs); + + let new_costs: CostTable = ::rlp::decode(&*serialized); + + assert_eq!(costs, new_costs); + } + + #[test] + fn buffer_mechanism() { + use std::thread; + use std::time::Duration; + + let flow_params = FlowParams::new(Default::default(), 100.into(), 20.into()); + let mut buffer = flow_params.create_buffer(); + + assert!(buffer.deduct_cost(101.into()).is_err()); + assert!(buffer.deduct_cost(10.into()).is_ok()); + + thread::sleep(Duration::from_secs(1)); + + flow_params.recharge(&mut buffer); + + assert_eq!(buffer.estimate, 100.into()); + } +} \ No newline at end of file diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 9b707db2f..34fe8acf2 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -30,7 +30,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use provider::Provider; use request::{self, Request}; -use self::buffer_flow::FlowManager; +use self::buffer_flow::FlowParams; mod buffer_flow; @@ -77,33 +77,6 @@ mod packet { // request and response for header proofs in a CHT. pub const GET_HEADER_PROOFS: u8 = 0x0d; pub const HEADER_PROOFS: u8 = 0x0e; - - // broadcast dynamic capabilities. - pub const CAPABILITIES: u8 = 0x0f; - - // request and response for block-level state deltas. - pub const GET_BLOCK_DELTAS: u8 = 0x10; - pub const BLOCK_DELTAS: u8 = 0x11; - - // request and response for transaction proofs. - pub const GET_TRANSACTION_PROOFS: u8 = 0x12; - pub const TRANSACTION_PROOFS: u8 = 0x13; -} - -// helper macro for disconnecting peer on error while returning -// the value if ok. -// requires that error types are debug. -macro_rules! try_dc { - ($io: expr, $peer: expr, $e: expr) => { - match $e { - Ok(x) => x, - Err(e) => { - debug!(target: "les", "disconnecting peer {} due to error {:?}", $peer, e); - $io.disconnect_peer($peer); - return; - } - } - } } struct Requested { @@ -209,26 +182,7 @@ impl LightProtocol { fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { const MAX_HEADERS: u64 = 512; - let req_id: u64 = try_dc!(io, *peer, data.val_at(0)); - let req = request::Headers { - block: try_dc!(io, *peer, data.at(1).and_then(|block_list| { - Ok((try!(block_list.val_at(0)), try!(block_list.val_at(1)))) - })), - max: ::std::cmp::min(MAX_HEADERS, try_dc!(io, *peer, data.val_at(2))), - skip: try_dc!(io, *peer, data.val_at(3)), - reverse: try_dc!(io, *peer, data.val_at(4)), - }; - - let res = self.provider.block_headers(req); - - let mut res_stream = RlpStream::new_list(2 + res.len()); - res_stream.append(&req_id); - res_stream.append(&0u64); // TODO: Buffer Flow. - for raw_header in res { - res_stream.append_raw(&raw_header, 1); - } - - try_dc!(io, *peer, io.respond(packet::BLOCK_HEADERS, res_stream.out())) + unimplemented!() } // Receive a response for block headers. @@ -292,31 +246,6 @@ impl LightProtocol { fn relay_transactions(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { unimplemented!() } - - // Receive updated capabilities from a peer. - fn capabilities(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { - unimplemented!() - } - - // Handle a request for block deltas. - fn get_block_deltas(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { - unimplemented!() - } - - // Receive block deltas. - fn block_deltas(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { - unimplemented!() - } - - // Handle a request for transaction proofs. - fn get_transaction_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { - unimplemented!() - } - - // Receive transaction proofs. - fn transaction_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { - unimplemented!() - } } impl NetworkProtocolHandler for LightProtocol { @@ -346,16 +275,6 @@ impl NetworkProtocolHandler for LightProtocol { packet::CONTRACT_CODES => self.contract_code(peer, io, rlp), packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), - packet::CAPABILITIES => self.capabilities(peer, io, rlp), - - packet::GET_HEADER_PROOFS => self.get_header_proofs(peer, io, rlp), - packet::HEADER_PROOFS => self.header_proofs(peer, io, rlp), - - packet::GET_BLOCK_DELTAS => self.get_block_deltas(peer, io, rlp), - packet::BLOCK_DELTAS => self.block_deltas(peer, io, rlp), - - packet::GET_TRANSACTION_PROOFS => self.get_transaction_proofs(peer, io, rlp), - packet::TRANSACTION_PROOFS => self.transaction_proofs(peer, io, rlp), other => { debug!(target: "les", "Disconnecting peer {} on unexpected packet {}", peer, other); diff --git a/ethcore/light/src/request.rs b/ethcore/light/src/request.rs index f5c594970..11b474ee5 100644 --- a/ethcore/light/src/request.rs +++ b/ethcore/light/src/request.rs @@ -51,9 +51,9 @@ pub struct Receipts { pub block_hashes: Vec, } -/// A request for state proofs. +/// A request for a state proof #[derive(Debug, Clone, PartialEq, Eq)] -pub struct StateProofs { +pub struct StateProof { /// Block hash to query state from. pub block: H256, /// Key of the state trie -- corresponds to account hash. @@ -65,6 +65,13 @@ pub struct StateProofs { pub from_level: u32, // could even safely be u8; trie w/ 32-byte key can be at most 64-levels deep. } +/// A request for state proofs. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StateProofs { + /// All the proof requests. + pub requests: Vec, +} + /// A request for contract code. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ContractCodes { @@ -72,9 +79,9 @@ pub struct ContractCodes { pub code_requests: Vec<(H256, H256)>, } -/// A request for header proofs from the Canonical Hash Trie. +/// A request for a header proof from the Canonical Hash Trie. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct HeaderProofs { +pub struct HeaderProof { /// Number of the CHT. pub cht_number: u64, /// Block number requested. @@ -83,6 +90,13 @@ pub struct HeaderProofs { pub from_level: u32, } +/// A request for header proofs from the CHT. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HeaderProofs { + /// All the proof requests. + pub requests: Vec, +} + /// Kinds of requests. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Kind { From 4ba486173472a9ade4ca9860d5a8252bd7d5bd83 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 7 Nov 2016 19:16:23 +0100 Subject: [PATCH 17/69] begin status module --- ethcore/light/src/net/mod.rs | 8 +-- ethcore/light/src/net/status.rs | 110 ++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 ethcore/light/src/net/status.rs diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 34fe8acf2..d7bcc4b39 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -16,7 +16,7 @@ //! LES Protocol Version 1 implementation. //! -//! This uses a "Provider" to answer requests and syncs to a `Client`. +//! This uses a "Provider" to answer requests. //! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) use io::TimerToken; @@ -33,6 +33,7 @@ use request::{self, Request}; use self::buffer_flow::FlowParams; mod buffer_flow; +mod status; const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; @@ -79,11 +80,6 @@ mod packet { pub const HEADER_PROOFS: u8 = 0x0e; } -struct Requested { - timestamp: usize, - req: Request, -} - // data about each peer. struct Peer { buffer: u64, // remaining buffer value. diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs new file mode 100644 index 000000000..d89c6ac79 --- /dev/null +++ b/ethcore/light/src/net/status.rs @@ -0,0 +1,110 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! Peer status and capabilities. + +use rlp::{RlpStream, Stream, UntrustedRlp, View}; +use util::{H256, U256}; + +/// Network ID structure. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum NetworkId { + /// ID for the mainnet + Mainnet = 1, + /// ID for the testnet + Testnet = 0, +} + +/// A peer status message. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Status { + /// Protocol version. + pub protocol_version: u32, + /// Network id of this peer. + pub network_id: NetworkId, + /// Total difficulty of the head of the chain. + pub head_td: U256, + /// Hash of the best block. + pub head_hash: H256, + /// Number of the best block. + pub head_num: u64, + /// Genesis hash + pub genesis_hash: Option, + /// Last announced chain head and reorg depth to common ancestor. + pub last_head: Option<(H256, u64)>, +} + +/// Peer capabilities. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Capabilities { + /// Whether this peer can serve headers + pub serve_headers: bool, + /// Earliest block number it can serve chain requests for. + pub serve_chain_since: Option, + /// Earliest block number it can serve state requests for. + pub serve_state_since: Option, + /// Whether it can relay transactions to the eth network. + pub tx_relay: bool, +} + +impl Default for Capabilities { + fn default() -> Self { + Capabilities { + serve_headers: false, + serve_chain_since: None, + serve_state_since: None, + tx_relay: false, + } + } +} + +impl Capabilities { + /// Decode capabilities from the given rlp stream, starting from the given + /// index. + fn decode_from(rlp: &UntrustedRlp, start_idx: usize) -> Result { + let mut caps = Capabilities::default(); + + for item in rlp.iter().skip(start_idx).take(4) { + let key: String = try!(item.val_at(0)); + + match &*key { + "serveHeaders" => caps.serve_headers = true, + "serveChainSince" => caps.serve_chain_since = Some(try!(item.val_at(1))), + "serveStateSince" => caps.serve_state_since = Some(try!(item.val_at(1))), + "txRelay" => caps.tx_relay = true, + _ => continue, + } + } + + Ok(caps) + } +} + +/// An announcement of new chain head or capabilities made by a peer. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Announcement { + /// Hash of the best block. + head_hash: H256, + /// Number of the best block. + head_num: u64, + /// Head total difficulty + head_td: U256, + /// reorg depth to common ancestor of last announced head. + reorg_depth: u64, + /// updated capabilities. + new_capabilities: Capabilities, +} \ No newline at end of file From 440f5e537f5d39b01cf9d7c8df6e86f640b92a11 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 8 Nov 2016 16:57:10 +0100 Subject: [PATCH 18/69] implement handshake parsing and creation --- ethcore/light/src/net/buffer_flow.rs | 13 +- ethcore/light/src/net/mod.rs | 2 +- ethcore/light/src/net/status.rs | 349 +++++++++++++++++++++++++-- 3 files changed, 342 insertions(+), 22 deletions(-) diff --git a/ethcore/light/src/net/buffer_flow.rs b/ethcore/light/src/net/buffer_flow.rs index deed1cded..7ee287533 100644 --- a/ethcore/light/src/net/buffer_flow.rs +++ b/ethcore/light/src/net/buffer_flow.rs @@ -171,7 +171,7 @@ pub struct FlowParams { impl FlowParams { /// Create new flow parameters from a request cost table, /// buffer limit, and (minimum) rate of recharge. - pub fn new(costs: CostTable, limit: U256, recharge: U256) -> Self { + pub fn new(limit: U256, costs: CostTable, recharge: U256) -> Self { FlowParams { costs: costs, limit: limit, @@ -179,6 +179,15 @@ impl FlowParams { } } + /// Get a reference to the buffer limit. + pub fn limit(&self) -> &U256 { &self.limit } + + /// Get a reference to the cost table. + pub fn cost_table(&self) -> &CostTable { &self.costs } + + /// Get a reference to the recharge rate. + pub fn recharge_rate(&self) -> &U256 { &self.recharge } + /// Estimate the maximum cost of the request. pub fn max_cost(&self, req: &Request) -> U256 { let amount = match *req { @@ -253,7 +262,7 @@ mod tests { use std::thread; use std::time::Duration; - let flow_params = FlowParams::new(Default::default(), 100.into(), 20.into()); + let flow_params = FlowParams::new(100.into(), Default::default(), 20.into()); let mut buffer = flow_params.create_buffer(); assert!(buffer.deduct_cost(101.into()).is_err()); diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index d7bcc4b39..4f0543526 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -97,7 +97,7 @@ pub struct LightProtocol { genesis_hash: H256, mainnet: bool, peers: RwLock>, - pending_requests: RwLock>, + pending_requests: RwLock>, req_id: AtomicUsize, } diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index d89c6ac79..23b8b68c0 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -16,9 +16,75 @@ //! Peer status and capabilities. -use rlp::{RlpStream, Stream, UntrustedRlp, View}; +use rlp::{DecoderError, RlpDecodable, RlpEncodable, RlpStream, Stream, UntrustedRlp, View}; use util::{H256, U256}; +use super::buffer_flow::{CostTable, FlowParams}; + +// recognized handshake/announcement keys. +// unknown keys are to be skipped, known keys have a defined order. +// their string values are defined in the LES spec. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Key { + ProtocolVersion, + NetworkId, + HeadTD, + HeadHash, + HeadNum, + GenesisHash, + ServeHeaders, + ServeChainSince, + ServeStateSince, + TxRelay, + BufferLimit, + BufferCostTable, + BufferRechargeRate, +} + +impl Key { + // get the string value of this key. + fn as_str(&self) -> &'static str { + match *self { + Key::ProtocolVersion => "protocolVersion", + Key::NetworkId => "networkId", + Key::HeadTD => "headTd", + Key::HeadHash => "headHash", + Key::HeadNum => "headNum", + Key::GenesisHash => "genesisHash", + Key::ServeHeaders => "serveHeaders", + Key::ServeChainSince => "serveChainSince", + Key::ServeStateSince => "serveStateSince", + Key::TxRelay => "txRelay", + Key::BufferLimit => "flowControl/BL", + Key::BufferCostTable => "flowControl/MRC", + Key::BufferRechargeRate => "flowControl/MRR", + } + } + + fn from_str(s: &str) -> Option { + match s { + "protocolVersion" => Some(Key::ProtocolVersion), + "networkId" => Some(Key::NetworkId), + "headTd" => Some(Key::HeadTD), + "headHash" => Some(Key::HeadHash), + "headNum" => Some(Key::HeadNum), + "genesisHash" => Some(Key::GenesisHash), + "serveHeaders" => Some(Key::ServeHeaders), + "serveChainSince" => Some(Key::ServeChainSince), + "serveStateSince" => Some(Key::ServeStateSince), + "txRelay" => Some(Key::TxRelay), + "flowControl/BL" => Some(Key::BufferLimit), + "flowControl/MRC" => Some(Key::BufferCostTable), + "flowControl/MRR" => Some(Key::BufferRechargeRate), + _ => None + } + } + + fn is_recognized(s: &str) -> bool { + Key::from_str(s).is_some() + } +} + /// Network ID structure. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] @@ -29,6 +95,66 @@ pub enum NetworkId { Testnet = 0, } +impl NetworkId { + fn from_raw(raw: u32) -> Option { + match raw { + 0 => Some(NetworkId::Testnet), + 1 => Some(NetworkId::Mainnet), + _ => None, + } + } +} + +// helper for decoding key-value pairs in the handshake or an announcement. +struct Parser<'a> { + pos: usize, + rlp: UntrustedRlp<'a>, +} + +impl<'a> Parser<'a> { + // attempt to parse the next key, value pair, and decode the value to the given type. + fn expect(&mut self, key: Key) -> Result { + self.expect_raw(key).and_then(|item| item.as_val()) + } + + // attempt to parse the next key, value pair, and returns the value's RLP. + fn expect_raw(&mut self, key: Key) -> Result, DecoderError> { + loop { + let pair = try!(self.rlp.at(self.pos)); + let k: String = try!(pair.val_at(0)); + let k = match Key::from_str(&k) { + Some(k) => k, + None => { + // skip any unrecognized keys. + self.pos += 1; + continue; + } + }; + + if k == key { + self.pos += 1; + return pair.at(1) + } else { + return Err(DecoderError::Custom("Missing expected key")) + } + } + } +} + +// Helper for encoding a key-value pair +fn encode_pair(key: Key, val: &T) -> Vec { + let mut s = RlpStream::new_list(2); + s.append(&key.as_str()).append(val); + s.out() +} + +// Helper for encoding a flag. +fn encode_flag(key: Key) -> Vec { + let mut s = RlpStream::new_list(2); + s.append(&key.as_str()).append_empty_data(); + s.out() +} + /// A peer status message. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Status { @@ -72,26 +198,80 @@ impl Default for Capabilities { } } -impl Capabilities { - /// Decode capabilities from the given rlp stream, starting from the given - /// index. - fn decode_from(rlp: &UntrustedRlp, start_idx: usize) -> Result { - let mut caps = Capabilities::default(); +/// Attempt to parse a handshake message into its three parts: +/// - chain status +/// - serving capabilities +/// - buffer flow parameters +pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, FlowParams), DecoderError> { + let mut parser = Parser { + pos: 0, + rlp: rlp, + }; - for item in rlp.iter().skip(start_idx).take(4) { - let key: String = try!(item.val_at(0)); + let status = Status { + protocol_version: try!(parser.expect(Key::ProtocolVersion)), + network_id: try!(parser.expect(Key::NetworkId) + .and_then(|id: u32| NetworkId::from_raw(id).ok_or(DecoderError::Custom("Invalid network ID")))), + head_td: try!(parser.expect(Key::HeadTD)), + head_hash: try!(parser.expect(Key::HeadHash)), + head_num: try!(parser.expect(Key::HeadNum)), + genesis_hash: parser.expect(Key::GenesisHash).ok(), + last_head: None, + }; - match &*key { - "serveHeaders" => caps.serve_headers = true, - "serveChainSince" => caps.serve_chain_since = Some(try!(item.val_at(1))), - "serveStateSince" => caps.serve_state_since = Some(try!(item.val_at(1))), - "txRelay" => caps.tx_relay = true, - _ => continue, - } - } + let capabilities = Capabilities { + serve_headers: parser.expect_raw(Key::ServeHeaders).is_ok(), + serve_chain_since: parser.expect(Key::ServeChainSince).ok(), + serve_state_since: parser.expect(Key::ServeStateSince).ok(), + tx_relay: parser.expect_raw(Key::TxRelay).is_ok(), + }; - Ok(caps) + let flow_params = FlowParams::new( + try!(parser.expect(Key::BufferLimit)), + try!(parser.expect(Key::BufferCostTable)), + try!(parser.expect(Key::BufferRechargeRate)), + ); + + Ok((status, capabilities, flow_params)) +} + +/// Write a handshake, given status, capabilities, and flow parameters. +pub fn write_handshake(status: &Status, capabilities: &Capabilities, flow_params: &FlowParams) -> Vec { + let mut pairs = Vec::new(); + pairs.push(encode_pair(Key::ProtocolVersion, &status.protocol_version)); + pairs.push(encode_pair(Key::NetworkId, &(status.network_id as u32))); + pairs.push(encode_pair(Key::HeadTD, &status.head_td)); + pairs.push(encode_pair(Key::HeadHash, &status.head_hash)); + pairs.push(encode_pair(Key::HeadNum, &status.head_num)); + + if let Some(ref genesis_hash) = status.genesis_hash { + pairs.push(encode_pair(Key::GenesisHash, genesis_hash)); } + + if capabilities.serve_headers { + pairs.push(encode_flag(Key::ServeHeaders)); + } + if let Some(ref serve_chain_since) = capabilities.serve_chain_since { + pairs.push(encode_pair(Key::ServeChainSince, serve_chain_since)); + } + if let Some(ref serve_state_since) = capabilities.serve_state_since { + pairs.push(encode_pair(Key::ServeStateSince, serve_state_since)); + } + if capabilities.tx_relay { + pairs.push(encode_flag(Key::TxRelay)); + } + + pairs.push(encode_pair(Key::BufferLimit, flow_params.limit())); + pairs.push(encode_pair(Key::BufferCostTable, flow_params.cost_table())); + pairs.push(encode_pair(Key::BufferRechargeRate, flow_params.recharge_rate())); + + let mut stream = RlpStream::new_list(pairs.len()); + + for pair in pairs { + stream.append_raw(&pair, 1); + } + + stream.out() } /// An announcement of new chain head or capabilities made by a peer. @@ -105,6 +285,137 @@ pub struct Announcement { head_td: U256, /// reorg depth to common ancestor of last announced head. reorg_depth: u64, - /// updated capabilities. - new_capabilities: Capabilities, + /// optional new state-serving capability + serve_state_since: Option, + /// optional new chain-serving capability + serve_chain_since: Option, + // TODO: changes in buffer flow? +} + +#[cfg(test)] +mod tests { + use super::*; + use super::super::buffer_flow::FlowParams; + use util::{U256, H256, FixedHash}; + use rlp::{RlpStream, Stream ,UntrustedRlp, View}; + + #[test] + fn full_handshake() { + let status = Status { + protocol_version: 1, + network_id: NetworkId::Mainnet, + head_td: U256::default(), + head_hash: H256::default(), + head_num: 10, + genesis_hash: Some(H256::zero()), + last_head: None, + }; + + let capabilities = Capabilities { + serve_headers: true, + serve_chain_since: Some(5), + serve_state_since: Some(8), + tx_relay: true, + }; + + let flow_params = FlowParams::new( + 1_000_000.into(), + Default::default(), + 1000.into(), + ); + + let handshake = write_handshake(&status, &capabilities, &flow_params); + + let (read_status, read_capabilities, read_flow) + = parse_handshake(UntrustedRlp::new(&handshake)).unwrap(); + + assert_eq!(read_status, status); + assert_eq!(read_capabilities, capabilities); + assert_eq!(read_flow, flow_params); + } + + #[test] + fn partial_handshake() { + let status = Status { + protocol_version: 1, + network_id: NetworkId::Mainnet, + head_td: U256::default(), + head_hash: H256::default(), + head_num: 10, + genesis_hash: None, + last_head: None, + }; + + let capabilities = Capabilities { + serve_headers: false, + serve_chain_since: Some(5), + serve_state_since: None, + tx_relay: true, + }; + + let flow_params = FlowParams::new( + 1_000_000.into(), + Default::default(), + 1000.into(), + ); + + let handshake = write_handshake(&status, &capabilities, &flow_params); + + let (read_status, read_capabilities, read_flow) + = parse_handshake(UntrustedRlp::new(&handshake)).unwrap(); + + assert_eq!(read_status, status); + assert_eq!(read_capabilities, capabilities); + assert_eq!(read_flow, flow_params); + } + + #[test] + fn skip_unknown_keys() { + let status = Status { + protocol_version: 1, + network_id: NetworkId::Mainnet, + head_td: U256::default(), + head_hash: H256::default(), + head_num: 10, + genesis_hash: None, + last_head: None, + }; + + let capabilities = Capabilities { + serve_headers: false, + serve_chain_since: Some(5), + serve_state_since: None, + tx_relay: true, + }; + + let flow_params = FlowParams::new( + 1_000_000.into(), + Default::default(), + 1000.into(), + ); + + let handshake = write_handshake(&status, &capabilities, &flow_params); + let interleaved = { + let handshake = UntrustedRlp::new(&handshake); + let mut stream = RlpStream::new_list(handshake.item_count() * 3); + + for item in handshake.iter() { + stream.append_raw(item.as_raw(), 1); + let (mut s1, mut s2) = (RlpStream::new_list(2), RlpStream::new_list(2)); + s1.append(&"foo").append_empty_data(); + s2.append(&"bar").append_empty_data(); + stream.append_raw(&s1.out(), 1); + stream.append_raw(&s2.out(), 1); + } + + stream.out() + }; + + let (read_status, read_capabilities, read_flow) + = parse_handshake(UntrustedRlp::new(&interleaved)).unwrap(); + + assert_eq!(read_status, status); + assert_eq!(read_capabilities, capabilities); + assert_eq!(read_flow, flow_params); + } } \ No newline at end of file From ca25deb4e6e16cf03204ef6864d8e460371a93db Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 8 Nov 2016 19:00:37 +0100 Subject: [PATCH 19/69] implement announcement serialization --- ethcore/light/src/net/status.rs | 179 ++++++++++++++++++++++++++------ 1 file changed, 150 insertions(+), 29 deletions(-) diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index 23b8b68c0..c48b73234 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -24,7 +24,7 @@ use super::buffer_flow::{CostTable, FlowParams}; // recognized handshake/announcement keys. // unknown keys are to be skipped, known keys have a defined order. // their string values are defined in the LES spec. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] enum Key { ProtocolVersion, NetworkId, @@ -61,6 +61,7 @@ impl Key { } } + // try to parse the key value from a string. fn from_str(s: &str) -> Option { match s { "protocolVersion" => Some(Key::ProtocolVersion), @@ -79,10 +80,6 @@ impl Key { _ => None } } - - fn is_recognized(s: &str) -> bool { - Key::from_str(s).is_some() - } } /// Network ID structure. @@ -112,32 +109,38 @@ struct Parser<'a> { } impl<'a> Parser<'a> { - // attempt to parse the next key, value pair, and decode the value to the given type. + // expect a specific next key, and decode the value. + // error on unexpected key or invalid value. fn expect(&mut self, key: Key) -> Result { self.expect_raw(key).and_then(|item| item.as_val()) } - // attempt to parse the next key, value pair, and returns the value's RLP. + // expect a specific next key, and get the value's RLP. + // if the key isn't found, the position isn't advanced. fn expect_raw(&mut self, key: Key) -> Result, DecoderError> { - loop { - let pair = try!(self.rlp.at(self.pos)); - let k: String = try!(pair.val_at(0)); - let k = match Key::from_str(&k) { - Some(k) => k, - None => { - // skip any unrecognized keys. - self.pos += 1; - continue; - } - }; + let pre_pos = self.pos; + if let Some((k, val)) = try!(self.get_next()) { + if k == key { return Ok(val) } + } - if k == key { - self.pos += 1; - return pair.at(1) - } else { - return Err(DecoderError::Custom("Missing expected key")) + self.pos = pre_pos; + Err(DecoderError::Custom("Missing expected key")) + } + + // get the next key and value RLP. + fn get_next(&mut self) -> Result)>, DecoderError> { + while self.pos < self.rlp.item_count() { + let pair = try!(self.rlp.at(self.pos)); + let k: String = try!(pair.val_at(0)); + + self.pos += 1; + match Key::from_str(&k) { + Some(key) => return Ok(Some((key , try!(pair.at(1))))), + None => continue, } } + + Ok(None) } } @@ -278,20 +281,90 @@ pub fn write_handshake(status: &Status, capabilities: &Capabilities, flow_params #[derive(Debug, Clone, PartialEq, Eq)] pub struct Announcement { /// Hash of the best block. - head_hash: H256, + pub head_hash: H256, /// Number of the best block. - head_num: u64, + pub head_num: u64, /// Head total difficulty - head_td: U256, + pub head_td: U256, /// reorg depth to common ancestor of last announced head. - reorg_depth: u64, + pub reorg_depth: u64, + /// optional new header-serving capability. false means "no change" + pub serve_headers: bool, /// optional new state-serving capability - serve_state_since: Option, + pub serve_state_since: Option, /// optional new chain-serving capability - serve_chain_since: Option, + pub serve_chain_since: Option, + /// optional new transaction-relay capability. false means "no change" + pub tx_relay: bool, // TODO: changes in buffer flow? } +/// Parse an announcement. +pub fn parse_announcement(rlp: UntrustedRlp) -> Result { + let mut last_key = None; + + let mut announcement = Announcement { + head_hash: try!(rlp.val_at(0)), + head_num: try!(rlp.val_at(1)), + head_td: try!(rlp.val_at(2)), + reorg_depth: try!(rlp.val_at(3)), + serve_headers: false, + serve_state_since: None, + serve_chain_since: None, + tx_relay: false, + }; + + let mut parser = Parser { + pos: 4, + rlp: rlp, + }; + + while let Some((key, item)) = try!(parser.get_next()) { + if Some(key) <= last_key { return Err(DecoderError::Custom("Invalid announcement key ordering")) } + last_key = Some(key); + + match key { + Key::ServeHeaders => announcement.serve_headers = true, + Key::ServeStateSince => announcement.serve_state_since = Some(try!(item.as_val())), + Key::ServeChainSince => announcement.serve_chain_since = Some(try!(item.as_val())), + Key::TxRelay => announcement.tx_relay = true, + _ => return Err(DecoderError::Custom("Nonsensical key in announcement")), + } + } + + Ok(announcement) +} + +/// Write an announcement out. +pub fn write_announcement(announcement: &Announcement) -> Vec { + let mut pairs = Vec::new(); + if announcement.serve_headers { + pairs.push(encode_flag(Key::ServeHeaders)); + } + if let Some(ref serve_chain_since) = announcement.serve_chain_since { + pairs.push(encode_pair(Key::ServeChainSince, serve_chain_since)); + } + if let Some(ref serve_state_since) = announcement.serve_state_since { + pairs.push(encode_pair(Key::ServeStateSince, serve_state_since)); + } + if announcement.tx_relay { + pairs.push(encode_flag(Key::TxRelay)); + } + + let mut stream = RlpStream::new_list(4 + pairs.len()); + stream + .append(&announcement.head_hash) + .append(&announcement.head_num) + .append(&announcement.head_td) + .append(&announcement.reorg_depth); + + for item in pairs { + stream.append_raw(&item, 1); + } + + stream.out() +} + #[cfg(test)] mod tests { use super::*; @@ -418,4 +491,52 @@ mod tests { assert_eq!(read_capabilities, capabilities); assert_eq!(read_flow, flow_params); } + + #[test] + fn announcement_roundtrip() { + let announcement = Announcement { + head_hash: H256::random(), + head_num: 100_000, + head_td: 1_000_000.into(), + reorg_depth: 4, + serve_headers: false, + serve_state_since: Some(99_000), + serve_chain_since: Some(1), + tx_relay: true, + }; + + let serialized = write_announcement(&announcement); + let read = parse_announcement(UntrustedRlp::new(&serialized)).unwrap(); + + assert_eq!(read, announcement); + } + + #[test] + fn keys_out_of_order() { + use super::{Key, encode_pair, encode_flag}; + + let mut stream = RlpStream::new_list(6); + stream + .append(&H256::zero()) + .append(&10u64) + .append(&100_000u64) + .append(&2u64) + .append_raw(&encode_pair(Key::ServeStateSince, &44u64), 1) + .append_raw(&encode_flag(Key::ServeHeaders), 1); + + let out = stream.drain(); + assert!(parse_announcement(UntrustedRlp::new(&out)).is_err()); + + let mut stream = RlpStream::new_list(6); + stream + .append(&H256::zero()) + .append(&10u64) + .append(&100_000u64) + .append(&2u64) + .append_raw(&encode_flag(Key::ServeHeaders), 1) + .append_raw(&encode_pair(Key::ServeStateSince, &44u64), 1); + + let out = stream.drain(); + assert!(parse_announcement(UntrustedRlp::new(&out)).is_ok()); + } } \ No newline at end of file From ec1b982b528205dcabe3d75d810ee77322306e7f Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 9 Nov 2016 15:36:26 +0100 Subject: [PATCH 20/69] errors, punishment, and handshake --- ethcore/light/src/client.rs | 11 ++- ethcore/light/src/net/error.rs | 94 ++++++++++++++++++ ethcore/light/src/net/mod.rs | 164 +++++++++++++++++++++----------- ethcore/light/src/net/status.rs | 19 ++-- ethcore/light/src/provider.rs | 8 +- 5 files changed, 225 insertions(+), 71 deletions(-) create mode 100644 ethcore/light/src/net/error.rs diff --git a/ethcore/light/src/client.rs b/ethcore/light/src/client.rs index 81355bf0f..fb4cb251f 100644 --- a/ethcore/light/src/client.rs +++ b/ethcore/light/src/client.rs @@ -52,7 +52,7 @@ impl Client { } /// Whether the block is already known (but not necessarily part of the canonical chain) - pub fn is_known(&self, id: BlockID) -> bool { + pub fn is_known(&self, _id: BlockID) -> bool { false } @@ -74,11 +74,18 @@ impl Client { // dummy implementation -- may draw from canonical cache further on. impl Provider for Client { - /// Get the chain info. fn chain_info(&self) -> BlockChainInfo { unimplemented!() } + fn reorg_depth(&self, _a: &H256, _b: &H256) -> Option { + None + } + + fn earliest_state(&self) -> Option { + None + } + fn block_headers(&self, _req: request::Headers) -> Vec { Vec::new() } diff --git a/ethcore/light/src/net/error.rs b/ethcore/light/src/net/error.rs new file mode 100644 index 000000000..e15bd50d3 --- /dev/null +++ b/ethcore/light/src/net/error.rs @@ -0,0 +1,94 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! Defines error types and levels of punishment to use upon +//! encountering. + +use rlp::DecoderError; +use network::NetworkError; + +use std::fmt; + +/// Levels of punishment. +/// +/// Currently just encompasses two different kinds of disconnect and +/// no punishment, but this is where reputation systems might come into play. +// In ascending order +#[derive(Debug, PartialEq, Eq)] +pub enum Punishment { + /// Perform no punishment. + None, + /// Disconnect the peer, but don't prevent them from reconnecting. + Disconnect, + /// Disconnect the peer and prevent them from reconnecting. + Disable, +} + +/// Kinds of errors which can be encountered in the course of LES. +#[derive(Debug)] +pub enum Error { + /// An RLP decoding error. + Rlp(DecoderError), + /// A network error. + Network(NetworkError), + /// Out of buffer. + BufferEmpty, + /// Unrecognized packet code. + UnrecognizedPacket(u8), + /// Unexpected handshake. + UnexpectedHandshake, + /// Peer on wrong network (wrong NetworkId or genesis hash) + WrongNetwork, +} + +impl Error { + /// What level of punishment does this error warrant? + pub fn punishment(&self) -> Punishment { + match *self { + Error::Rlp(_) => Punishment::Disable, + Error::Network(_) => Punishment::None, + Error::BufferEmpty => Punishment::Disable, + Error::UnrecognizedPacket(_) => Punishment::Disconnect, + Error::UnexpectedHandshake => Punishment::Disconnect, + Error::WrongNetwork => Punishment::Disable, + } + } +} + +impl From for Error { + fn from(err: DecoderError) -> Self { + Error::Rlp(err) + } +} + +impl From for Error { + fn from(err: NetworkError) -> Self { + Error::Network(err) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::Rlp(ref err) => err.fmt(f), + Error::Network(ref err) => err.fmt(f), + Error::BufferEmpty => write!(f, "Out of buffer"), + Error::UnrecognizedPacket(code) => write!(f, "Unrecognized packet: 0x{:x}", code), + Error::UnexpectedHandshake => write!(f, "Unexpected handshake"), + Error::WrongNetwork => write!(f, "Wrong network"), + } + } +} \ No newline at end of file diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 4f0543526..c0697407d 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -30,9 +30,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use provider::Provider; use request::{self, Request}; -use self::buffer_flow::FlowParams; + +use self::buffer_flow::{Buffer, FlowParams}; +use self::error::{Error, Punishment}; +use self::status::{Status, Capabilities}; mod buffer_flow; +mod error; mod status; const TIMEOUT: TimerToken = 0; @@ -80,10 +84,21 @@ mod packet { pub const HEADER_PROOFS: u8 = 0x0e; } +// A pending peer: one we've sent our status to but +// may not have received one for. +struct PendingPeer { + sent_head: H256, +} + // data about each peer. struct Peer { - buffer: u64, // remaining buffer value. + local_buffer: Buffer, // their buffer relative to us + remote_buffer: Buffer, // our buffer relative to them current_asking: HashSet, // pending request ids. + status: Status, + capabilities: Capabilities, + remote_flow: FlowParams, + sent_head: H256, // last head we've given them. } /// This is an implementation of the light ethereum network protocol, abstracted @@ -95,31 +110,32 @@ struct Peer { pub struct LightProtocol { provider: Box, genesis_hash: H256, - mainnet: bool, + network_id: status::NetworkId, + pending_peers: RwLock>, peers: RwLock>, pending_requests: RwLock>, + capabilities: RwLock, + flow_params: FlowParams, // assumed static and same for every peer. req_id: AtomicUsize, } impl LightProtocol { - // make a request to a given peer. - fn request_from(&self, peer: &PeerId, req: Request) { + // Check on the status of all pending requests. + fn check_pending_requests(&self) { unimplemented!() } // called when a peer connects. fn on_connect(&self, peer: &PeerId, io: &NetworkContext) { let peer = *peer; + match self.send_status(peer, io) { - Ok(()) => { - self.peers.write().insert(peer, Peer { - buffer: 0, - current_asking: HashSet::new(), - }); + Ok(pending_peer) => { + self.pending_peers.write().insert(peer, pending_peer); } Err(e) => { trace!(target: "les", "Error while sending status: {}", e); - io.disable_peer(peer); + io.disconnect_peer(peer); } } } @@ -127,119 +143,140 @@ impl LightProtocol { // called when a peer disconnects. fn on_disconnect(&self, peer: PeerId, io: &NetworkContext) { // TODO: reassign all requests assigned to this peer. + self.pending_peers.write().remove(&peer); self.peers.write().remove(&peer); } - fn send_status(&self, peer: PeerId, io: &NetworkContext) -> Result<(), NetworkError> { + // send status to a peer. + fn send_status(&self, peer: PeerId, io: &NetworkContext) -> Result { let chain_info = self.provider.chain_info(); - // TODO [rob] use optional keys too. - let mut stream = RlpStream::new_list(6); - stream - .begin_list(2) - .append(&"protocolVersion") - .append(&PROTOCOL_VERSION) - .begin_list(2) - .append(&"networkId") - .append(&(self.mainnet as u8)) - .begin_list(2) - .append(&"headTd") - .append(&chain_info.total_difficulty) - .begin_list(2) - .append(&"headHash") - .append(&chain_info.best_block_hash) - .begin_list(2) - .append(&"headNum") - .append(&chain_info.best_block_number) - .begin_list(2) - .append(&"genesisHash") - .append(&self.genesis_hash); + // TODO: could update capabilities here. - io.send(peer, packet::STATUS, stream.out()) + let status = Status { + head_td: chain_info.total_difficulty, + head_hash: chain_info.best_block_hash, + head_num: chain_info.best_block_number, + genesis_hash: chain_info.genesis_hash, + protocol_version: PROTOCOL_VERSION, + network_id: self.network_id, + last_head: None, + }; + + let capabilities = self.capabilities.read().clone(); + let status_packet = status::write_handshake(&status, &capabilities, &self.flow_params); + + try!(io.send(peer, packet::STATUS, status_packet)); + + Ok(PendingPeer { + sent_head: chain_info.best_block_hash, + }) } - /// Check on the status of all pending requests. - fn check_pending_requests(&self) { - unimplemented!() - } + // Handle status message from peer. + fn status(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + let pending = match self.pending_peers.write().remove(peer) { + Some(pending) => pending, + None => { + return Err(Error::UnexpectedHandshake); + } + }; - fn status(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { - unimplemented!() + let (status, capabilities, flow_params) = try!(status::parse_handshake(data)); + + trace!(target: "les", "Connected peer with chain head {:?}", (status.head_hash, status.head_num)); + + if (status.network_id, status.genesis_hash) != (self.network_id, self.genesis_hash) { + return Err(Error::WrongNetwork); + } + + self.peers.write().insert(*peer, Peer { + local_buffer: self.flow_params.create_buffer(), + remote_buffer: flow_params.create_buffer(), + current_asking: HashSet::new(), + status: status, + capabilities: capabilities, + remote_flow: flow_params, + sent_head: pending.sent_head, + }); + + + Ok(()) } // Handle an announcement. - fn announcement(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn announcement(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_NEW_HASHES: usize = 256; unimplemented!() } // Handle a request for block headers. - fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_HEADERS: u64 = 512; unimplemented!() } // Receive a response for block headers. - fn block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for block bodies. - fn get_block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn get_block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_BODIES: usize = 512; unimplemented!() } // Receive a response for block bodies. - fn block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for receipts. - fn get_receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn get_receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a response for receipts. - fn receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for proofs. - fn get_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn get_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a response for proofs. - fn proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for contract code. - fn get_contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn get_contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a response for contract code. - fn contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for header proofs - fn get_header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn get_header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a response for header proofs - fn header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a set of transactions to relay. - fn relay_transactions(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) { + fn relay_transactions(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { unimplemented!() } } @@ -251,7 +288,7 @@ impl NetworkProtocolHandler for LightProtocol { fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { let rlp = UntrustedRlp::new(data); - match packet_id { + let res = match packet_id { packet::STATUS => self.status(peer, io, rlp), packet::ANNOUNCE => self.announcement(peer, io, rlp), @@ -273,8 +310,21 @@ impl NetworkProtocolHandler for LightProtocol { packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), other => { - debug!(target: "les", "Disconnecting peer {} on unexpected packet {}", peer, other); - io.disconnect_peer(*peer); + Err(Error::UnrecognizedPacket(other)) + } + }; + + if let Err(e) = res { + match e.punishment() { + Punishment::None => {} + Punishment::Disconnect => { + debug!(target: "les", "Disconnecting peer {}: {}", peer, e); + io.disconnect_peer(*peer) + } + Punishment::Disable => { + debug!(target: "les", "Disabling peer {}: {}", peer, e); + io.disable_peer(*peer) + } } } } diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index c48b73234..1cac14845 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -172,7 +172,7 @@ pub struct Status { /// Number of the best block. pub head_num: u64, /// Genesis hash - pub genesis_hash: Option, + pub genesis_hash: H256, /// Last announced chain head and reorg depth to common ancestor. pub last_head: Option<(H256, u64)>, } @@ -182,7 +182,7 @@ pub struct Status { pub struct Capabilities { /// Whether this peer can serve headers pub serve_headers: bool, - /// Earliest block number it can serve chain requests for. + /// Earliest block number it can serve block/receipt requests for. pub serve_chain_since: Option, /// Earliest block number it can serve state requests for. pub serve_state_since: Option, @@ -193,7 +193,7 @@ pub struct Capabilities { impl Default for Capabilities { fn default() -> Self { Capabilities { - serve_headers: false, + serve_headers: true, serve_chain_since: None, serve_state_since: None, tx_relay: false, @@ -218,7 +218,7 @@ pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, FlowP head_td: try!(parser.expect(Key::HeadTD)), head_hash: try!(parser.expect(Key::HeadHash)), head_num: try!(parser.expect(Key::HeadNum)), - genesis_hash: parser.expect(Key::GenesisHash).ok(), + genesis_hash: try!(parser.expect(Key::GenesisHash)), last_head: None, }; @@ -246,10 +246,7 @@ pub fn write_handshake(status: &Status, capabilities: &Capabilities, flow_params pairs.push(encode_pair(Key::HeadTD, &status.head_td)); pairs.push(encode_pair(Key::HeadHash, &status.head_hash)); pairs.push(encode_pair(Key::HeadNum, &status.head_num)); - - if let Some(ref genesis_hash) = status.genesis_hash { - pairs.push(encode_pair(Key::GenesisHash, genesis_hash)); - } + pairs.push(encode_pair(Key::GenesisHash, &status.genesis_hash)); if capabilities.serve_headers { pairs.push(encode_flag(Key::ServeHeaders)); @@ -380,7 +377,7 @@ mod tests { head_td: U256::default(), head_hash: H256::default(), head_num: 10, - genesis_hash: Some(H256::zero()), + genesis_hash: H256::zero(), last_head: None, }; @@ -415,7 +412,7 @@ mod tests { head_td: U256::default(), head_hash: H256::default(), head_num: 10, - genesis_hash: None, + genesis_hash: H256::zero(), last_head: None, }; @@ -450,7 +447,7 @@ mod tests { head_td: U256::default(), head_hash: H256::default(), head_num: 10, - genesis_hash: None, + genesis_hash: H256::zero(), last_head: None, }; diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index d6458bedd..b1625f95f 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -19,7 +19,7 @@ use ethcore::transaction::SignedTransaction; use ethcore::blockchain_info::BlockChainInfo; -use util::Bytes; +use util::{Bytes, H256}; use request; @@ -33,6 +33,12 @@ pub trait Provider: Send + Sync { /// Provide current blockchain info. fn chain_info(&self) -> BlockChainInfo; + /// Find the depth of a common ancestor between two blocks. + fn reorg_depth(&self, a: &H256, b: &H256) -> Option; + + /// Earliest state. + fn earliest_state(&self) -> Option; + /// Provide a list of headers starting at the requested block, /// possibly in reverse and skipping `skip` at a time. /// From c132775bb17c6047a19015979fb3b4822a06a2cd Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 9 Nov 2016 16:21:09 +0100 Subject: [PATCH 21/69] handle announcements --- ethcore/light/src/net/mod.rs | 38 +++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index c0697407d..a721fde72 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -200,15 +200,47 @@ impl LightProtocol { sent_head: pending.sent_head, }); - Ok(()) } // Handle an announcement. fn announcement(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_NEW_HASHES: usize = 256; + if !self.peers.read().contains_key(peer) { + debug!(target: "les", "Ignoring announcement from unknown peer"); + return Ok(()) + } - unimplemented!() + let announcement = try!(status::parse_announcement(data)); + let mut peers = self.peers.write(); + + let peer_info = match peers.get_mut(peer) { + Some(info) => info, + None => return Ok(()), + }; + + // update status. + { + // TODO: punish peer if they've moved backwards. + let status = &mut peer_info.status; + let last_head = status.head_hash; + status.head_hash = announcement.head_hash; + status.head_td = announcement.head_td; + status.head_num = announcement.head_num; + status.last_head = Some((last_head, announcement.reorg_depth)); + } + + // update capabilities. + { + let caps = &mut peer_info.capabilities; + caps.serve_headers = caps.serve_headers || announcement.serve_headers; + caps.serve_state_since = caps.serve_state_since.or(announcement.serve_state_since); + caps.serve_chain_since = caps.serve_chain_since.or(announcement.serve_chain_since); + caps.tx_relay = caps.tx_relay || announcement.tx_relay; + } + + // TODO: notify listeners if new best block. + + Ok(()) } // Handle a request for block headers. From 25d5efac15142454386122ae3a97258bcad9da6d Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 9 Nov 2016 18:05:00 +0100 Subject: [PATCH 22/69] making announcements, clean up warnings --- ethcore/light/src/client.rs | 5 +- ethcore/light/src/net/buffer_flow.rs | 32 ++--- ethcore/light/src/net/mod.rs | 188 ++++++++++++++++++++++----- ethcore/light/src/net/status.rs | 2 +- ethcore/light/src/request.rs | 7 +- 5 files changed, 173 insertions(+), 61 deletions(-) diff --git a/ethcore/light/src/client.rs b/ethcore/light/src/client.rs index fb4cb251f..e3b5745b2 100644 --- a/ethcore/light/src/client.rs +++ b/ethcore/light/src/client.rs @@ -21,11 +21,10 @@ use std::sync::Arc; use ethcore::engines::Engine; use ethcore::ids::BlockID; -use ethcore::miner::TransactionQueue; use ethcore::service::ClientIoMessage; use ethcore::block_import_error::BlockImportError; use ethcore::block_status::BlockStatus; -use ethcore::verification::queue::{Config as QueueConfig, HeaderQueue, QueueInfo, Status}; +use ethcore::verification::queue::{HeaderQueue, QueueInfo}; use ethcore::transaction::SignedTransaction; use ethcore::blockchain_info::BlockChainInfo; @@ -62,7 +61,7 @@ impl Client { } /// Inquire about the status of a given block. - pub fn status(&self, id: BlockID) -> BlockStatus { + pub fn status(&self, _id: BlockID) -> BlockStatus { BlockStatus::Unknown } diff --git a/ethcore/light/src/net/buffer_flow.rs b/ethcore/light/src/net/buffer_flow.rs index 7ee287533..ff4c46f16 100644 --- a/ethcore/light/src/net/buffer_flow.rs +++ b/ethcore/light/src/net/buffer_flow.rs @@ -23,8 +23,9 @@ //! This module provides an interface for configuration of buffer //! flow costs and recharge rates. -use request::{self, Request}; +use request; use super::packet; +use super::error::Error; use rlp::*; use util::U256; @@ -34,10 +35,6 @@ use time::{Duration, SteadyTime}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Cost(pub U256, pub U256); -/// An error: insufficient buffer. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct InsufficientBuffer; - /// Buffer value. /// /// Produced and recharged using `FlowParams`. @@ -50,6 +47,9 @@ pub struct Buffer { } impl Buffer { + /// Get the current buffer value. + pub fn current(&self) -> U256 { self.estimate.clone() } + /// Make a definitive update. /// This will be the value obtained after receiving /// a response to a request. @@ -59,12 +59,14 @@ impl Buffer { } /// Attempt to apply the given cost to the buffer. + /// /// If successful, the cost will be deducted successfully. + /// /// If unsuccessful, the structure will be unaltered an an /// error will be produced. - pub fn deduct_cost(&mut self, cost: U256) -> Result<(), InsufficientBuffer> { + pub fn deduct_cost(&mut self, cost: U256) -> Result<(), Error> { match cost > self.estimate { - true => Err(InsufficientBuffer), + true => Err(Error::BufferEmpty), false => { self.estimate = self.estimate - cost; Ok(()) @@ -188,23 +190,9 @@ impl FlowParams { /// Get a reference to the recharge rate. pub fn recharge_rate(&self) -> &U256 { &self.recharge } - /// Estimate the maximum cost of the request. - pub fn max_cost(&self, req: &Request) -> U256 { - let amount = match *req { - Request::Headers(ref req) => req.max as usize, - Request::Bodies(ref req) => req.block_hashes.len(), - Request::Receipts(ref req) => req.block_hashes.len(), - Request::StateProofs(ref req) => req.requests.len(), - Request::Codes(ref req) => req.code_requests.len(), - Request::HeaderProofs(ref req) => req.requests.len(), - }; - - self.actual_cost(req.kind(), amount) - } - /// Compute the actual cost of a request, given the kind of request /// and number of requests made. - pub fn actual_cost(&self, kind: request::Kind, amount: usize) -> U256 { + pub fn compute_cost(&self, kind: request::Kind, amount: usize) -> U256 { let cost = match kind { request::Kind::Headers => &self.costs.headers, request::Kind::Bodies => &self.costs.bodies, diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index a721fde72..e72ce4bb2 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -20,13 +20,13 @@ //! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) use io::TimerToken; -use network::{NetworkProtocolHandler, NetworkService, NetworkContext, NetworkError, PeerId}; -use rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View}; +use network::{NetworkProtocolHandler, NetworkContext, NetworkError, PeerId}; +use rlp::{RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; -use util::{U256, Mutex, RwLock}; +use util::RwLock; use std::collections::{HashMap, HashSet}; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::AtomicUsize; use provider::Provider; use request::{self, Request}; @@ -39,6 +39,8 @@ mod buffer_flow; mod error; mod status; +pub use self::status::Announcement; + const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; @@ -120,11 +122,38 @@ pub struct LightProtocol { } impl LightProtocol { - // Check on the status of all pending requests. - fn check_pending_requests(&self) { - unimplemented!() - } + /// Make an announcement of new chain head and capabilities to all peers. + /// The announcement is expected to be valid. + pub fn make_announcement(&self, mut announcement: Announcement, io: &NetworkContext) { + let mut reorgs_map = HashMap::new(); + // calculate reorg info and send packets + for (peer_id, peer_info) in self.peers.write().iter_mut() { + let reorg_depth = reorgs_map.entry(peer_info.sent_head) + .or_insert_with(|| { + match self.provider.reorg_depth(&announcement.head_hash, &peer_info.sent_head) { + Some(depth) => depth, + None => { + // both values will always originate locally -- this means something + // has gone really wrong + debug!(target: "les", "couldn't compute reorganization depth between {:?} and {:?}", + &announcement.head_hash, &peer_info.sent_head); + 0 + } + } + }); + + peer_info.sent_head = announcement.head_hash; + announcement.reorg_depth = *reorg_depth; + + if let Err(e) = io.send(*peer_id, packet::ANNOUNCE, status::write_announcement(&announcement)) { + debug!(target: "les", "Error sending to peer {}: {}", peer_id, e); + } + } + } +} + +impl LightProtocol { // called when a peer connects. fn on_connect(&self, peer: &PeerId, io: &NetworkContext) { let peer = *peer; @@ -141,7 +170,7 @@ impl LightProtocol { } // called when a peer disconnects. - fn on_disconnect(&self, peer: PeerId, io: &NetworkContext) { + fn on_disconnect(&self, peer: PeerId) { // TODO: reassign all requests assigned to this peer. self.pending_peers.write().remove(&peer); self.peers.write().remove(&peer); @@ -174,7 +203,7 @@ impl LightProtocol { } // Handle status message from peer. - fn status(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn status(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> { let pending = match self.pending_peers.write().remove(peer) { Some(pending) => pending, None => { @@ -204,7 +233,7 @@ impl LightProtocol { } // Handle an announcement. - fn announcement(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn announcement(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> { if !self.peers.read().contains_key(peer) { debug!(target: "les", "Ignoring announcement from unknown peer"); return Ok(()) @@ -245,70 +274,161 @@ impl LightProtocol { // Handle a request for block headers. fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_HEADERS: u64 = 512; + const MAX_HEADERS: usize = 512; - unimplemented!() + let mut present_buffer = match self.peers.read().get(peer) { + Some(peer) => peer.local_buffer.clone(), + None => { + debug!(target: "les", "Ignoring announcement from unknown peer"); + return Ok(()) + } + }; + + self.flow_params.recharge(&mut present_buffer); + let req_id: u64 = try!(data.val_at(0)); + + let req = request::Headers { + block: { + let rlp = try!(data.at(1)); + (try!(rlp.val_at(0)), try!(rlp.val_at(1))) + }, + max: ::std::cmp::min(MAX_HEADERS, try!(data.val_at(2))), + skip: try!(data.val_at(3)), + reverse: try!(data.val_at(4)), + }; + + let max_cost = self.flow_params.compute_cost(request::Kind::Headers, req.max); + try!(present_buffer.deduct_cost(max_cost)); + + let response = self.provider.block_headers(req); + let actual_cost = self.flow_params.compute_cost(request::Kind::Headers, response.len()); + + let cur_buffer = match self.peers.write().get_mut(peer) { + Some(peer) => { + self.flow_params.recharge(&mut peer.local_buffer); + try!(peer.local_buffer.deduct_cost(actual_cost)); + peer.local_buffer.current() + } + None => { + debug!(target: "les", "peer disconnected during serving of request."); + return Ok(()) + } + }; + + io.respond(packet::BLOCK_HEADERS, { + let mut stream = RlpStream::new_list(response.len() + 2); + stream.append(&req_id).append(&cur_buffer); + + for header in response { + stream.append_raw(&header, 1); + } + + stream.out() + }).map_err(Into::into) } // Receive a response for block headers. - fn block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn block_headers(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for block bodies. fn get_block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_BODIES: usize = 512; + const MAX_BODIES: usize = 256; - unimplemented!() + let mut present_buffer = match self.peers.read().get(peer) { + Some(peer) => peer.local_buffer.clone(), + None => { + debug!(target: "les", "Ignoring announcement from unknown peer"); + return Ok(()) + } + }; + + self.flow_params.recharge(&mut present_buffer); + let req_id: u64 = try!(data.val_at(0)); + + let req = request::Bodies { + block_hashes: try!(data.iter().skip(1).take(MAX_BODIES).map(|x| x.as_val()).collect()) + }; + + let max_cost = self.flow_params.compute_cost(request::Kind::Bodies, req.block_hashes.len()); + try!(present_buffer.deduct_cost(max_cost)); + + let response = self.provider.block_bodies(req); + let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count(); + let actual_cost = self.flow_params.compute_cost(request::Kind::Bodies, response_len); + + let cur_buffer = match self.peers.write().get_mut(peer) { + Some(peer) => { + self.flow_params.recharge(&mut peer.local_buffer); + try!(peer.local_buffer.deduct_cost(actual_cost)); + peer.local_buffer.current() + } + None => { + debug!(target: "les", "peer disconnected during serving of request."); + return Ok(()) + } + }; + + io.respond(packet::BLOCK_BODIES, { + let mut stream = RlpStream::new_list(response.len() + 2); + stream.append(&req_id).append(&cur_buffer); + + for body in response { + stream.append_raw(&body, 1); + } + + stream.out() + }).map_err(Into::into) } // Receive a response for block bodies. - fn block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn block_bodies(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for receipts. - fn get_receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_receipts(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a response for receipts. - fn receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn receipts(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for proofs. - fn get_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a response for proofs. - fn proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for contract code. - fn get_contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_contract_code(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a response for contract code. - fn contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn contract_code(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Handle a request for header proofs - fn get_header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_header_proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a response for header proofs - fn header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn header_proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } // Receive a set of transactions to relay. - fn relay_transactions(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn relay_transactions(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { unimplemented!() } } @@ -320,9 +440,11 @@ impl NetworkProtocolHandler for LightProtocol { fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { let rlp = UntrustedRlp::new(data); + + // handle the packet let res = match packet_id { - packet::STATUS => self.status(peer, io, rlp), - packet::ANNOUNCE => self.announcement(peer, io, rlp), + packet::STATUS => self.status(peer, rlp), + packet::ANNOUNCE => self.announcement(peer, rlp), packet::GET_BLOCK_HEADERS => self.get_block_headers(peer, io, rlp), packet::BLOCK_HEADERS => self.block_headers(peer, io, rlp), @@ -339,6 +461,9 @@ impl NetworkProtocolHandler for LightProtocol { packet::GET_CONTRACT_CODES => self.get_contract_code(peer, io, rlp), packet::CONTRACT_CODES => self.contract_code(peer, io, rlp), + packet::GET_HEADER_PROOFS => self.get_header_proofs(peer, io, rlp), + packet::HEADER_PROOFS => self.header_proofs(peer, io, rlp), + packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), other => { @@ -346,6 +471,7 @@ impl NetworkProtocolHandler for LightProtocol { } }; + // if something went wrong, figure out how much to punish the peer. if let Err(e) = res { match e.punishment() { Punishment::None => {} @@ -365,11 +491,11 @@ impl NetworkProtocolHandler for LightProtocol { self.on_connect(peer, io); } - fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { - self.on_disconnect(*peer, io); + fn disconnected(&self, _io: &NetworkContext, peer: &PeerId) { + self.on_disconnect(*peer); } - fn timeout(&self, io: &NetworkContext, timer: TimerToken) { + fn timeout(&self, _io: &NetworkContext, timer: TimerToken) { match timer { TIMEOUT => { // broadcast transactions to peers. diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index 1cac14845..5aaea9f3a 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -19,7 +19,7 @@ use rlp::{DecoderError, RlpDecodable, RlpEncodable, RlpStream, Stream, UntrustedRlp, View}; use util::{H256, U256}; -use super::buffer_flow::{CostTable, FlowParams}; +use super::buffer_flow::FlowParams; // recognized handshake/announcement keys. // unknown keys are to be skipped, known keys have a defined order. diff --git a/ethcore/light/src/request.rs b/ethcore/light/src/request.rs index 11b474ee5..f043f0f25 100644 --- a/ethcore/light/src/request.rs +++ b/ethcore/light/src/request.rs @@ -18,8 +18,7 @@ // TODO: make IPC compatible. -use ethcore::transaction::Transaction; -use util::{Address, H256}; +use util::H256; /// A request for block headers. #[derive(Debug, Clone, PartialEq, Eq)] @@ -27,9 +26,9 @@ pub struct Headers { /// Block information for the request being made. pub block: (u64, H256), /// The maximum amount of headers which can be returned. - pub max: u64, + pub max: usize, /// The amount of headers to skip between each response entry. - pub skip: u64, + pub skip: usize, /// Whether the headers should proceed in falling number from the initial block. pub reverse: bool, } From 6c23d53f04f0f90ed3b9f62e8710f27b97a14ab5 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 9 Nov 2016 18:05:56 +0100 Subject: [PATCH 23/69] allow dead code temporarily --- ethcore/light/src/lib.rs | 3 +++ ethcore/light/src/net/buffer_flow.rs | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index f6d05c65c..07e6833a7 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -28,6 +28,9 @@ //! It starts by performing a header-only sync, verifying random samples //! of members of the chain to varying degrees. +// TODO: remove when integrating with parity. +#![allow(dead_code)] + pub mod client; pub mod net; pub mod provider; diff --git a/ethcore/light/src/net/buffer_flow.rs b/ethcore/light/src/net/buffer_flow.rs index ff4c46f16..b7bd30f82 100644 --- a/ethcore/light/src/net/buffer_flow.rs +++ b/ethcore/light/src/net/buffer_flow.rs @@ -233,7 +233,6 @@ impl FlowParams { #[cfg(test)] mod tests { use super::*; - use util::U256; #[test] fn should_serialize_cost_table() { From ebff010d16426ec7577a186f0d4c34c4b2eb96be Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 9 Nov 2016 23:25:54 +0100 Subject: [PATCH 24/69] partial implementation of provider for client types --- ethcore/light/src/provider.rs | 56 +++++++++++++++++++++++++++++-- ethcore/src/client/client.rs | 13 +++++-- ethcore/src/client/mod.rs | 11 +++--- ethcore/src/client/test_client.rs | 8 +++++ ethcore/src/client/traits.rs | 4 +++ ethcore/src/types/pruning_info.rs | 30 +++++++++++++++++ 6 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 ethcore/src/types/pruning_info.rs diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index b1625f95f..3cabe3feb 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -17,8 +17,11 @@ //! A provider for the LES protocol. This is typically a full node, who can //! give as much data as necessary to its peers. +use ethcore::client::BlockChainClient; use ethcore::transaction::SignedTransaction; use ethcore::blockchain_info::BlockChainInfo; + +use rlp::EMPTY_LIST_RLP; use util::{Bytes, H256}; use request; @@ -36,8 +39,9 @@ pub trait Provider: Send + Sync { /// Find the depth of a common ancestor between two blocks. fn reorg_depth(&self, a: &H256, b: &H256) -> Option; - /// Earliest state. - fn earliest_state(&self) -> Option; + /// Earliest block where state queries are available. + /// All states between this value and + fn earliest_state(&self) -> u64; /// Provide a list of headers starting at the requested block, /// possibly in reverse and skipping `skip` at a time. @@ -67,5 +71,53 @@ pub trait Provider: Send + Sync { fn header_proofs(&self, req: request::HeaderProofs) -> Vec; /// Provide pending transactions. + fn pending_transactions(&self) -> Vec; +} + +// TODO [rob] move into trait definition file after ethcore crate +// is split up. ideally `ethcore-light` will be between `ethcore-blockchain` +// and `ethcore-client` +impl Provider for T { + fn chain_info(&self) -> BlockChainInfo { + BlockChainClient::chain_info(self) + } + + fn reorg_depth(&self, a: &H256, b: &H256) -> Option { + self.tree_route.map(|route| route.index as u64) + } + + fn earliest_state(&self) -> u64 { + self.pruning_info().earliest_state + } + + fn block_headers(&self, req: request::Headers) -> Vec { + unimplemented!() + } + + fn block_bodies(&self, req: request::Bodies) -> Vec { + req.block_hashes.into_iter() + .map(|hash| self.block_body(hash.into())) + .map(|body| body.unwrap_or_else(|| EMPTY_LIST_RLP.into())) + .collect() + } + + fn receipts(&self, req: request::Receipts) -> Vec { + req.block_hashes.into_iter() + .map(|hash| self.block_receipts(&hash) + .map(|receipts| receips.unwrap_or_else(|| EMPTY_LIST_RLP.into())) + .collect() + } + + fn proofs(&self, req: request::StateProofs) -> Vec { + unimplemented!() + } + + fn code(&self, req: request::ContractCodes) -> Vec; + + fn header_proofs(&self, req: request::HeaderProofs) -> Vec { + // TODO: [rob] implement CHT stuff on `ethcore` side. + req.requests.into_iter().map(|_| EMPTY_LIST_RLP.into()).collect() + } + fn pending_transactions(&self) -> Vec; } \ No newline at end of file diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index ec59e01cf..72356e91d 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -52,7 +52,7 @@ use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use client::{ BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, - ChainNotify, + ChainNotify, PruningInfo, }; use client::Error as ClientError; use env_info::EnvInfo; @@ -262,7 +262,7 @@ impl Client { } } - /// Register an action to be done if a mode change happens. + /// Register an action to be done if a mode change happens. pub fn on_mode_change(&self, f: F) where F: 'static + FnMut(&Mode) + Send { *self.on_mode_change.lock() = Some(Box::new(f)); } @@ -890,7 +890,7 @@ impl BlockChainClient for Client { trace!(target: "mode", "Making callback..."); f(&*mode) }, - _ => {} + _ => {} } } match new_mode { @@ -1226,6 +1226,13 @@ impl BlockChainClient for Client { self.uncle(id) .map(|header| self.engine.extra_info(&decode(&header))) } + + fn pruning_info(&self) -> PruningInfo { + PruningInfo { + earliest_chain: self.chain.read().first_block().unwrap_or(1), + earliest_state: self.state_db.lock().journal_db().earliest_era().unwrap_or(0), + } + } } impl MiningBlockChainClient for Client { diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 3898ab6cd..7eafbee36 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -25,18 +25,21 @@ mod client; pub use self::client::*; pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType}; pub use self::error::Error; -pub use types::ids::*; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; +pub use self::chain_notify::ChainNotify; +pub use self::traits::{BlockChainClient, MiningBlockChainClient}; + +pub use types::ids::*; pub use types::trace_filter::Filter as TraceFilter; +pub use types::pruning_info::PruningInfo; +pub use types::call_analytics::CallAnalytics; + pub use executive::{Executed, Executive, TransactOptions}; pub use env_info::{LastHashes, EnvInfo}; -pub use self::chain_notify::ChainNotify; -pub use types::call_analytics::CallAnalytics; pub use block_import_error::BlockImportError; pub use transaction_import::TransactionImportResult; pub use transaction_import::TransactionImportError; -pub use self::traits::{BlockChainClient, MiningBlockChainClient}; pub use verification::VerifierType; /// IPC interfaces diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 434edd3e8..713c55056 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -38,6 +38,7 @@ use evm::{Factory as EvmFactory, VMType, Schedule}; use miner::{Miner, MinerService, TransactionImportResult}; use spec::Spec; use types::mode::Mode; +use types::pruning_info::PruningInfo; use views::BlockView; use verification::queue::QueueInfo; @@ -650,4 +651,11 @@ impl BlockChainClient for TestBlockChainClient { fn mode(&self) -> Mode { Mode::Active } fn set_mode(&self, _: Mode) { unimplemented!(); } + + fn pruning_info(&self) -> PruningInfo { + PruningInfo { + earliest_chain: 1, + earlest_state: 1, + } + } } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 67092e986..2cbf52f6c 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -39,6 +39,7 @@ use types::call_analytics::CallAnalytics; use types::blockchain_info::BlockChainInfo; use types::block_status::BlockStatus; use types::mode::Mode; +use types::pruning_info::PruningInfo; #[ipc(client_ident="RemoteClient")] /// Blockchain database client. Owns and manages a blockchain and a block queue. @@ -241,6 +242,9 @@ pub trait BlockChainClient : Sync + Send { /// Returns engine-related extra info for `UncleID`. fn uncle_extra_info(&self, id: UncleID) -> Option>; + + /// Returns information about pruning/data availability. + fn pruning_info(&self) -> PruningInfo; } /// Extended client interface used for mining diff --git a/ethcore/src/types/pruning_info.rs b/ethcore/src/types/pruning_info.rs new file mode 100644 index 000000000..c49b7825b --- /dev/null +++ b/ethcore/src/types/pruning_info.rs @@ -0,0 +1,30 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! Information about portions of the state and chain which the client may serve. +//! +//! Currently assumes that a client will store everything past a certain point +//! or everything. Will be extended in the future to support a definition +//! of which portions of the ancient chain and current state trie are stored as well. + +/// Client pruning info. See module-level docs for more details. +#[derive(Debug, Clone, Binary)] +pub struct PruningInfo { + /// The first block which everything can be served after. + pub earliest_chain: u64 + /// The first block where state requests may be served. + pub earliest_state: u64 +} \ No newline at end of file From 11e6b08f023f406baf355ce8e94dd1cb05f4a530 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 9 Nov 2016 23:39:56 +0100 Subject: [PATCH 25/69] Move ethcore-light crate into ethcore/light module --- ethcore/light/Cargo.toml | 16 ---------------- ethcore/{light/src => src/light}/client.rs | 0 ethcore/{light/src/lib.rs => src/light/mod.rs} | 9 +-------- .../{light/src => src/light}/net/buffer_flow.rs | 0 ethcore/{light/src => src/light}/net/error.rs | 0 ethcore/{light/src => src/light}/net/mod.rs | 0 ethcore/{light/src => src/light}/net/status.rs | 0 ethcore/{light/src => src/light}/provider.rs | 0 ethcore/{light/src => src/light}/request.rs | 0 9 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 ethcore/light/Cargo.toml rename ethcore/{light/src => src/light}/client.rs (100%) rename ethcore/{light/src/lib.rs => src/light/mod.rs} (87%) rename ethcore/{light/src => src/light}/net/buffer_flow.rs (100%) rename ethcore/{light/src => src/light}/net/error.rs (100%) rename ethcore/{light/src => src/light}/net/mod.rs (100%) rename ethcore/{light/src => src/light}/net/status.rs (100%) rename ethcore/{light/src => src/light}/provider.rs (100%) rename ethcore/{light/src => src/light}/request.rs (100%) diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml deleted file mode 100644 index daf141de7..000000000 --- a/ethcore/light/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -description = "Parity LES primitives" -homepage = "https://ethcore.io" -license = "GPL-3.0" -name = "ethcore-light" -version = "1.5.0" -authors = ["Ethcore "] - -[dependencies] -log = "0.3" -ethcore = { path = ".." } -ethcore-util = { path = "../../util" } -ethcore-network = { path = "../../util/network" } -ethcore-io = { path = "../../util/io" } -rlp = { path = "../../util/rlp" } -time = "0.1" \ No newline at end of file diff --git a/ethcore/light/src/client.rs b/ethcore/src/light/client.rs similarity index 100% rename from ethcore/light/src/client.rs rename to ethcore/src/light/client.rs diff --git a/ethcore/light/src/lib.rs b/ethcore/src/light/mod.rs similarity index 87% rename from ethcore/light/src/lib.rs rename to ethcore/src/light/mod.rs index 07e6833a7..d96cedeaf 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/src/light/mod.rs @@ -28,7 +28,7 @@ //! It starts by performing a header-only sync, verifying random samples //! of members of the chain to varying degrees. -// TODO: remove when integrating with parity. +// TODO: remove when integrating with the rest of parity. #![allow(dead_code)] pub mod client; @@ -36,12 +36,5 @@ pub mod net; pub mod provider; pub mod request; -extern crate ethcore_util as util; -extern crate ethcore_network as network; -extern crate ethcore_io as io; -extern crate ethcore; -extern crate rlp; -extern crate time; - #[macro_use] extern crate log; \ No newline at end of file diff --git a/ethcore/light/src/net/buffer_flow.rs b/ethcore/src/light/net/buffer_flow.rs similarity index 100% rename from ethcore/light/src/net/buffer_flow.rs rename to ethcore/src/light/net/buffer_flow.rs diff --git a/ethcore/light/src/net/error.rs b/ethcore/src/light/net/error.rs similarity index 100% rename from ethcore/light/src/net/error.rs rename to ethcore/src/light/net/error.rs diff --git a/ethcore/light/src/net/mod.rs b/ethcore/src/light/net/mod.rs similarity index 100% rename from ethcore/light/src/net/mod.rs rename to ethcore/src/light/net/mod.rs diff --git a/ethcore/light/src/net/status.rs b/ethcore/src/light/net/status.rs similarity index 100% rename from ethcore/light/src/net/status.rs rename to ethcore/src/light/net/status.rs diff --git a/ethcore/light/src/provider.rs b/ethcore/src/light/provider.rs similarity index 100% rename from ethcore/light/src/provider.rs rename to ethcore/src/light/provider.rs diff --git a/ethcore/light/src/request.rs b/ethcore/src/light/request.rs similarity index 100% rename from ethcore/light/src/request.rs rename to ethcore/src/light/request.rs From 8c2c04844471e2923db06a066cb58953e1587544 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 10 Nov 2016 14:05:47 +0100 Subject: [PATCH 26/69] clean up errors --- Cargo.lock | 1 + ethcore/Cargo.toml | 1 + ethcore/src/client/client.rs | 2 +- ethcore/src/client/test_client.rs | 2 +- ethcore/src/lib.rs | 2 + ethcore/src/light/client.rs | 20 +++++----- ethcore/src/light/mod.rs | 4 +- ethcore/src/light/net/buffer_flow.rs | 2 +- ethcore/src/light/net/mod.rs | 15 ++++--- ethcore/src/light/provider.rs | 38 ++++++++++-------- .../request.rs => types/les_request.rs} | 40 ++++++++++++------- ethcore/src/types/mod.rs.in | 2 + ethcore/src/types/pruning_info.rs | 4 +- 13 files changed, 78 insertions(+), 55 deletions(-) rename ethcore/src/{light/request.rs => types/les_request.rs} (80%) diff --git a/Cargo.lock b/Cargo.lock index 925535d8a..78f04a9ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,6 +289,7 @@ dependencies = [ "ethcore-ipc 1.4.0", "ethcore-ipc-codegen 1.4.0", "ethcore-ipc-nano 1.4.0", + "ethcore-network 1.5.0", "ethcore-util 1.5.0", "ethjson 0.1.0", "ethkey 0.2.0", diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 667b40ace..825d9ffd1 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -41,6 +41,7 @@ ethcore-ipc-nano = { path = "../ipc/nano" } rlp = { path = "../util/rlp" } lru-cache = "0.1.0" ethcore-bloom-journal = { path = "../util/bloom" } +ethcore-network = { path = "../util/network" } [dependencies.hyper] git = "https://github.com/ethcore/hyper" diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 72356e91d..788596d86 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1229,7 +1229,7 @@ impl BlockChainClient for Client { fn pruning_info(&self) -> PruningInfo { PruningInfo { - earliest_chain: self.chain.read().first_block().unwrap_or(1), + earliest_chain: self.chain.read().first_block_number().unwrap_or(1), earliest_state: self.state_db.lock().journal_db().earliest_era().unwrap_or(0), } } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 713c55056..91f0b18ff 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -655,7 +655,7 @@ impl BlockChainClient for TestBlockChainClient { fn pruning_info(&self) -> PruningInfo { PruningInfo { earliest_chain: 1, - earlest_state: 1, + earliest_state: 1, } } } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index bf3e59171..2c07e7de6 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -102,6 +102,7 @@ extern crate rlp; extern crate ethcore_bloom_journal as bloom_journal; extern crate byteorder; extern crate transient_hashmap; +extern crate ethcore_network as network; #[macro_use] extern crate log; @@ -138,6 +139,7 @@ pub mod snapshot; pub mod action_params; pub mod db; pub mod verification; +pub mod light; #[macro_use] pub mod evm; mod cache_manager; diff --git a/ethcore/src/light/client.rs b/ethcore/src/light/client.rs index e3b5745b2..7b821d971 100644 --- a/ethcore/src/light/client.rs +++ b/ethcore/src/light/client.rs @@ -19,21 +19,21 @@ use std::sync::Arc; -use ethcore::engines::Engine; -use ethcore::ids::BlockID; -use ethcore::service::ClientIoMessage; -use ethcore::block_import_error::BlockImportError; -use ethcore::block_status::BlockStatus; -use ethcore::verification::queue::{HeaderQueue, QueueInfo}; -use ethcore::transaction::SignedTransaction; -use ethcore::blockchain_info::BlockChainInfo; +use engines::Engine; +use ids::BlockID; +use service::ClientIoMessage; +use block_import_error::BlockImportError; +use block_status::BlockStatus; +use verification::queue::{HeaderQueue, QueueInfo}; +use transaction::SignedTransaction; +use blockchain_info::BlockChainInfo; use io::IoChannel; use util::hash::H256; use util::{Bytes, Mutex}; -use provider::Provider; -use request; +use light::provider::Provider; +use light::request; /// Light client implementation. pub struct Client { diff --git a/ethcore/src/light/mod.rs b/ethcore/src/light/mod.rs index d96cedeaf..a7b2d3922 100644 --- a/ethcore/src/light/mod.rs +++ b/ethcore/src/light/mod.rs @@ -34,7 +34,5 @@ pub mod client; pub mod net; pub mod provider; -pub mod request; -#[macro_use] -extern crate log; \ No newline at end of file +pub use types::les_request as request; \ No newline at end of file diff --git a/ethcore/src/light/net/buffer_flow.rs b/ethcore/src/light/net/buffer_flow.rs index b7bd30f82..62f351515 100644 --- a/ethcore/src/light/net/buffer_flow.rs +++ b/ethcore/src/light/net/buffer_flow.rs @@ -23,7 +23,7 @@ //! This module provides an interface for configuration of buffer //! flow costs and recharge rates. -use request; +use light::request; use super::packet; use super::error::Error; diff --git a/ethcore/src/light/net/mod.rs b/ethcore/src/light/net/mod.rs index e72ce4bb2..47146a2f9 100644 --- a/ethcore/src/light/net/mod.rs +++ b/ethcore/src/light/net/mod.rs @@ -28,8 +28,8 @@ use util::RwLock; use std::collections::{HashMap, HashSet}; use std::sync::atomic::AtomicUsize; -use provider::Provider; -use request::{self, Request}; +use light::provider::Provider; +use light::request::{self, Request}; use self::buffer_flow::{Buffer, FlowParams}; use self::error::{Error, Punishment}; @@ -287,11 +287,14 @@ impl LightProtocol { self.flow_params.recharge(&mut present_buffer); let req_id: u64 = try!(data.val_at(0)); + let block = { + let rlp = try!(data.at(1)); + (try!(rlp.val_at(0)), try!(rlp.val_at(1))) + }; + let req = request::Headers { - block: { - let rlp = try!(data.at(1)); - (try!(rlp.val_at(0)), try!(rlp.val_at(1))) - }, + block_num: block.0, + block_hash: block.1, max: ::std::cmp::min(MAX_HEADERS, try!(data.val_at(2))), skip: try!(data.val_at(3)), reverse: try!(data.val_at(4)), diff --git a/ethcore/src/light/provider.rs b/ethcore/src/light/provider.rs index 3cabe3feb..481865643 100644 --- a/ethcore/src/light/provider.rs +++ b/ethcore/src/light/provider.rs @@ -17,14 +17,14 @@ //! A provider for the LES protocol. This is typically a full node, who can //! give as much data as necessary to its peers. -use ethcore::client::BlockChainClient; -use ethcore::transaction::SignedTransaction; -use ethcore::blockchain_info::BlockChainInfo; +use client::BlockChainClient; +use transaction::SignedTransaction; +use blockchain_info::BlockChainInfo; use rlp::EMPTY_LIST_RLP; use util::{Bytes, H256}; -use request; +use light::request; /// Defines the operations that a provider for `LES` must fulfill. /// @@ -40,8 +40,8 @@ pub trait Provider: Send + Sync { fn reorg_depth(&self, a: &H256, b: &H256) -> Option; /// Earliest block where state queries are available. - /// All states between this value and - fn earliest_state(&self) -> u64; + /// If `None`, no state queries are servable. + fn earliest_state(&self) -> Option; /// Provide a list of headers starting at the requested block, /// possibly in reverse and skipping `skip` at a time. @@ -83,11 +83,11 @@ impl Provider for T { } fn reorg_depth(&self, a: &H256, b: &H256) -> Option { - self.tree_route.map(|route| route.index as u64) + self.tree_route(a, b).map(|route| route.index as u64) } - fn earliest_state(&self) -> u64 { - self.pruning_info().earliest_state + fn earliest_state(&self) -> Option { + Some(self.pruning_info().earliest_state) } fn block_headers(&self, req: request::Headers) -> Vec { @@ -95,16 +95,18 @@ impl Provider for T { } fn block_bodies(&self, req: request::Bodies) -> Vec { + use ids::BlockID; + req.block_hashes.into_iter() - .map(|hash| self.block_body(hash.into())) - .map(|body| body.unwrap_or_else(|| EMPTY_LIST_RLP.into())) + .map(|hash| self.block_body(BlockID::Hash(hash))) + .map(|body| body.unwrap_or_else(|| EMPTY_LIST_RLP.to_vec())) .collect() } fn receipts(&self, req: request::Receipts) -> Vec { req.block_hashes.into_iter() - .map(|hash| self.block_receipts(&hash) - .map(|receipts| receips.unwrap_or_else(|| EMPTY_LIST_RLP.into())) + .map(|hash| self.block_receipts(&hash)) + .map(|receipts| receipts.unwrap_or_else(|| EMPTY_LIST_RLP.to_vec())) .collect() } @@ -112,12 +114,16 @@ impl Provider for T { unimplemented!() } - fn code(&self, req: request::ContractCodes) -> Vec; + fn code(&self, req: request::ContractCodes) -> Vec { + unimplemented!() + } fn header_proofs(&self, req: request::HeaderProofs) -> Vec { // TODO: [rob] implement CHT stuff on `ethcore` side. - req.requests.into_iter().map(|_| EMPTY_LIST_RLP.into()).collect() + req.requests.into_iter().map(|_| EMPTY_LIST_RLP.to_vec()).collect() } - fn pending_transactions(&self) -> Vec; + fn pending_transactions(&self) -> Vec { + unimplemented!() + } } \ No newline at end of file diff --git a/ethcore/src/light/request.rs b/ethcore/src/types/les_request.rs similarity index 80% rename from ethcore/src/light/request.rs rename to ethcore/src/types/les_request.rs index f043f0f25..e947c654d 100644 --- a/ethcore/src/light/request.rs +++ b/ethcore/src/types/les_request.rs @@ -16,15 +16,16 @@ //! LES request types. -// TODO: make IPC compatible. - use util::H256; /// A request for block headers. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct Headers { - /// Block information for the request being made. - pub block: (u64, H256), + /// Starting block number + pub block_num: u64, + /// Starting block hash. This and number could be combined but IPC codegen is + /// not robust enough to support it. + pub block_hash: H256, /// The maximum amount of headers which can be returned. pub max: usize, /// The amount of headers to skip between each response entry. @@ -34,7 +35,7 @@ pub struct Headers { } /// A request for specific block bodies. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct Bodies { /// Hashes which bodies are being requested for. pub block_hashes: Vec @@ -44,14 +45,14 @@ pub struct Bodies { /// /// This request is answered with a list of transaction receipts for each block /// requested. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct Receipts { /// Block hashes to return receipts for. pub block_hashes: Vec, } /// A request for a state proof -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct StateProof { /// Block hash to query state from. pub block: H256, @@ -65,21 +66,30 @@ pub struct StateProof { } /// A request for state proofs. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct StateProofs { /// All the proof requests. pub requests: Vec, } /// A request for contract code. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct ContractCode { + /// Block hash + pub block_hash: H256, + /// Account key (== sha3(address)) + pub account_key: H256, +} + +/// A request for contract code. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct ContractCodes { /// Block hash and account key (== sha3(address)) pairs to fetch code for. - pub code_requests: Vec<(H256, H256)>, + pub code_requests: Vec, } /// A request for a header proof from the Canonical Hash Trie. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct HeaderProof { /// Number of the CHT. pub cht_number: u64, @@ -90,14 +100,14 @@ pub struct HeaderProof { } /// A request for header proofs from the CHT. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct HeaderProofs { /// All the proof requests. pub requests: Vec, } /// Kinds of requests. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Binary)] pub enum Kind { /// Requesting headers. Headers, @@ -114,7 +124,7 @@ pub enum Kind { } /// Encompasses all possible types of requests in a single structure. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] pub enum Request { /// Requesting headers. Headers(Headers), diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in index 6ef67009a..05c2c4dba 100644 --- a/ethcore/src/types/mod.rs.in +++ b/ethcore/src/types/mod.rs.in @@ -34,3 +34,5 @@ pub mod block_import_error; pub mod restoration_status; pub mod snapshot_manifest; pub mod mode; +pub mod pruning_info; +pub mod les_request; \ No newline at end of file diff --git a/ethcore/src/types/pruning_info.rs b/ethcore/src/types/pruning_info.rs index c49b7825b..40564f488 100644 --- a/ethcore/src/types/pruning_info.rs +++ b/ethcore/src/types/pruning_info.rs @@ -24,7 +24,7 @@ #[derive(Debug, Clone, Binary)] pub struct PruningInfo { /// The first block which everything can be served after. - pub earliest_chain: u64 + pub earliest_chain: u64, /// The first block where state requests may be served. - pub earliest_state: u64 + pub earliest_state: u64, } \ No newline at end of file From abf39fde0a3339f6b190932784f6a125b85014cd Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 15 Nov 2016 14:53:30 +0100 Subject: [PATCH 27/69] implement provider for client --- ethcore/src/client/client.rs | 170 ++++++++++++++++++++++++++----- ethcore/src/light/client.rs | 2 +- ethcore/src/light/mod.rs | 1 + ethcore/src/light/provider.rs | 58 +---------- ethcore/src/state/account.rs | 21 ++++ ethcore/src/state/mod.rs | 52 +++++++++- ethcore/src/types/les_request.rs | 2 +- util/src/trie/mod.rs | 6 +- util/src/trie/recorder.rs | 2 +- 9 files changed, 227 insertions(+), 87 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 788596d86..1054f0614 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -68,6 +68,7 @@ use factory::Factories; use rlp::{decode, View, UntrustedRlp}; use state_db::StateDB; use rand::OsRng; +use light::{self, request}; // re-export pub use types::blockchain_info::BlockChainInfo; @@ -1317,32 +1318,155 @@ impl MayPanic for Client { } } +// Implementation of a light client data provider for a client. +impl light::Provider for Client { + fn chain_info(&self) -> BlockChainInfo { + BlockChainClient::chain_info(self) + } -#[test] -fn should_not_cache_details_before_commit() { - use tests::helpers::*; - use std::thread; - use std::time::Duration; - use std::sync::atomic::{AtomicBool, Ordering}; + fn reorg_depth(&self, a: &H256, b: &H256) -> Option { + self.tree_route(a, b).map(|route| route.index as u64) + } - let client = generate_dummy_client(0); - let genesis = client.chain_info().best_block_hash; - let (new_hash, new_block) = get_good_dummy_block_hash(); + fn earliest_state(&self) -> Option { + Some(self.pruning_info().earliest_state) + } - let go = { - // Separate thread uncommited transaction - let go = Arc::new(AtomicBool::new(false)); - let go_thread = go.clone(); - let another_client = client.reference().clone(); - thread::spawn(move || { - let mut batch = DBTransaction::new(&*another_client.chain.read().db().clone()); - another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new()); - go_thread.store(true, Ordering::SeqCst); - }); - go - }; + fn block_headers(&self, req: request::Headers) -> Vec { + let best_num = self.chain.read().best_block_number(); + let start_num = req.block_num; - while !go.load(Ordering::SeqCst) { thread::park_timeout(Duration::from_millis(5)); } + match self.block_hash(BlockID::Number(req.block_num)) { + Some(hash) if hash == req.block_hash => {} + _=> { + trace!(target: "les_provider", "unknown/non-canonical start block in header request: {:?}", (req.block_num, req.block_hash)); + return vec![] + } + } - assert!(client.tree_route(&genesis, &new_hash).is_none()); + (0u64..req.max as u64) + .map(|x: u64| x.saturating_mul(req.skip)) + .take_while(|x| if req.reverse { x < &start_num } else { best_num - start_num < *x }) + .map(|x| if req.reverse { start_num - x } else { start_num + x }) + .map(|x| self.block_header(BlockID::Number(x))) + .flat_map(|x| x) + .fuse() // collect no more beyond the first `None` + .collect() + } + + fn block_bodies(&self, req: request::Bodies) -> Vec { + use ids::BlockID; + + req.block_hashes.into_iter() + .map(|hash| self.block_body(BlockID::Hash(hash))) + .map(|body| body.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) + .collect() + } + + fn receipts(&self, req: request::Receipts) -> Vec { + req.block_hashes.into_iter() + .map(|hash| self.block_receipts(&hash)) + .map(|receipts| receipts.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) + .collect() + } + + fn proofs(&self, req: request::StateProofs) -> Vec { + use rlp::{EMPTY_LIST_RLP, RlpStream, Stream}; + + let mut results = Vec::with_capacity(req.requests.len()); + + for request in req.requests { + let state = match self.state_at(BlockID::Hash(request.block)) { + Some(state) => state, + None => { + trace!(target: "light_provider", "state for {} not available", request.block); + results.push(EMPTY_LIST_RLP.to_vec()); + continue; + } + }; + + let res = match request.key2 { + Some(storage_key) => state.prove_storage(request.key1, storage_key, request.from_level), + None => state.prove_account(request.key1, request.from_level), + }; + + match res { + Ok(records) => { + let mut stream = RlpStream::new_list(records.len()); + for record in records { + stream.append_raw(&record, 1); + } + results.push(stream.out()) + } + Err(e) => { + debug!(target: "light_provider", "encountered error {} while forming proof of state at {}", e, request.block); + results.push(EMPTY_LIST_RLP.to_vec()); + } + } + } + + results + } + + fn contract_code(&self, req: request::ContractCodes) -> Vec { + req.code_requests.into_iter() + .map(|req| { + self.state_at(BlockID::Hash(req.block_hash)) + .map(|state| { + match state.code_by_address_hash(req.account_key) { + Ok(code) => code.unwrap_or_else(Vec::new), + Err(e) => { + debug!(target: "light_provider", "encountered error {} while fetching code.", e); + Vec::new() + } + } + }).unwrap_or_else(Vec::new) + }) + .collect() + } + + fn header_proofs(&self, req: request::HeaderProofs) -> Vec { + req.requests.into_iter().map(|_| ::rlp::EMPTY_LIST_RLP.to_vec()).collect() + } + + fn pending_transactions(&self) -> Vec { + BlockChainClient::pending_transactions(self) + } } + +#[cfg(test)] +mod tests { + + #[test] + fn should_not_cache_details_before_commit() { + use client::BlockChainClient; + use tests::helpers::*; + + use std::thread; + use std::time::Duration; + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, Ordering}; + use util::kvdb::DBTransaction; + + let client = generate_dummy_client(0); + let genesis = client.chain_info().best_block_hash; + let (new_hash, new_block) = get_good_dummy_block_hash(); + + let go = { + // Separate thread uncommited transaction + let go = Arc::new(AtomicBool::new(false)); + let go_thread = go.clone(); + let another_client = client.reference().clone(); + thread::spawn(move || { + let mut batch = DBTransaction::new(&*another_client.chain.read().db().clone()); + another_client.chain.read().insert_block(&mut batch, &new_block, Vec::new()); + go_thread.store(true, Ordering::SeqCst); + }); + go + }; + + while !go.load(Ordering::SeqCst) { thread::park_timeout(Duration::from_millis(5)); } + + assert!(client.tree_route(&genesis, &new_hash).is_none()); + } +} \ No newline at end of file diff --git a/ethcore/src/light/client.rs b/ethcore/src/light/client.rs index 7b821d971..699db743a 100644 --- a/ethcore/src/light/client.rs +++ b/ethcore/src/light/client.rs @@ -101,7 +101,7 @@ impl Provider for Client { Vec::new() } - fn code(&self, _req: request::ContractCodes) -> Vec { + fn contract_code(&self, _req: request::ContractCodes) -> Vec { Vec::new() } diff --git a/ethcore/src/light/mod.rs b/ethcore/src/light/mod.rs index a7b2d3922..dca592453 100644 --- a/ethcore/src/light/mod.rs +++ b/ethcore/src/light/mod.rs @@ -35,4 +35,5 @@ pub mod client; pub mod net; pub mod provider; +pub use self::provider::Provider; pub use types::les_request as request; \ No newline at end of file diff --git a/ethcore/src/light/provider.rs b/ethcore/src/light/provider.rs index 481865643..bf65acd18 100644 --- a/ethcore/src/light/provider.rs +++ b/ethcore/src/light/provider.rs @@ -17,11 +17,9 @@ //! A provider for the LES protocol. This is typically a full node, who can //! give as much data as necessary to its peers. -use client::BlockChainClient; use transaction::SignedTransaction; use blockchain_info::BlockChainInfo; -use rlp::EMPTY_LIST_RLP; use util::{Bytes, H256}; use light::request; @@ -65,65 +63,11 @@ pub trait Provider: Send + Sync { fn proofs(&self, req: request::StateProofs) -> Vec; /// Provide contract code for the specified (block_hash, account_hash) pairs. - fn code(&self, req: request::ContractCodes) -> Vec; + fn contract_code(&self, req: request::ContractCodes) -> Vec; /// Provide header proofs from the Canonical Hash Tries. fn header_proofs(&self, req: request::HeaderProofs) -> Vec; /// Provide pending transactions. fn pending_transactions(&self) -> Vec; -} - -// TODO [rob] move into trait definition file after ethcore crate -// is split up. ideally `ethcore-light` will be between `ethcore-blockchain` -// and `ethcore-client` -impl Provider for T { - fn chain_info(&self) -> BlockChainInfo { - BlockChainClient::chain_info(self) - } - - fn reorg_depth(&self, a: &H256, b: &H256) -> Option { - self.tree_route(a, b).map(|route| route.index as u64) - } - - fn earliest_state(&self) -> Option { - Some(self.pruning_info().earliest_state) - } - - fn block_headers(&self, req: request::Headers) -> Vec { - unimplemented!() - } - - fn block_bodies(&self, req: request::Bodies) -> Vec { - use ids::BlockID; - - req.block_hashes.into_iter() - .map(|hash| self.block_body(BlockID::Hash(hash))) - .map(|body| body.unwrap_or_else(|| EMPTY_LIST_RLP.to_vec())) - .collect() - } - - fn receipts(&self, req: request::Receipts) -> Vec { - req.block_hashes.into_iter() - .map(|hash| self.block_receipts(&hash)) - .map(|receipts| receipts.unwrap_or_else(|| EMPTY_LIST_RLP.to_vec())) - .collect() - } - - fn proofs(&self, req: request::StateProofs) -> Vec { - unimplemented!() - } - - fn code(&self, req: request::ContractCodes) -> Vec { - unimplemented!() - } - - fn header_proofs(&self, req: request::HeaderProofs) -> Vec { - // TODO: [rob] implement CHT stuff on `ethcore` side. - req.requests.into_iter().map(|_| EMPTY_LIST_RLP.to_vec()).collect() - } - - fn pending_transactions(&self) -> Vec { - unimplemented!() - } } \ No newline at end of file diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index 76061f6a0..4cca0f645 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -437,6 +437,27 @@ impl Account { } } +// light client storage proof. +impl Account { + /// Prove a storage key's existence or nonexistence in the account's storage + /// trie. + /// `storage_key` is the hash of the desired storage key, meaning + /// this will only work correctly under a secure trie. + /// Returns a merkle proof of the storage trie node with all nodes before `from_level` + /// omitted. + pub fn prove_storage(&self, db: &HashDB, storage_key: H256, from_level: u32) -> Result, Box> { + use util::trie::{Trie, TrieDB}; + use util::trie::recorder::{Recorder, BasicRecorder as TrieRecorder}; + + let mut recorder = TrieRecorder::with_depth(from_level); + + let trie = try!(TrieDB::new(db, &self.storage_root)); + let _ = try!(trie.get_recorded(&storage_key, &mut recorder)); + + Ok(recorder.drain().into_iter().map(|r| r.data).collect()) + } +} + impl fmt::Debug for Account { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", PodAccount::from_account(self)) diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 01a7e3b15..bedc46d9c 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -16,7 +16,7 @@ use std::cell::{RefCell, RefMut}; use std::collections::hash_map::Entry; -use util::*; + use receipt::Receipt; use engines::Engine; use env_info::EnvInfo; @@ -30,6 +30,9 @@ use types::state_diff::StateDiff; use transaction::SignedTransaction; use state_db::StateDB; +use util::*; +use util::trie::recorder::{Recorder, BasicRecorder as TrieRecorder}; + mod account; mod substate; @@ -751,6 +754,53 @@ impl State { } } +// LES state proof implementations. +impl State { + /// Prove an account's existence or nonexistence in the state trie. + /// Returns a merkle proof of the account's trie node with all nodes before `from_level` + /// omitted or an encountered trie error. + /// Requires a secure trie to be used for accurate results. + /// `account_key` == sha3(address) + pub fn prove_account(&self, account_key: H256, from_level: u32) -> Result, Box> { + let mut recorder = TrieRecorder::with_depth(from_level); + let trie = try!(TrieDB::new(self.db.as_hashdb(), &self.root)); + let _ = try!(trie.get_recorded(&account_key, &mut recorder)); + + Ok(recorder.drain().into_iter().map(|r| r.data).collect()) + } + + /// Prove an account's storage key's existence or nonexistence in the state. + /// Returns a merkle proof of the account's storage trie with all nodes before + /// `from_level` omitted. Requires a secure trie to be used for correctness. + /// `account_key` == sha3(address) + /// `storage_key` == sha3(key) + pub fn prove_storage(&self, account_key: H256, storage_key: H256, from_level: u32) -> Result, Box> { + // TODO: probably could look into cache somehow but it's keyed by + // address, not sha3(address). + let trie = try!(TrieDB::new(self.db.as_hashdb(), &self.root)); + let acc = match try!(trie.get(&account_key)) { + Some(rlp) => Account::from_rlp(&rlp), + None => return Ok(Vec::new()), + }; + + let account_db = self.factories.accountdb.readonly(self.db.as_hashdb(), account_key); + acc.prove_storage(account_db.as_hashdb(), storage_key, from_level) + } + + /// Get code by address hash. + /// Only works when backed by a secure trie. + pub fn code_by_address_hash(&self, account_key: H256) -> Result, Box> { + let trie = try!(TrieDB::new(self.db.as_hashdb(), &self.root)); + let mut acc = match try!(trie.get(&account_key)) { + Some(rlp) => Account::from_rlp(&rlp), + None => return Ok(None), + }; + + let account_db = self.factories.accountdb.readonly(self.db.as_hashdb(), account_key); + Ok(acc.cache_code(account_db.as_hashdb()).map(|c| (&*c).clone())) + } +} + impl fmt::Debug for State { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.cache.borrow()) diff --git a/ethcore/src/types/les_request.rs b/ethcore/src/types/les_request.rs index e947c654d..6e822dcdd 100644 --- a/ethcore/src/types/les_request.rs +++ b/ethcore/src/types/les_request.rs @@ -29,7 +29,7 @@ pub struct Headers { /// The maximum amount of headers which can be returned. pub max: usize, /// The amount of headers to skip between each response entry. - pub skip: usize, + pub skip: u64, /// Whether the headers should proceed in falling number from the initial block. pub reverse: bool, } diff --git a/util/src/trie/mod.rs b/util/src/trie/mod.rs index d4cc04962..5d71c2400 100644 --- a/util/src/trie/mod.rs +++ b/util/src/trie/mod.rs @@ -97,11 +97,11 @@ pub trait Trie { } /// Query the value of the given key in this trie while recording visited nodes - /// to the given recorder. If the query fails, the nodes passed to the recorder are unspecified. + /// to the given recorder. If the query encounters an error, the nodes passed to the recorder are unspecified. fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> Result> where 'a: 'b, R: Recorder; - /// Returns an iterator over elements of trie. + /// Returns a depth-first iterator over the elements of the trie. fn iter<'a>(&'a self) -> Result + 'a>>; } @@ -235,5 +235,5 @@ impl TrieFactory { } /// Returns true iff the trie DB is a fat DB (allows enumeration of keys). - pub fn is_fat(&self) -> bool { self.spec == TrieSpec::Fat } + pub fn is_fat(&self) -> bool { self.spec == TrieSpec::Fat } } diff --git a/util/src/trie/recorder.rs b/util/src/trie/recorder.rs index 2f1d926f0..3930dd543 100644 --- a/util/src/trie/recorder.rs +++ b/util/src/trie/recorder.rs @@ -35,7 +35,6 @@ pub struct Record { /// These are used to record which nodes are visited during a trie query. /// Inline nodes are not to be recorded, as they are contained within their parent. pub trait Recorder { - /// Record that the given node has been visited. /// /// The depth parameter is the depth of the visited node, with the root node having depth 0. @@ -58,6 +57,7 @@ impl Recorder for NoOp { /// A simple recorder. Does nothing fancy but fulfills the `Recorder` interface /// properly. +#[derive(Debug)] pub struct BasicRecorder { nodes: Vec, min_depth: u32, From cb54152c2356e95c10cc8cae8cd3b7b99f3e005a Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 15 Nov 2016 15:47:08 +0100 Subject: [PATCH 28/69] cut off headers after first missing --- ethcore/src/client/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 1054f0614..58c8f4df6 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1349,8 +1349,8 @@ impl light::Provider for Client { .take_while(|x| if req.reverse { x < &start_num } else { best_num - start_num < *x }) .map(|x| if req.reverse { start_num - x } else { start_num + x }) .map(|x| self.block_header(BlockID::Number(x))) + .take_while(|x| x.is_some()) .flat_map(|x| x) - .fuse() // collect no more beyond the first `None` .collect() } From 7bfb9e4003494fcedba1e65847d8e4abb8ef1c6c Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 15 Nov 2016 18:19:16 +0100 Subject: [PATCH 29/69] handle all LES requests --- ethcore/src/light/net/buffer_flow.rs | 10 + ethcore/src/light/net/mod.rs | 263 ++++++++++++++++++++++----- ethcore/src/light/provider.rs | 8 +- ethcore/src/types/les_request.rs | 2 +- 4 files changed, 234 insertions(+), 49 deletions(-) diff --git a/ethcore/src/light/net/buffer_flow.rs b/ethcore/src/light/net/buffer_flow.rs index 62f351515..8d6835db3 100644 --- a/ethcore/src/light/net/buffer_flow.rs +++ b/ethcore/src/light/net/buffer_flow.rs @@ -228,6 +228,16 @@ impl FlowParams { buf.estimate = ::std::cmp::min(self.limit, buf.estimate + (elapsed * self.recharge)); } + + /// Refund some buffer which was previously deducted. + /// Does not update the recharge timestamp. + pub fn refund(&self, buf: &mut Buffer, refund_amount: U256) { + buf.estimate = buf.estimate + refund_amount; + + if buf.estimate > self.limit { + buf.estimate = self.limit + } + } } #[cfg(test)] diff --git a/ethcore/src/light/net/mod.rs b/ethcore/src/light/net/mod.rs index 47146a2f9..1d7fc8f2d 100644 --- a/ethcore/src/light/net/mod.rs +++ b/ethcore/src/light/net/mod.rs @@ -23,7 +23,7 @@ use io::TimerToken; use network::{NetworkProtocolHandler, NetworkContext, NetworkError, PeerId}; use rlp::{RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; -use util::RwLock; +use util::{Mutex, RwLock, U256}; use std::collections::{HashMap, HashSet}; use std::sync::atomic::AtomicUsize; @@ -94,7 +94,7 @@ struct PendingPeer { // data about each peer. struct Peer { - local_buffer: Buffer, // their buffer relative to us + local_buffer: Mutex, // their buffer relative to us remote_buffer: Buffer, // our buffer relative to them current_asking: HashSet, // pending request ids. status: Status, @@ -103,6 +103,28 @@ struct Peer { sent_head: H256, // last head we've given them. } +impl Peer { + // check the maximum cost of a request, returning an error if there's + // not enough buffer left. + // returns the calculated maximum cost. + fn deduct_max(&self, flow_params: &FlowParams, kind: request::Kind, max: usize) -> Result { + let mut local_buffer = self.local_buffer.lock(); + flow_params.recharge(&mut local_buffer); + + let max_cost = flow_params.compute_cost(kind, max); + try!(local_buffer.deduct_cost(max_cost)); + Ok(max_cost) + } + + // refund buffer for a request. returns new buffer amount. + fn refund(&self, flow_params: &FlowParams, amount: U256) -> U256 { + let mut local_buffer = self.local_buffer.lock(); + flow_params.refund(&mut local_buffer, amount); + + local_buffer.current() + } +} + /// This is an implementation of the light ethereum network protocol, abstracted /// over a `Provider` of data and a p2p network. /// @@ -220,7 +242,7 @@ impl LightProtocol { } self.peers.write().insert(*peer, Peer { - local_buffer: self.flow_params.create_buffer(), + local_buffer: Mutex::new(self.flow_params.create_buffer()), remote_buffer: flow_params.create_buffer(), current_asking: HashSet::new(), status: status, @@ -276,15 +298,15 @@ impl LightProtocol { fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_HEADERS: usize = 512; - let mut present_buffer = match self.peers.read().get(peer) { - Some(peer) => peer.local_buffer.clone(), + let peers = self.peers.read(); + let peer = match peers.get(peer) { + Some(peer) => peer, None => { - debug!(target: "les", "Ignoring announcement from unknown peer"); + debug!(target: "les", "Ignoring request from unknown peer"); return Ok(()) } }; - self.flow_params.recharge(&mut present_buffer); let req_id: u64 = try!(data.val_at(0)); let block = { @@ -300,24 +322,13 @@ impl LightProtocol { reverse: try!(data.val_at(4)), }; - let max_cost = self.flow_params.compute_cost(request::Kind::Headers, req.max); - try!(present_buffer.deduct_cost(max_cost)); + let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::Headers, req.max)); let response = self.provider.block_headers(req); let actual_cost = self.flow_params.compute_cost(request::Kind::Headers, response.len()); + assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - let cur_buffer = match self.peers.write().get_mut(peer) { - Some(peer) => { - self.flow_params.recharge(&mut peer.local_buffer); - try!(peer.local_buffer.deduct_cost(actual_cost)); - peer.local_buffer.current() - } - None => { - debug!(target: "les", "peer disconnected during serving of request."); - return Ok(()) - } - }; - + let cur_buffer = peer.refund(&self.flow_params, max_cost - actual_cost); io.respond(packet::BLOCK_HEADERS, { let mut stream = RlpStream::new_list(response.len() + 2); stream.append(&req_id).append(&cur_buffer); @@ -339,39 +350,29 @@ impl LightProtocol { fn get_block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_BODIES: usize = 256; - let mut present_buffer = match self.peers.read().get(peer) { - Some(peer) => peer.local_buffer.clone(), + let peers = self.peers.read(); + let peer = match peers.get(peer) { + Some(peer) => peer, None => { - debug!(target: "les", "Ignoring announcement from unknown peer"); + debug!(target: "les", "Ignoring request from unknown peer"); return Ok(()) } }; - self.flow_params.recharge(&mut present_buffer); let req_id: u64 = try!(data.val_at(0)); let req = request::Bodies { block_hashes: try!(data.iter().skip(1).take(MAX_BODIES).map(|x| x.as_val()).collect()) }; - let max_cost = self.flow_params.compute_cost(request::Kind::Bodies, req.block_hashes.len()); - try!(present_buffer.deduct_cost(max_cost)); + let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::Bodies, req.block_hashes.len())); let response = self.provider.block_bodies(req); let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count(); let actual_cost = self.flow_params.compute_cost(request::Kind::Bodies, response_len); + assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - let cur_buffer = match self.peers.write().get_mut(peer) { - Some(peer) => { - self.flow_params.recharge(&mut peer.local_buffer); - try!(peer.local_buffer.deduct_cost(actual_cost)); - peer.local_buffer.current() - } - None => { - debug!(target: "les", "peer disconnected during serving of request."); - return Ok(()) - } - }; + let cur_buffer = peer.refund(&self.flow_params, max_cost - actual_cost); io.respond(packet::BLOCK_BODIES, { let mut stream = RlpStream::new_list(response.len() + 2); @@ -391,8 +392,43 @@ impl LightProtocol { } // Handle a request for receipts. - fn get_receipts(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn get_receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + const MAX_RECEIPTS: usize = 256; + + let peers = self.peers.read(); + let peer = match peers.get(peer) { + Some(peer) => peer, + None => { + debug!(target: "les", "Ignoring request from unknown peer"); + return Ok(()) + } + }; + + let req_id: u64 = try!(data.val_at(0)); + + let req = request::Receipts { + block_hashes: try!(data.iter().skip(1).take(MAX_RECEIPTS).map(|x| x.as_val()).collect()) + }; + + let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::Receipts, req.block_hashes.len())); + + let response = self.provider.receipts(req); + let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count(); + let actual_cost = self.flow_params.compute_cost(request::Kind::Receipts, response_len); + assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); + + let cur_buffer = peer.refund(&self.flow_params, max_cost - actual_cost); + + io.respond(packet::RECEIPTS, { + let mut stream = RlpStream::new_list(response.len() + 2); + stream.append(&req_id).append(&cur_buffer); + + for receipts in response { + stream.append_raw(&receipts, 1); + } + + stream.out() + }).map_err(Into::into) } // Receive a response for receipts. @@ -401,8 +437,54 @@ impl LightProtocol { } // Handle a request for proofs. - fn get_proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn get_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + const MAX_PROOFS: usize = 128; + + let peers = self.peers.read(); + let peer = match peers.get(peer) { + Some(peer) => peer, + None => { + debug!(target: "les", "Ignoring request from unknown peer"); + return Ok(()) + } + }; + + let req_id: u64 = try!(data.val_at(0)); + + let req = { + let requests: Result, Error> = data.iter().skip(1).take(MAX_PROOFS).map(|x| { + Ok(request::StateProof { + block: try!(x.val_at(0)), + key1: try!(x.val_at(1)), + key2: if try!(x.at(2)).is_empty() { None } else { Some(try!(x.val_at(2))) }, + from_level: try!(x.val_at(3)), + }) + }).collect(); + + request::StateProofs { + requests: try!(requests), + } + }; + + let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::StateProofs, req.requests.len())); + + let response = self.provider.proofs(req); + let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count(); + let actual_cost = self.flow_params.compute_cost(request::Kind::StateProofs, response_len); + assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); + + let cur_buffer = peer.refund(&self.flow_params, max_cost - actual_cost); + + io.respond(packet::PROOFS, { + let mut stream = RlpStream::new_list(response.len() + 2); + stream.append(&req_id).append(&cur_buffer); + + for proof in response { + stream.append_raw(&proof, 1); + } + + stream.out() + }).map_err(Into::into) } // Receive a response for proofs. @@ -411,8 +493,52 @@ impl LightProtocol { } // Handle a request for contract code. - fn get_contract_code(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn get_contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + const MAX_CODES: usize = 256; + + let peers = self.peers.read(); + let peer = match peers.get(peer) { + Some(peer) => peer, + None => { + debug!(target: "les", "Ignoring request from unknown peer"); + return Ok(()) + } + }; + + let req_id: u64 = try!(data.val_at(0)); + + let req = { + let requests: Result, Error> = data.iter().skip(1).take(MAX_CODES).map(|x| { + Ok(request::ContractCode { + block_hash: try!(x.val_at(0)), + account_key: try!(x.val_at(1)), + }) + }).collect(); + + request::ContractCodes { + code_requests: try!(requests), + } + }; + + let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::Codes, req.code_requests.len())); + + let response = self.provider.contract_code(req); + let response_len = response.iter().filter(|x| !x.is_empty()).count(); + let actual_cost = self.flow_params.compute_cost(request::Kind::Codes, response_len); + assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); + + let cur_buffer = peer.refund(&self.flow_params, max_cost - actual_cost); + + io.respond(packet::CONTRACT_CODES, { + let mut stream = RlpStream::new_list(response.len() + 2); + stream.append(&req_id).append(&cur_buffer); + + for code in response { + stream.append_raw(&code, 1); + } + + stream.out() + }).map_err(Into::into) } // Receive a response for contract code. @@ -421,8 +547,53 @@ impl LightProtocol { } // Handle a request for header proofs - fn get_header_proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn get_header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + const MAX_PROOFS: usize = 256; + + let peers = self.peers.read(); + let peer = match peers.get(peer) { + Some(peer) => peer, + None => { + debug!(target: "les", "Ignoring request from unknown peer"); + return Ok(()) + } + }; + + let req_id: u64 = try!(data.val_at(0)); + + let req = { + let requests: Result, Error> = data.iter().skip(1).take(MAX_PROOFS).map(|x| { + Ok(request::HeaderProof { + cht_number: try!(x.val_at(0)), + block_number: try!(x.val_at(1)), + from_level: try!(x.val_at(2)), + }) + }).collect(); + + request::HeaderProofs { + requests: try!(requests), + } + }; + + let max_cost = try!(peer.deduct_max(&self.flow_params, request::Kind::HeaderProofs, req.requests.len())); + + let response = self.provider.header_proofs(req); + let response_len = response.iter().filter(|x| &x[..] != ::rlp::EMPTY_LIST_RLP).count(); + let actual_cost = self.flow_params.compute_cost(request::Kind::HeaderProofs, response_len); + assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); + + let cur_buffer = peer.refund(&self.flow_params, max_cost - actual_cost); + + io.respond(packet::HEADER_PROOFS, { + let mut stream = RlpStream::new_list(response.len() + 2); + stream.append(&req_id).append(&cur_buffer); + + for proof in response { + stream.append_raw(&proof, 1); + } + + stream.out() + }).map_err(Into::into) } // Receive a response for header proofs diff --git a/ethcore/src/light/provider.rs b/ethcore/src/light/provider.rs index bf65acd18..5aa61828a 100644 --- a/ethcore/src/light/provider.rs +++ b/ethcore/src/light/provider.rs @@ -27,7 +27,8 @@ use light::request; /// Defines the operations that a provider for `LES` must fulfill. /// /// These are defined at [1], but may be subject to change. -/// Requests which can't be fulfilled should return an empty RLP list. +/// Requests which can't be fulfilled should return either an empty RLP list +/// or empty vector where appropriate. /// /// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) pub trait Provider: Send + Sync { @@ -35,6 +36,8 @@ pub trait Provider: Send + Sync { fn chain_info(&self) -> BlockChainInfo; /// Find the depth of a common ancestor between two blocks. + /// If either block is unknown or an ancestor can't be found + /// then return `None`. fn reorg_depth(&self, a: &H256, b: &H256) -> Option; /// Earliest block where state queries are available. @@ -59,10 +62,11 @@ pub trait Provider: Send + Sync { /// Provide a set of merkle proofs, as requested. Each request is a /// block hash and request parameters. /// - /// Returns a vector to RLP-encoded lists satisfying the requests. + /// Returns a vector of RLP-encoded lists satisfying the requests. fn proofs(&self, req: request::StateProofs) -> Vec; /// Provide contract code for the specified (block_hash, account_hash) pairs. + /// Each item in the resulting vector is either the raw bytecode or empty. fn contract_code(&self, req: request::ContractCodes) -> Vec; /// Provide header proofs from the Canonical Hash Tries. diff --git a/ethcore/src/types/les_request.rs b/ethcore/src/types/les_request.rs index 6e822dcdd..09d96db09 100644 --- a/ethcore/src/types/les_request.rs +++ b/ethcore/src/types/les_request.rs @@ -103,7 +103,7 @@ pub struct HeaderProof { #[derive(Debug, Clone, PartialEq, Eq, Binary)] pub struct HeaderProofs { /// All the proof requests. - pub requests: Vec, + pub requests: Vec, } /// Kinds of requests. From 3fabad5c0fb9eb0923b706bd38a78013a90106bd Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 18 Nov 2016 12:36:31 +0100 Subject: [PATCH 30/69] event struct types --- ethcore/src/light/net/event.rs | 40 ++++++++++++++++++++++++++++++++++ ethcore/src/light/net/mod.rs | 3 ++- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 ethcore/src/light/net/event.rs diff --git a/ethcore/src/light/net/event.rs b/ethcore/src/light/net/event.rs new file mode 100644 index 000000000..f7dee217a --- /dev/null +++ b/ethcore/src/light/net/event.rs @@ -0,0 +1,40 @@ +// Copyright 2015, 2016 Ethcore (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 events and event listeners. + +use network::PeerId; + +use super::{Status, Capabilities, Announcement}; + +use transaction::SignedTransaction; + +/// Peer connected +pub struct Connect(PeerId, Status, Capabilities); + +/// Peer disconnected +pub struct Disconnect(PeerId); + +/// Peer announces new capabilities. +pub struct Announcement(PeerId, Announcement); + +/// Transactions to be relayed. +pub struct RelayTransactions(Vec); + +/// An LES event handler. +pub trait Handler { + fn on_connect(&self, _event: Connect); +} \ No newline at end of file diff --git a/ethcore/src/light/net/mod.rs b/ethcore/src/light/net/mod.rs index 1d7fc8f2d..fc9ecbd18 100644 --- a/ethcore/src/light/net/mod.rs +++ b/ethcore/src/light/net/mod.rs @@ -39,7 +39,8 @@ mod buffer_flow; mod error; mod status; -pub use self::status::Announcement; +pub mod event; +pub use self::status::{Status, Capabilities, Announcement}; const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; From 63aa54cfc7b821625e7c421857ff8a33eaeec344 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 18 Nov 2016 15:30:06 +0100 Subject: [PATCH 31/69] trigger event handlers, update capabilities --- ethcore/src/light/net/event.rs | 40 ------------------- ethcore/src/light/net/mod.rs | 69 +++++++++++++++++++++++++-------- ethcore/src/light/net/status.rs | 10 +++++ 3 files changed, 63 insertions(+), 56 deletions(-) delete mode 100644 ethcore/src/light/net/event.rs diff --git a/ethcore/src/light/net/event.rs b/ethcore/src/light/net/event.rs deleted file mode 100644 index f7dee217a..000000000 --- a/ethcore/src/light/net/event.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2015, 2016 Ethcore (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 events and event listeners. - -use network::PeerId; - -use super::{Status, Capabilities, Announcement}; - -use transaction::SignedTransaction; - -/// Peer connected -pub struct Connect(PeerId, Status, Capabilities); - -/// Peer disconnected -pub struct Disconnect(PeerId); - -/// Peer announces new capabilities. -pub struct Announcement(PeerId, Announcement); - -/// Transactions to be relayed. -pub struct RelayTransactions(Vec); - -/// An LES event handler. -pub trait Handler { - fn on_connect(&self, _event: Connect); -} \ No newline at end of file diff --git a/ethcore/src/light/net/mod.rs b/ethcore/src/light/net/mod.rs index fc9ecbd18..8d369e7bb 100644 --- a/ethcore/src/light/net/mod.rs +++ b/ethcore/src/light/net/mod.rs @@ -30,16 +30,15 @@ use std::sync::atomic::AtomicUsize; use light::provider::Provider; use light::request::{self, Request}; +use transaction::SignedTransaction; use self::buffer_flow::{Buffer, FlowParams}; use self::error::{Error, Punishment}; -use self::status::{Status, Capabilities}; mod buffer_flow; mod error; mod status; -pub mod event; pub use self::status::{Status, Capabilities, Announcement}; const TIMEOUT: TimerToken = 0; @@ -126,6 +125,18 @@ impl Peer { } } +/// An LES event handler. +pub trait Handler: Send + Sync { + /// Called when a peer connects. + fn on_connect(&self, _id: PeerId, _status: &Status, _capabilities: &Capabilities) { } + /// Called when a peer disconnects + fn on_disconnect(&self, _id: PeerId) { } + /// Called when a peer makes an announcement. + fn on_announcement(&self, _id: PeerId, _announcement: &Announcement) { } + /// Called when a peer requests relay of some transactions. + fn on_transactions(&self, _id: PeerId, _relay: &[SignedTransaction]) { } +} + /// This is an implementation of the light ethereum network protocol, abstracted /// over a `Provider` of data and a p2p network. /// @@ -141,6 +152,7 @@ pub struct LightProtocol { pending_requests: RwLock>, capabilities: RwLock, flow_params: FlowParams, // assumed static and same for every peer. + handlers: Vec>, req_id: AtomicUsize, } @@ -150,6 +162,9 @@ impl LightProtocol { pub fn make_announcement(&self, mut announcement: Announcement, io: &NetworkContext) { let mut reorgs_map = HashMap::new(); + // update stored capabilities + self.capabilities.write().update_from(&announcement); + // calculate reorg info and send packets for (peer_id, peer_info) in self.peers.write().iter_mut() { let reorg_depth = reorgs_map.entry(peer_info.sent_head) @@ -174,6 +189,14 @@ impl LightProtocol { } } } + + /// Add an event handler. + /// Ownership will be transferred to the protocol structure, + /// and the handler will be kept alive as long as it is. + /// These are intended to be added at the beginning of the + pub fn add_handler(&mut self, handler: Box) { + self.handlers.push(handler); + } } impl LightProtocol { @@ -196,7 +219,11 @@ impl LightProtocol { fn on_disconnect(&self, peer: PeerId) { // TODO: reassign all requests assigned to this peer. self.pending_peers.write().remove(&peer); - self.peers.write().remove(&peer); + if self.peers.write().remove(&peer).is_some() { + for handler in &self.handlers { + handler.on_disconnect(peer) + } + } } // send status to a peer. @@ -246,12 +273,16 @@ impl LightProtocol { local_buffer: Mutex::new(self.flow_params.create_buffer()), remote_buffer: flow_params.create_buffer(), current_asking: HashSet::new(), - status: status, - capabilities: capabilities, + status: status.clone(), + capabilities: capabilities.clone(), remote_flow: flow_params, sent_head: pending.sent_head, }); + for handler in &self.handlers { + handler.on_connect(*peer, &status, &capabilities) + } + Ok(()) } @@ -282,15 +313,11 @@ impl LightProtocol { } // update capabilities. - { - let caps = &mut peer_info.capabilities; - caps.serve_headers = caps.serve_headers || announcement.serve_headers; - caps.serve_state_since = caps.serve_state_since.or(announcement.serve_state_since); - caps.serve_chain_since = caps.serve_chain_since.or(announcement.serve_chain_since); - caps.tx_relay = caps.tx_relay || announcement.tx_relay; - } + peer_info.capabilities.update_from(&announcement); - // TODO: notify listeners if new best block. + for handler in &self.handlers { + handler.on_announcement(*peer, &announcement); + } Ok(()) } @@ -603,8 +630,18 @@ impl LightProtocol { } // Receive a set of transactions to relay. - fn relay_transactions(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn relay_transactions(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> { + const MAX_TRANSACTIONS: usize = 256; + + let txs: Vec<_> = try!(data.iter().take(MAX_TRANSACTIONS).map(|x| x.as_val::()).collect()); + + debug!(target: "les", "Received {} transactions to relay from peer {}", txs.len(), peer); + + for handler in &self.handlers { + handler.on_transactions(*peer, &txs); + } + + Ok(()) } } @@ -639,7 +676,7 @@ impl NetworkProtocolHandler for LightProtocol { packet::GET_HEADER_PROOFS => self.get_header_proofs(peer, io, rlp), packet::HEADER_PROOFS => self.header_proofs(peer, io, rlp), - packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), + packet::SEND_TRANSACTIONS => self.relay_transactions(peer, rlp), other => { Err(Error::UnrecognizedPacket(other)) diff --git a/ethcore/src/light/net/status.rs b/ethcore/src/light/net/status.rs index 5aaea9f3a..e8f874621 100644 --- a/ethcore/src/light/net/status.rs +++ b/ethcore/src/light/net/status.rs @@ -201,6 +201,16 @@ impl Default for Capabilities { } } +impl Capabilities { + /// Update the capabilities from an announcement. + pub fn update_from(&mut self, announcement: &Announcement) { + self.serve_headers = self.serve_headers || announcement.serve_headers; + self.serve_state_since = self.serve_state_since.or(announcement.serve_state_since); + self.serve_chain_since = self.serve_chain_since.or(announcement.serve_chain_since); + self.tx_relay = self.tx_relay || announcement.tx_relay; + } +} + /// Attempt to parse a handshake message into its three parts: /// - chain status /// - serving capabilities From 4fd9670b332c0768935bdfb477ac3ae60eaee55e Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 18 Nov 2016 19:12:20 +0100 Subject: [PATCH 32/69] support request sending --- ethcore/src/light/net/buffer_flow.rs | 33 +++++ ethcore/src/light/net/error.rs | 4 + ethcore/src/light/net/mod.rs | 177 ++++++++++++++++++++++++++- ethcore/src/light/net/status.rs | 2 + ethcore/src/types/les_request.rs | 12 ++ 5 files changed, 223 insertions(+), 5 deletions(-) diff --git a/ethcore/src/light/net/buffer_flow.rs b/ethcore/src/light/net/buffer_flow.rs index 8d6835db3..7f653f826 100644 --- a/ethcore/src/light/net/buffer_flow.rs +++ b/ethcore/src/light/net/buffer_flow.rs @@ -206,6 +206,39 @@ impl FlowParams { cost.0 + (amount * cost.1) } + /// Compute the maximum number of costs of a specific kind which can be made + /// with the given buffer. + /// Saturates at `usize::max()`. This is not a problem in practice because + /// this amount of requests is already prohibitively large. + pub fn max_amount(&self, buffer: &Buffer, kind: request::Kind) -> usize { + use util::Uint; + use std::usize; + + let cost = match kind { + request::Kind::Headers => &self.costs.headers, + request::Kind::Bodies => &self.costs.bodies, + request::Kind::Receipts => &self.costs.receipts, + request::Kind::StateProofs => &self.costs.state_proofs, + request::Kind::Codes => &self.costs.contract_codes, + request::Kind::HeaderProofs => &self.costs.header_proofs, + }; + + let start = buffer.current(); + + if start <= cost.0 { + return 0; + } else if cost.1 == U256::zero() { + return usize::MAX; + } + + let max = (start - cost.0) / cost.1; + if max >= usize::MAX.into() { + usize::MAX + } else { + max.as_u64() as usize + } + } + /// Create initial buffer parameter. pub fn create_buffer(&self) -> Buffer { Buffer { diff --git a/ethcore/src/light/net/error.rs b/ethcore/src/light/net/error.rs index e15bd50d3..0855cdeb8 100644 --- a/ethcore/src/light/net/error.rs +++ b/ethcore/src/light/net/error.rs @@ -52,6 +52,8 @@ pub enum Error { UnexpectedHandshake, /// Peer on wrong network (wrong NetworkId or genesis hash) WrongNetwork, + /// Unknown peer. + UnknownPeer, } impl Error { @@ -64,6 +66,7 @@ impl Error { Error::UnrecognizedPacket(_) => Punishment::Disconnect, Error::UnexpectedHandshake => Punishment::Disconnect, Error::WrongNetwork => Punishment::Disable, + Error::UnknownPeer => Punishment::Disconnect, } } } @@ -89,6 +92,7 @@ impl fmt::Display for Error { Error::UnrecognizedPacket(code) => write!(f, "Unrecognized packet: 0x{:x}", code), Error::UnexpectedHandshake => write!(f, "Unexpected handshake"), Error::WrongNetwork => write!(f, "Wrong network"), + Error::UnknownPeer => write!(f, "unknown peer"), } } } \ No newline at end of file diff --git a/ethcore/src/light/net/mod.rs b/ethcore/src/light/net/mod.rs index 8d369e7bb..a2202d460 100644 --- a/ethcore/src/light/net/mod.rs +++ b/ethcore/src/light/net/mod.rs @@ -24,9 +24,10 @@ use network::{NetworkProtocolHandler, NetworkContext, NetworkError, PeerId}; use rlp::{RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; use util::{Mutex, RwLock, U256}; +use time::SteadyTime; use std::collections::{HashMap, HashSet}; -use std::sync::atomic::AtomicUsize; +use std::sync::atomic::{AtomicUsize, Ordering}; use light::provider::Provider; use light::request::{self, Request}; @@ -39,7 +40,7 @@ mod buffer_flow; mod error; mod status; -pub use self::status::{Status, Capabilities, Announcement}; +pub use self::status::{Status, Capabilities, Announcement, NetworkId}; const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; @@ -86,6 +87,10 @@ mod packet { pub const HEADER_PROOFS: u8 = 0x0e; } +/// A request id. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ReqId(usize); + // A pending peer: one we've sent our status to but // may not have received one for. struct PendingPeer { @@ -137,6 +142,24 @@ pub trait Handler: Send + Sync { fn on_transactions(&self, _id: PeerId, _relay: &[SignedTransaction]) { } } +// a request and the time it was made. +struct Requested { + request: Request, + timestamp: SteadyTime, +} + +/// Protocol parameters. +pub struct Params { + /// Genesis hash. + pub genesis_hash: H256, + /// Network id. + pub network_id: NetworkId, + /// Buffer flow parameters. + pub flow_params: FlowParams, + /// Initial capabilities. + pub capabilities: Capabilities, +} + /// This is an implementation of the light ethereum network protocol, abstracted /// over a `Provider` of data and a p2p network. /// @@ -146,10 +169,10 @@ pub trait Handler: Send + Sync { pub struct LightProtocol { provider: Box, genesis_hash: H256, - network_id: status::NetworkId, + network_id: NetworkId, pending_peers: RwLock>, peers: RwLock>, - pending_requests: RwLock>, + pending_requests: RwLock>, capabilities: RwLock, flow_params: FlowParams, // assumed static and same for every peer. handlers: Vec>, @@ -157,9 +180,71 @@ pub struct LightProtocol { } impl LightProtocol { + /// Create a new instance of the protocol manager. + pub fn new(provider: Box, params: Params) -> Self { + LightProtocol { + provider: provider, + genesis_hash: params.genesis_hash, + network_id: params.network_id, + pending_peers: RwLock::new(HashMap::new()), + peers: RwLock::new(HashMap::new()), + pending_requests: RwLock::new(HashMap::new()), + capabilities: RwLock::new(params.capabilities), + flow_params: params.flow_params, + handlers: Vec::new(), + req_id: AtomicUsize::new(0), + } + } + + /// Check the maximum amount of requests of a specific type + /// which a peer would be able to serve. + pub fn max_requests(&self, peer: PeerId, kind: request::Kind) -> Option { + self.peers.write().get_mut(&peer).map(|peer| { + peer.remote_flow.recharge(&mut peer.remote_buffer); + peer.remote_flow.max_amount(&peer.remote_buffer, kind) + }) + } + + /// Make a request to a peer. + /// + /// Fails on: nonexistent peer, network error, + /// insufficient buffer. Does not check capabilities before sending. + /// On success, returns a request id which can later be coordinated + /// with an event. + pub fn request_from(&self, io: &NetworkContext, peer_id: &PeerId, request: Request) -> Result { + let mut peers = self.peers.write(); + let peer = try!(peers.get_mut(peer_id).ok_or_else(|| Error::UnknownPeer)); + peer.remote_flow.recharge(&mut peer.remote_buffer); + + let max = peer.remote_flow.compute_cost(request.kind(), request.amount()); + try!(peer.remote_buffer.deduct_cost(max)); + + let req_id = self.req_id.fetch_add(1, Ordering::SeqCst); + let packet_data = encode_request(&request, req_id); + + let packet_id = match request.kind() { + request::Kind::Headers => packet::GET_BLOCK_HEADERS, + request::Kind::Bodies => packet::GET_BLOCK_BODIES, + request::Kind::Receipts => packet::GET_RECEIPTS, + request::Kind::StateProofs => packet::GET_PROOFS, + request::Kind::Codes => packet::GET_CONTRACT_CODES, + request::Kind::HeaderProofs => packet::GET_HEADER_PROOFS, + }; + + try!(io.send(*peer_id, packet_id, packet_data)); + + peer.current_asking.insert(req_id); + self.pending_requests.write().insert(req_id, Requested { + request: request, + timestamp: SteadyTime::now(), + }); + + Ok(ReqId(req_id)) + } + /// Make an announcement of new chain head and capabilities to all peers. /// The announcement is expected to be valid. - pub fn make_announcement(&self, mut announcement: Announcement, io: &NetworkContext) { + pub fn make_announcement(&self, io: &NetworkContext, mut announcement: Announcement) { let mut reorgs_map = HashMap::new(); // update stored capabilities @@ -715,4 +800,86 @@ impl NetworkProtocolHandler for LightProtocol { _ => warn!(target: "les", "received timeout on unknown token {}", timer), } } +} + +// Helper for encoding the request to RLP with the given ID. +fn encode_request(req: &Request, req_id: usize) -> Vec { + match *req { + Request::Headers(ref headers) => { + let mut stream = RlpStream::new_list(5); + stream + .append(&req_id) + .begin_list(2) + .append(&headers.block_num) + .append(&headers.block_hash) + .append(&headers.max) + .append(&headers.skip) + .append(&headers.reverse); + stream.out() + } + Request::Bodies(ref request) => { + let mut stream = RlpStream::new_list(request.block_hashes.len() + 1); + stream.append(&req_id); + + for hash in &request.block_hashes { + stream.append(hash); + } + + stream.out() + } + Request::Receipts(ref request) => { + let mut stream = RlpStream::new_list(request.block_hashes.len() + 1); + stream.append(&req_id); + + for hash in &request.block_hashes { + stream.append(hash); + } + + stream.out() + } + Request::StateProofs(ref request) => { + let mut stream = RlpStream::new_list(request.requests.len() + 1); + stream.append(&req_id); + + for proof_req in &request.requests { + stream.begin_list(4) + .append(&proof_req.block) + .append(&proof_req.key1); + + match proof_req.key2 { + Some(ref key2) => stream.append(key2), + None => stream.append_empty_data(), + }; + + stream.append(&proof_req.from_level); + } + + stream.out() + } + Request::Codes(ref request) => { + let mut stream = RlpStream::new_list(request.code_requests.len() + 1); + stream.append(&req_id); + + for code_req in &request.code_requests { + stream.begin_list(2) + .append(&code_req.block_hash) + .append(&code_req.account_key); + } + + stream.out() + } + Request::HeaderProofs(ref request) => { + let mut stream = RlpStream::new_list(request.requests.len() + 1); + stream.append(&req_id); + + for proof_req in &request.requests { + stream.begin_list(3) + .append(&proof_req.cht_number) + .append(&proof_req.block_number) + .append(&proof_req.from_level); + } + + stream.out() + } + } } \ No newline at end of file diff --git a/ethcore/src/light/net/status.rs b/ethcore/src/light/net/status.rs index e8f874621..2c0c5f79a 100644 --- a/ethcore/src/light/net/status.rs +++ b/ethcore/src/light/net/status.rs @@ -183,8 +183,10 @@ pub struct Capabilities { /// Whether this peer can serve headers pub serve_headers: bool, /// Earliest block number it can serve block/receipt requests for. + /// `None` means no requests will be servable. pub serve_chain_since: Option, /// Earliest block number it can serve state requests for. + /// `None` means no requests will be servable. pub serve_state_since: Option, /// Whether it can relay transactions to the eth network. pub tx_relay: bool, diff --git a/ethcore/src/types/les_request.rs b/ethcore/src/types/les_request.rs index 09d96db09..d0de080ee 100644 --- a/ethcore/src/types/les_request.rs +++ b/ethcore/src/types/les_request.rs @@ -152,4 +152,16 @@ impl Request { Request::HeaderProofs(_) => Kind::HeaderProofs, } } + + /// Get the amount of requests being made. + pub fn amount(&self) -> usize { + match *self { + Request::Headers(ref req) => req.max, + Request::Bodies(ref req) => req.block_hashes.len(), + Request::Receipts(ref req) => req.block_hashes.len(), + Request::StateProofs(ref req) => req.requests.len(), + Request::Codes(ref req) => req.code_requests.len(), + Request::HeaderProofs(ref req) => req.requests.len(), + } + } } \ No newline at end of file From 48df2e12facf6265d44fb1828e2ee03abf2a0af4 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 18 Nov 2016 19:26:05 +0100 Subject: [PATCH 33/69] exclusive access to each peer at a time --- ethcore/src/light/net/mod.rs | 59 +++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/ethcore/src/light/net/mod.rs b/ethcore/src/light/net/mod.rs index a2202d460..4b92e262e 100644 --- a/ethcore/src/light/net/mod.rs +++ b/ethcore/src/light/net/mod.rs @@ -99,7 +99,7 @@ struct PendingPeer { // data about each peer. struct Peer { - local_buffer: Mutex, // their buffer relative to us + local_buffer: Buffer, // their buffer relative to us remote_buffer: Buffer, // our buffer relative to them current_asking: HashSet, // pending request ids. status: Status, @@ -112,21 +112,25 @@ impl Peer { // check the maximum cost of a request, returning an error if there's // not enough buffer left. // returns the calculated maximum cost. - fn deduct_max(&self, flow_params: &FlowParams, kind: request::Kind, max: usize) -> Result { - let mut local_buffer = self.local_buffer.lock(); - flow_params.recharge(&mut local_buffer); + fn deduct_max(&mut self, flow_params: &FlowParams, kind: request::Kind, max: usize) -> Result { + flow_params.recharge(&mut self.local_buffer); let max_cost = flow_params.compute_cost(kind, max); - try!(local_buffer.deduct_cost(max_cost)); + try!(self.local_buffer.deduct_cost(max_cost)); Ok(max_cost) } // refund buffer for a request. returns new buffer amount. - fn refund(&self, flow_params: &FlowParams, amount: U256) -> U256 { - let mut local_buffer = self.local_buffer.lock(); - flow_params.refund(&mut local_buffer, amount); + fn refund(&mut self, flow_params: &FlowParams, amount: U256) -> U256 { + flow_params.refund(&mut self.local_buffer, amount); - local_buffer.current() + self.local_buffer.current() + } + + // recharge remote buffer with remote flow params. + fn recharge_remote(&mut self) { + let flow = &mut self.remote_flow; + flow.recharge(&mut self.remote_buffer); } } @@ -171,7 +175,7 @@ pub struct LightProtocol { genesis_hash: H256, network_id: NetworkId, pending_peers: RwLock>, - peers: RwLock>, + peers: RwLock>>, pending_requests: RwLock>, capabilities: RwLock, flow_params: FlowParams, // assumed static and same for every peer. @@ -199,8 +203,9 @@ impl LightProtocol { /// Check the maximum amount of requests of a specific type /// which a peer would be able to serve. pub fn max_requests(&self, peer: PeerId, kind: request::Kind) -> Option { - self.peers.write().get_mut(&peer).map(|peer| { - peer.remote_flow.recharge(&mut peer.remote_buffer); + self.peers.read().get(&peer).map(|peer| { + let mut peer = peer.lock(); + peer.recharge_remote(); peer.remote_flow.max_amount(&peer.remote_buffer, kind) }) } @@ -212,9 +217,11 @@ impl LightProtocol { /// On success, returns a request id which can later be coordinated /// with an event. pub fn request_from(&self, io: &NetworkContext, peer_id: &PeerId, request: Request) -> Result { - let mut peers = self.peers.write(); - let peer = try!(peers.get_mut(peer_id).ok_or_else(|| Error::UnknownPeer)); - peer.remote_flow.recharge(&mut peer.remote_buffer); + let peers = self.peers.read(); + let peer = try!(peers.get(peer_id).ok_or_else(|| Error::UnknownPeer)); + let mut peer = peer.lock(); + + peer.recharge_remote(); let max = peer.remote_flow.compute_cost(request.kind(), request.amount()); try!(peer.remote_buffer.deduct_cost(max)); @@ -251,7 +258,8 @@ impl LightProtocol { self.capabilities.write().update_from(&announcement); // calculate reorg info and send packets - for (peer_id, peer_info) in self.peers.write().iter_mut() { + for (peer_id, peer_info) in self.peers.read().iter() { + let mut peer_info = peer_info.lock(); let reorg_depth = reorgs_map.entry(peer_info.sent_head) .or_insert_with(|| { match self.provider.reorg_depth(&announcement.head_hash, &peer_info.sent_head) { @@ -354,15 +362,15 @@ impl LightProtocol { return Err(Error::WrongNetwork); } - self.peers.write().insert(*peer, Peer { - local_buffer: Mutex::new(self.flow_params.create_buffer()), + self.peers.write().insert(*peer, Mutex::new(Peer { + local_buffer: self.flow_params.create_buffer(), remote_buffer: flow_params.create_buffer(), current_asking: HashSet::new(), status: status.clone(), capabilities: capabilities.clone(), remote_flow: flow_params, sent_head: pending.sent_head, - }); + })); for handler in &self.handlers { handler.on_connect(*peer, &status, &capabilities) @@ -379,13 +387,15 @@ impl LightProtocol { } let announcement = try!(status::parse_announcement(data)); - let mut peers = self.peers.write(); + let peers = self.peers.read(); - let peer_info = match peers.get_mut(peer) { + let peer_info = match peers.get(peer) { Some(info) => info, None => return Ok(()), }; + let mut peer_info = peer_info.lock(); + // update status. { // TODO: punish peer if they've moved backwards. @@ -420,6 +430,8 @@ impl LightProtocol { } }; + let mut peer = peer.lock(); + let req_id: u64 = try!(data.val_at(0)); let block = { @@ -471,6 +483,7 @@ impl LightProtocol { return Ok(()) } }; + let mut peer = peer.lock(); let req_id: u64 = try!(data.val_at(0)); @@ -516,6 +529,7 @@ impl LightProtocol { return Ok(()) } }; + let mut peer = peer.lock(); let req_id: u64 = try!(data.val_at(0)); @@ -561,6 +575,7 @@ impl LightProtocol { return Ok(()) } }; + let mut peer = peer.lock(); let req_id: u64 = try!(data.val_at(0)); @@ -617,6 +632,7 @@ impl LightProtocol { return Ok(()) } }; + let mut peer = peer.lock(); let req_id: u64 = try!(data.val_at(0)); @@ -671,6 +687,7 @@ impl LightProtocol { return Ok(()) } }; + let mut peer = peer.lock(); let req_id: u64 = try!(data.val_at(0)); From 58ca93c123a73b264907a3f6565373e788201392 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 18 Nov 2016 19:27:32 +0100 Subject: [PATCH 34/69] document lock order --- ethcore/src/light/net/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ethcore/src/light/net/mod.rs b/ethcore/src/light/net/mod.rs index 4b92e262e..2d38d4fd5 100644 --- a/ethcore/src/light/net/mod.rs +++ b/ethcore/src/light/net/mod.rs @@ -170,6 +170,10 @@ pub struct Params { /// This is simply designed for request-response purposes. Higher level uses /// of the protocol, such as synchronization, will function as wrappers around /// this system. +// +// LOCK ORDER: +// Locks must be acquired in the order declared, and when holding a read lock +// on the peers, only one peer may be held at a time. pub struct LightProtocol { provider: Box, genesis_hash: H256, From 3b6d886860cbe89736ed4448d992b3b20572d990 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 27 Nov 2016 14:11:37 +0100 Subject: [PATCH 35/69] Fix up the transaction JSON serialisation for RPC. --- ethcore/src/types/transaction.rs | 3 +++ rpc/src/v1/types/transaction.rs | 22 ++++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 8289c5864..d7e06790b 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -304,6 +304,9 @@ impl SignedTransaction { /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid. pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => (v - 1) % 2, _ => 4 } } + /// The `v` value that appears in the RLP. + pub fn original_v(&self) -> u8 { self.v } + /// The network ID, or `None` if this is a global transaction. pub fn network_id(&self) -> Option { match self.v { diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index f566f9b20..561843246 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -57,12 +57,18 @@ pub struct Transaction { /// Public key of the signer. #[serde(rename="publicKey")] pub public_key: Option, - /// The V field of the signature. + /// The network id of the transaction, if any. + #[serde(rename="networkId")] + pub network_id: Option, + /// The standardised V field of the signature (0 or 1). + #[serde(rename="standardV")] + pub standard_v: u8, + /// The standardised V field of the signature. pub v: u8, /// The R field of the signature. - pub r: H256, + pub r: U256, /// The S field of the signature. - pub s: H256, + pub s: U256, } /// Local Transaction Status @@ -176,7 +182,9 @@ impl From for Transaction { }, raw: ::rlp::encode(&t.signed).to_vec().into(), public_key: t.public_key().ok().map(Into::into), - v: signature.v(), + network_id: t.network_id(), + standard_v: t.standard_v(), + v: t.original_v(), r: signature.r().into(), s: signature.s().into(), } @@ -207,7 +215,9 @@ impl From for Transaction { }, raw: ::rlp::encode(&t).to_vec().into(), public_key: t.public_key().ok().map(Into::into), - v: signature.v(), + network_id: t.network_id(), + standard_v: t.standard_v(), + v: t.original_v(), r: signature.r().into(), s: signature.s().into(), } @@ -238,7 +248,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x","s":"0x"}"#); } #[test] From 0cf8db58b8c06c12b6ea6f9ca1dd6a68d4a543b6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 27 Nov 2016 14:49:30 +0100 Subject: [PATCH 36/69] Fix tests. --- ethcore/res/ethereum/tests | 2 +- rpc/src/v1/tests/mocked/eth.rs | 9 ++++++--- rpc/src/v1/tests/mocked/signing.rs | 7 +++++-- rpc/src/v1/types/block.rs | 2 +- rpc/src/v1/types/transaction.rs | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests index d509c7593..e8f4624b7 160000 --- a/ethcore/res/ethereum/tests +++ b/ethcore/res/ethereum/tests @@ -1 +1 @@ -Subproject commit d509c75936ec6cbba683ee1916aa0bca436bc376 +Subproject commit e8f4624b7f1a15c63674eecf577c7ab76c3b16be diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 861bb5234..d1d419719 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -495,7 +495,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":0,"value":"0xa"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":0,"to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":27,"value":"0xa"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", @@ -810,13 +810,16 @@ fn rpc_eth_sign_transaction() { &format!("\"from\":\"0x{:?}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + - r#""input":"0x","nonce":"0x1","# + + r#""input":"0x","# + + &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + + &format!("\"standardV\":{},", t.standard_v()) + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":{},", signature.v()) + + &format!("\"v\":{},", t.original_v()) + r#""value":"0x9184e72a""# + r#"}},"id":1}"#; diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 7431bc45e..3166f0436 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -278,13 +278,16 @@ fn should_add_sign_transaction_to_the_queue() { &format!("\"from\":\"0x{:?}\",", &address) + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + &format!("\"hash\":\"0x{:?}\",", t.hash()) + - r#""input":"0x","nonce":"0x1","# + + r#""input":"0x","# + + &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + + &format!("\"standardV\":{},", t.standard_v()) + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":{},", signature.v()) + + &format!("\"v\":{},", t.original_v()) + r#""value":"0x9184e72a""# + r#"}},"id":1}"#; diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index f52785e90..270b077be 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -139,7 +139,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000"}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":0,"v":0,"r":"0x0","s":"0x0"}]"#); let t = BlockTransactions::Hashes(vec![H256::default().into()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 561843246..bd56e6a30 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -248,7 +248,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x","s":"0x"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":0,"v":0,"r":"0x0","s":"0x0"}"#); } #[test] From 436016ef026c9444f8e2b1fcb1fd0df1255c462d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 29 Nov 2016 13:46:06 +0100 Subject: [PATCH 37/69] Introduce to_hex() utility in bigint. Fix tests. --- rpc/src/v1/tests/mocked/eth.rs | 10 ++++---- rpc/src/v1/tests/mocked/signing.rs | 8 +++---- rpc/src/v1/types/block.rs | 2 +- rpc/src/v1/types/transaction.rs | 14 +++++------ util/bigint/src/uint.rs | 37 +++++++++++++++++++++++++----- 5 files changed, 48 insertions(+), 23 deletions(-) diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index d1d419719..c25ed881b 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -495,7 +495,7 @@ fn rpc_eth_pending_transaction_by_hash() { tester.miner.pending_transactions.lock().insert(H256::zero(), tx); } - let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":0,"to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":27,"value":"0xa"},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"creates":null,"from":"0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e","gas":"0x5208","gasPrice":"0x1","hash":"0x41df922fd0d4766fcc02e161f8295ec28522f329ae487f14d811e4b64c8d6e31","input":"0x","networkId":null,"nonce":"0x0","publicKey":"0x7ae46da747962c2ee46825839c1ef9298e3bd2e70ca2938495c3693a485ec3eaa8f196327881090ff64cf4fbb0a48485d4f83098e189ed3b7a87d5941b59f789","r":"0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353","raw":"0xf85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","s":"0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804","standardV":"0x0","to":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87","transactionIndex":null,"v":"0x1b","value":"0xa"},"id":1}"#; let request = r#"{ "jsonrpc": "2.0", "method": "eth_getTransactionByHash", @@ -814,12 +814,12 @@ fn rpc_eth_sign_transaction() { &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + - &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + + &format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + - &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + - &format!("\"standardV\":{},", t.standard_v()) + + &format!("\"s\":\"0x{}\",", U256::from(signature.s()).to_hex()) + + &format!("\"standardV\":\"0x{}\",", U256::from(t.standard_v()).to_hex()) + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":{},", t.original_v()) + + &format!("\"v\":\"0x{}\",", U256::from(t.original_v()).to_hex()) + r#""value":"0x9184e72a""# + r#"}},"id":1}"#; diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 3166f0436..4b1314853 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -282,12 +282,12 @@ fn should_add_sign_transaction_to_the_queue() { &format!("\"networkId\":{},", t.network_id().map_or("null".to_owned(), |n| format!("{}", n))) + r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + - &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + + &format!("\"r\":\"0x{}\",", U256::from(signature.r()).to_hex()) + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + - &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + - &format!("\"standardV\":{},", t.standard_v()) + + &format!("\"s\":\"0x{}\",", U256::from(signature.s()).to_hex()) + + &format!("\"standardV\":\"0x{}\",", U256::from(t.standard_v()).to_hex()) + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + - &format!("\"v\":{},", t.original_v()) + + &format!("\"v\":\"0x{}\",", U256::from(t.original_v()).to_hex()) + r#""value":"0x9184e72a""# + r#"}},"id":1}"#; diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 270b077be..5aab6ced8 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -139,7 +139,7 @@ mod tests { fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":0,"v":0,"r":"0x0","s":"0x0"}]"#); + assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0"}]"#); let t = BlockTransactions::Hashes(vec![H256::default().into()]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index bd56e6a30..7f26adf41 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -62,9 +62,9 @@ pub struct Transaction { pub network_id: Option, /// The standardised V field of the signature (0 or 1). #[serde(rename="standardV")] - pub standard_v: u8, + pub standard_v: U256, /// The standardised V field of the signature. - pub v: u8, + pub v: U256, /// The R field of the signature. pub r: U256, /// The S field of the signature. @@ -183,8 +183,8 @@ impl From for Transaction { raw: ::rlp::encode(&t.signed).to_vec().into(), public_key: t.public_key().ok().map(Into::into), network_id: t.network_id(), - standard_v: t.standard_v(), - v: t.original_v(), + standard_v: t.standard_v().into(), + v: t.original_v().into(), r: signature.r().into(), s: signature.s().into(), } @@ -216,8 +216,8 @@ impl From for Transaction { raw: ::rlp::encode(&t).to_vec().into(), public_key: t.public_key().ok().map(Into::into), network_id: t.network_id(), - standard_v: t.standard_v(), - v: t.original_v(), + standard_v: t.standard_v().into(), + v: t.original_v().into(), r: signature.r().into(), s: signature.s().into(), } @@ -248,7 +248,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":0,"v":0,"r":"0x0","s":"0x0"}"#); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"networkId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0"}"#); } #[test] diff --git a/util/bigint/src/uint.rs b/util/bigint/src/uint.rs index f4dd91140..49aa06d91 100644 --- a/util/bigint/src/uint.rs +++ b/util/bigint/src/uint.rs @@ -37,12 +37,12 @@ //! implementations for even more speed, hidden behind the `x64_arithmetic` //! feature flag. -use std::{mem, fmt}; +use std::{mem, fmt, cmp}; use std::str::{FromStr}; use std::hash::Hash; use std::ops::{Shr, Shl, BitAnd, BitOr, BitXor, Not, Div, Rem, Mul, Add, Sub}; use std::cmp::Ordering; -use rustc_serialize::hex::{FromHex, FromHexError}; +use rustc_serialize::hex::{ToHex, FromHex, FromHexError}; /// Conversion from decimal string error #[derive(Debug, PartialEq)] @@ -520,8 +520,10 @@ pub trait Uint: Sized + Default + FromStr + From + fmt::Debug + fmt::Displa fn bit(&self, index: usize) -> bool; /// Return single byte fn byte(&self, index: usize) -> u8; - /// Convert U256 to the sequence of bytes with a big endian + /// Convert to the sequence of bytes with a big endian fn to_big_endian(&self, bytes: &mut[u8]); + /// Convert to a non-zero-prefixed hex representation prefixed by `0x`. + fn to_hex(&self) -> String; /// Create `Uint(10**n)` fn exp10(n: usize) -> Self; /// Return eponentation `self**other`. Panic on overflow. @@ -684,6 +686,17 @@ macro_rules! construct_uint { } } + #[inline] + fn to_hex(&self) -> String { + if self.is_zero() { return "0".to_owned(); } // special case. + let mut bytes = [0u8; 8 * $n_words]; + self.to_big_endian(&mut bytes); + let bp7 = self.bits() + 7; + let len = cmp::max(bp7 / 8, 1); + let bytes_hex = bytes[bytes.len() - len..].to_hex(); + (&bytes_hex[1 - bp7 % 8 / 4..]).to_owned() + } + #[inline] fn exp10(n: usize) -> Self { match n { @@ -1637,7 +1650,7 @@ mod tests { } #[test] - fn uint256_pow () { + fn uint256_pow() { assert_eq!(U256::from(10).pow(U256::from(0)), U256::from(1)); assert_eq!(U256::from(10).pow(U256::from(1)), U256::from(10)); assert_eq!(U256::from(10).pow(U256::from(2)), U256::from(100)); @@ -1647,12 +1660,24 @@ mod tests { #[test] #[should_panic] - fn uint256_pow_overflow_panic () { + fn uint256_pow_overflow_panic() { U256::from(2).pow(U256::from(0x100)); } #[test] - fn uint256_overflowing_pow () { + fn should_format_hex_correctly() { + assert_eq!(&U256::from(0).to_hex(), &"0"); + assert_eq!(&U256::from(0x1).to_hex(), &"1"); + assert_eq!(&U256::from(0xf).to_hex(), &"f"); + assert_eq!(&U256::from(0x10).to_hex(), &"10"); + assert_eq!(&U256::from(0xff).to_hex(), &"ff"); + assert_eq!(&U256::from(0x100).to_hex(), &"100"); + assert_eq!(&U256::from(0xfff).to_hex(), &"fff"); + assert_eq!(&U256::from(0x1000).to_hex(), &"1000"); + } + + #[test] + fn uint256_overflowing_pow() { // assert_eq!( // U256::from(2).overflowing_pow(U256::from(0xff)), // (U256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap(), false) From 0e0bdc8ae93aaa3889693cf10ec438fae4e67bf5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 29 Nov 2016 13:48:28 +0100 Subject: [PATCH 38/69] Deduplicate code. --- rpc/src/v1/types/uint.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs index ce0fa49a2..e513d23db 100644 --- a/rpc/src/v1/types/uint.rs +++ b/rpc/src/v1/types/uint.rs @@ -14,9 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::cmp; use std::str::FromStr; -use rustc_serialize::hex::ToHex; use serde; use util::{U256 as EthU256, Uint}; @@ -50,18 +48,7 @@ macro_rules! impl_uint { impl serde::Serialize for $name { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer { - let mut hex = "0x".to_owned(); - let mut bytes = [0u8; 8 * $size]; - self.0.to_big_endian(&mut bytes); - let len = cmp::max((self.0.bits() + 7) / 8, 1); - let bytes_hex = bytes[bytes.len() - len..].to_hex(); - - if bytes_hex.starts_with('0') { - hex.push_str(&bytes_hex[1..]); - } else { - hex.push_str(&bytes_hex); - } - serializer.serialize_str(&hex) + serializer.serialize_str(&format!("0x{}", self.0.to_hex())) } } From d58905ae13f781139380f3611bd6d137eeb64208 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 29 Nov 2016 17:59:17 +0100 Subject: [PATCH 39/69] fix comment --- util/bigint/src/uint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/bigint/src/uint.rs b/util/bigint/src/uint.rs index 49aa06d91..c0b6e0987 100644 --- a/util/bigint/src/uint.rs +++ b/util/bigint/src/uint.rs @@ -522,7 +522,7 @@ pub trait Uint: Sized + Default + FromStr + From + fmt::Debug + fmt::Displa fn byte(&self, index: usize) -> u8; /// Convert to the sequence of bytes with a big endian fn to_big_endian(&self, bytes: &mut[u8]); - /// Convert to a non-zero-prefixed hex representation prefixed by `0x`. + /// Convert to a non-zero-prefixed hex representation (not prefixed by `0x`). fn to_hex(&self) -> String; /// Create `Uint(10**n)` fn exp10(n: usize) -> Self; From cd5b6fdf592654e83b229104c4263e770d9202a0 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 2 Dec 2016 18:21:54 +0100 Subject: [PATCH 40/69] queue: CLI for auto-scaling and num verifiers --- ethcore/src/verification/queue/mod.rs | 58 ++++++++++++++++++++++----- parity/blockchain.rs | 18 ++++++++- parity/cli/mod.rs | 2 + parity/cli/usage.txt | 7 +++- parity/configuration.rs | 15 +++++++ parity/run.rs | 6 ++- 6 files changed, 92 insertions(+), 14 deletions(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 686a1d093..de4428f02 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -53,6 +53,8 @@ pub struct Config { /// Maximum heap memory to use. /// When the limit is reached, is_full returns true. pub max_mem_use: usize, + /// Settings for the number of verifiers and adaptation strategy. + pub verifier_settings: VerifierSettings, } impl Default for Config { @@ -60,6 +62,26 @@ impl Default for Config { Config { max_queue_size: 30000, max_mem_use: 50 * 1024 * 1024, + verifier_settings: VerifierSettings::default(), + } + } +} + +/// Verifier settings. +#[derive(Debug, PartialEq, Clone)] +pub struct VerifierSettings { + /// Whether to scale amount of verifiers according to load. + // Todo: replace w/ strategy enum? + pub scale_verifiers: bool, + /// Beginning amount of verifiers. + pub num_verifiers: usize, +} + +impl Default for VerifierSettings { + fn default() -> Self { + VerifierSettings { + scale_verifiers: false, + num_verifiers: MAX_VERIFIERS, } } } @@ -139,6 +161,7 @@ pub struct VerificationQueue { ticks_since_adjustment: AtomicUsize, max_queue_size: usize, max_mem_use: usize, + scale_verifiers: bool, } struct QueueSignal { @@ -221,12 +244,15 @@ impl VerificationQueue { }); let empty = Arc::new(SCondvar::new()); let panic_handler = PanicHandler::new_in_arc(); + let scale_verifiers = config.verifier_settings.scale_verifiers; - let max_verifiers = min(::num_cpus::get(), MAX_VERIFIERS); - let default_amount = max(::num_cpus::get(), 3) - 2; + let num_cpus = ::num_cpus::get(); + let max_verifiers = min(num_cpus, MAX_VERIFIERS); + let default_amount = max(1, min(max_verifiers, config.verifier_settings.num_verifiers)); let mut verifiers = Vec::with_capacity(max_verifiers); debug!(target: "verification", "Allocating {} verifiers, {} initially active", max_verifiers, default_amount); + debug!(target: "verification", "Verifier auto-scaling {}", if scale_verifiers { "enabled" } else { "disabled" }); for i in 0..max_verifiers { debug!(target: "verification", "Adding verification thread #{}", i); @@ -273,6 +299,7 @@ impl VerificationQueue { ticks_since_adjustment: AtomicUsize::new(0), max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT), max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT), + scale_verifiers: scale_verifiers, } } @@ -598,6 +625,8 @@ impl VerificationQueue { self.processing.write().shrink_to_fit(); + if !self.scale_verifiers { return } + if self.ticks_since_adjustment.fetch_add(1, AtomicOrdering::SeqCst) + 1 >= READJUSTMENT_PERIOD { self.ticks_since_adjustment.store(0, AtomicOrdering::SeqCst); } else { @@ -693,10 +722,15 @@ mod tests { use error::*; use views::*; - fn get_test_queue() -> BlockQueue { + // create a test block queue. + // auto_scaling enables verifier adjustment. + fn get_test_queue(auto_scale: bool) -> BlockQueue { let spec = get_test_spec(); let engine = spec.engine; - BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true) + + let mut config = Config::default(); + config.verifier_settings.scale_verifiers = auto_scale; + BlockQueue::new(config, engine, IoChannel::disconnected(), true) } #[test] @@ -709,7 +743,7 @@ mod tests { #[test] fn can_import_blocks() { - let queue = get_test_queue(); + let queue = get_test_queue(false); if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) { panic!("error importing block that is valid by definition({:?})", e); } @@ -717,7 +751,7 @@ mod tests { #[test] fn returns_error_for_duplicates() { - let queue = get_test_queue(); + let queue = get_test_queue(false); if let Err(e) = queue.import(Unverified::new(get_good_dummy_block())) { panic!("error importing block that is valid by definition({:?})", e); } @@ -736,7 +770,7 @@ mod tests { #[test] fn returns_ok_for_drained_duplicates() { - let queue = get_test_queue(); + let queue = get_test_queue(false); let block = get_good_dummy_block(); let hash = BlockView::new(&block).header().hash().clone(); if let Err(e) = queue.import(Unverified::new(block)) { @@ -753,7 +787,7 @@ mod tests { #[test] fn returns_empty_once_finished() { - let queue = get_test_queue(); + let queue = get_test_queue(false); queue.import(Unverified::new(get_good_dummy_block())) .expect("error importing block that is valid by definition"); queue.flush(); @@ -781,7 +815,7 @@ mod tests { fn scaling_limits() { use super::MAX_VERIFIERS; - let queue = get_test_queue(); + let queue = get_test_queue(true); queue.scale_verifiers(MAX_VERIFIERS + 1); assert!(queue.verifiers.lock().1 < MAX_VERIFIERS + 1); @@ -793,7 +827,7 @@ mod tests { #[test] fn readjust_verifiers() { - let queue = get_test_queue(); + let queue = get_test_queue(true); // put all the verifiers to sleep to ensure // the test isn't timing sensitive. @@ -806,13 +840,15 @@ mod tests { verifiers.1 }; + queue.scale_verifiers(num_verifiers - 1); + for block in get_good_dummy_block_seq(5000) { queue.import(Unverified::new(block)).expect("Block good by definition; qed"); } // almost all unverified == bump verifier count. queue.collect_garbage(); - assert_eq!(queue.verifiers.lock().1, num_verifiers + 1); + assert_eq!(queue.verifiers.lock().1, num_verifiers); // wake them up again and verify everything. { diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 02d3e39fb..0750d369d 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -28,6 +28,7 @@ use ethcore::service::ClientService; use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockImportError, BlockChainClient, BlockID}; use ethcore::error::ImportError; use ethcore::miner::Miner; +use ethcore::verification::queue::VerifierSettings; use cache::CacheConfig; use informant::{Informant, MillisecondDuration}; use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_bool}; @@ -84,6 +85,7 @@ pub struct ImportBlockchain { pub vm_type: VMType, pub check_seal: bool, pub with_color: bool, + pub verifier_settings: VerifierSettings, } #[derive(Debug, PartialEq)] @@ -175,7 +177,21 @@ fn execute_import(cmd: ImportBlockchain) -> Result { try!(execute_upgrades(&db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.fork_path().as_path()))); // prepare client config - let client_config = to_client_config(&cmd.cache_config, Mode::Active, tracing, fat_db, cmd.compaction, cmd.wal, cmd.vm_type, "".into(), algorithm, cmd.pruning_history, cmd.check_seal); + let mut client_config = to_client_config( + &cmd.cache_config, + Mode::Active, + tracing, + fat_db, + cmd.compaction, + cmd.wal, + cmd.vm_type, + "".into(), + algorithm, + cmd.pruning_history, + cmd.check_seal + ); + + client_config.queue.verifier_settings = cmd.verifier_settings; // build client let service = try!(ClientService::start( diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index d33c58d9d..30b273b38 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -242,6 +242,8 @@ usage! { or |c: &Config| otry!(c.footprint).db_compaction.clone(), flag_fat_db: String = "auto", or |c: &Config| otry!(c.footprint).fat_db.clone(), + flag_scale_verifiers: bool = false, or |_| None, + flag_num_verifiers: Option = None, or |_| None, // -- Import/Export Options flag_from: String = "1", or |_| None, diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index b67af6110..02e7e00ec 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -250,7 +250,7 @@ Footprint Options: the state cache (default: {flag_cache_size_state}). --cache-size MB Set total amount of discretionary memory to use for the entire system, overrides other cache and queue - options.a (default: {flag_cache_size:?}) + options. (default: {flag_cache_size:?}) --fast-and-loose Disables DB WAL, which gives a significant speed up but means an unclean exit is unrecoverable. (default: {flag_fast_and_loose}) --db-compaction TYPE Database compaction type. TYPE may be one of: @@ -261,6 +261,11 @@ Footprint Options: of all accounts and storage keys. Doubles the size of the state database. BOOL may be one of on, off or auto. (default: {flag_fat_db}) + --scale-verifiers Automatically scale amount of verifier threads based on + workload. Not guaranteed to be faster. + (default: {flag_scale_verifiers}) + --num-verifiers INT Amount of verifier threads to use or to begin with, if verifier + auto-scaling is enabled. (default: {flag_num_verifiers:?}) Import/Export Options: --from BLOCK Export from block BLOCK, which may be an index or diff --git a/parity/configuration.rs b/parity/configuration.rs index c4a54f747..7b8100bbb 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -25,6 +25,7 @@ use util::log::Colour; use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP}; use ethcore::client::VMType; use ethcore::miner::{MinerOptions, Banning}; +use ethcore::verification::queue::VerifierSettings; use rpc::{IpcConfiguration, HttpConfiguration}; use ethcore_rpc::NetworkSettings; @@ -158,6 +159,7 @@ impl Configuration { vm_type: vm_type, check_seal: !self.args.flag_no_seal_check, with_color: logger_config.color, + verifier_settings: self.verifier_settings(), }; Cmd::Blockchain(BlockchainCmd::Import(import_cmd)) } else if self.args.cmd_export { @@ -241,6 +243,8 @@ impl Configuration { None }; + let verifier_settings = self.verifier_settings(); + let run_cmd = RunCmd { cache_config: cache_config, dirs: dirs, @@ -275,6 +279,7 @@ impl Configuration { no_periodic_snapshot: self.args.flag_no_periodic_snapshot, check_seal: !self.args.flag_no_seal_check, download_old_blocks: !self.args.flag_no_ancient_blocks, + verifier_settings: verifier_settings, }; Cmd::Run(run_cmd) }; @@ -702,6 +707,16 @@ impl Configuration { !ui_disabled } + + fn verifier_settings(&self) -> VerifierSettings { + let mut settings = VerifierSettings::default(); + settings.scale_verifiers = self.args.flag_scale_verifiers; + if let Some(num_verifiers) = self.args.flag_num_verifiers { + settings.num_verifiers = num_verifiers; + } + + settings + } } #[cfg(test)] diff --git a/parity/run.rs b/parity/run.rs index f977c450c..20a372424 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -28,6 +28,7 @@ use ethcore::service::ClientService; use ethcore::account_provider::AccountProvider; use ethcore::miner::{Miner, MinerService, ExternalMiner, MinerOptions}; use ethcore::snapshot; +use ethcore::verification::queue::VerifierSettings; use ethsync::SyncConfig; use informant::Informant; @@ -92,6 +93,7 @@ pub struct RunCmd { pub no_periodic_snapshot: bool, pub check_seal: bool, pub download_old_blocks: bool, + pub verifier_settings: VerifierSettings, } pub fn open_ui(dapps_conf: &dapps::Configuration, signer_conf: &signer::Configuration) -> Result<(), String> { @@ -217,7 +219,7 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { miner.set_transactions_limit(cmd.miner_extras.transactions_limit); // create client config - let client_config = to_client_config( + let mut client_config = to_client_config( &cmd.cache_config, mode.clone(), tracing, @@ -231,6 +233,8 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { cmd.check_seal, ); + client_config.queue.verifier_settings = cmd.verifier_settings; + // set up bootnodes let mut net_conf = cmd.net_conf; if !cmd.custom_bootnodes { From 80f98bc8b7a43275ef172119b17693e6b534850d Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 2 Dec 2016 18:36:00 +0100 Subject: [PATCH 41/69] fix tests --- parity/cli/config.full.toml | 2 ++ parity/cli/config.toml | 1 + parity/cli/mod.rs | 12 ++++++++++-- parity/configuration.rs | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index fcd9a9712..ffb0848ea 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -94,6 +94,8 @@ cache_size = 128 # Overrides above caches with total size fast_and_loose = false db_compaction = "ssd" fat_db = "auto" +scale_verifiers = true +num_verifiers = 6 [snapshots] disable_periodic = false diff --git a/parity/cli/config.toml b/parity/cli/config.toml index c9bd563a8..02b4a9577 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -57,6 +57,7 @@ cache_size_queue = 100 cache_size_state = 25 db_compaction = "ssd" fat_db = "off" +scale_verifiers = false, [snapshots] disable_periodic = true diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 30b273b38..1d1bcc4c0 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -242,8 +242,10 @@ usage! { or |c: &Config| otry!(c.footprint).db_compaction.clone(), flag_fat_db: String = "auto", or |c: &Config| otry!(c.footprint).fat_db.clone(), - flag_scale_verifiers: bool = false, or |_| None, - flag_num_verifiers: Option = None, or |_| None, + flag_scale_verifiers: bool = false, + or |c: &Config| otry!(c.footprint).scale_verifiers.clone(), + flag_num_verifiers: Option = None, + or |c: &Config| otry!(c.footprint).num_verifiers.clone().map(Some), // -- Import/Export Options flag_from: String = "1", or |_| None, @@ -404,6 +406,8 @@ struct Footprint { cache_size_state: Option, db_compaction: Option, fat_db: Option, + scale_verifiers: Option, + num_verifiers: Option, } #[derive(Default, Debug, PartialEq, RustcDecodable)] @@ -604,6 +608,8 @@ mod tests { flag_fast_and_loose: false, flag_db_compaction: "ssd".into(), flag_fat_db: "auto".into(), + flag_scale_verifiers: true, + flag_num_verifiers: Some(6), // -- Import/Export Options flag_from: "1".into(), @@ -773,6 +779,8 @@ mod tests { cache_size_state: Some(25), db_compaction: Some("ssd".into()), fat_db: Some("off".into()), + scale_verifiers: Some(false), + num_verifiers: None, }), snapshots: Some(Snapshots { disable_periodic: Some(true), diff --git a/parity/configuration.rs b/parity/configuration.rs index 7b8100bbb..55783e5b2 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -813,6 +813,7 @@ mod tests { vm_type: VMType::Interpreter, check_seal: true, with_color: !cfg!(windows), + verifier_settings: Default::default(), }))); } @@ -936,6 +937,7 @@ mod tests { no_periodic_snapshot: false, check_seal: true, download_old_blocks: true, + verifier_settings: Default::default(), })); } From 2911c549e317a17bf50d3e7395a006d2bbf36eef Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 4 Dec 2016 10:48:26 -0800 Subject: [PATCH 42/69] Encode networkid as a u64. --- ethcore/src/client/client.rs | 2 +- ethcore/src/client/test_client.rs | 2 +- ethcore/src/client/traits.rs | 2 +- ethcore/src/engines/mod.rs | 2 +- ethcore/src/ethereum/ethash.rs | 8 ++++---- ethcore/src/spec/spec.rs | 4 ++-- ethcore/src/types/transaction.rs | 16 ++++++++-------- parity/run.rs | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 85ec05b03..ddfdd836d 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1272,7 +1272,7 @@ impl BlockChainClient for Client { self.miner.pending_transactions(self.chain.read().best_block_number()) } - fn signing_network_id(&self) -> Option { + fn signing_network_id(&self) -> Option { self.engine.signing_network_id(&self.latest_env_info()) } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index c03b5920b..8d7e0b715 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -662,7 +662,7 @@ impl BlockChainClient for TestBlockChainClient { self.miner.pending_transactions(self.chain_info().best_block_number) } - fn signing_network_id(&self) -> Option { None } + fn signing_network_id(&self) -> Option { None } fn mode(&self) -> Mode { Mode::Active } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 6d774e250..30e47601a 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -237,7 +237,7 @@ pub trait BlockChainClient : Sync + Send { } /// Get the preferred network ID to sign on - fn signing_network_id(&self) -> Option; + fn signing_network_id(&self) -> Option; /// Get the mode. fn mode(&self) -> Mode; diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index c70a19de8..2732f3032 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -113,7 +113,7 @@ pub trait Engine : Sync + Send { fn verify_transaction(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) } /// The network ID that transactions should be signed with. - fn signing_network_id(&self, _env_info: &EnvInfo) -> Option { None } + fn signing_network_id(&self, _env_info: &EnvInfo) -> Option { None } /// Verify the seal of a block. This is an auxilliary method that actually just calls other `verify_` methods /// to get the job done. By default it must pass `verify_basic` and `verify_block_unordered`. If more or fewer diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 38a1df525..ab6169d00 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -163,9 +163,9 @@ impl Engine for Ethash { } } - fn signing_network_id(&self, env_info: &EnvInfo) -> Option { - if env_info.number >= self.ethash_params.eip155_transition && self.params().network_id < 127 { - Some(self.params().network_id as u8) + fn signing_network_id(&self, env_info: &EnvInfo) -> Option { + if env_info.number >= self.ethash_params.eip155_transition { + Some(self.params().network_id) } else { None } @@ -314,7 +314,7 @@ impl Engine for Ethash { } if let Some(n) = t.network_id() { - if header.number() < self.ethash_params.eip155_transition || n as usize != self.params().network_id { + if header.number() < self.ethash_params.eip155_transition || n != self.params().network_id { return Err(TransactionError::InvalidNetworkId.into()) } } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 71c15bca2..a4a4a4c6b 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -38,7 +38,7 @@ pub struct CommonParams { /// Maximum size of extra data. pub maximum_extra_data_size: usize, /// Network id. - pub network_id: usize, + pub network_id: u64, /// Main subprotocol name. pub subprotocol_name: String, /// Minimum gas limit. @@ -167,7 +167,7 @@ impl Spec { pub fn nodes(&self) -> &[String] { &self.nodes } /// Get the configured Network ID. - pub fn network_id(&self) -> usize { self.params.network_id } + pub fn network_id(&self) -> u64 { self.params.network_id } /// Get the configured subprotocol name. pub fn subprotocol_name(&self) -> String { self.params.subprotocol_name.clone() } diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 7dba4da53..29afb2226 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -72,7 +72,7 @@ pub struct Transaction { impl Transaction { /// Append object with a without signature into RLP stream - pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, network_id: Option) { + pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream, network_id: Option) { s.begin_list(if network_id.is_none() { 6 } else { 9 }); s.append(&self.nonce); s.append(&self.gas_price); @@ -140,26 +140,26 @@ impl From for SignedTransaction { impl Transaction { /// The message hash of the transaction. - pub fn hash(&self, network_id: Option) -> H256 { + pub fn hash(&self, network_id: Option) -> H256 { let mut stream = RlpStream::new(); self.rlp_append_unsigned_transaction(&mut stream, network_id); stream.out().sha3() } /// Signs the transaction as coming from `sender`. - pub fn sign(self, secret: &Secret, network_id: Option) -> SignedTransaction { + pub fn sign(self, secret: &Secret, network_id: Option) -> SignedTransaction { let sig = ::ethkey::sign(secret, &self.hash(network_id)) .expect("data is valid and context has signing capabilities; qed"); self.with_signature(sig, network_id) } /// Signs the transaction with signature. - pub fn with_signature(self, sig: Signature, network_id: Option) -> SignedTransaction { + pub fn with_signature(self, sig: Signature, network_id: Option) -> SignedTransaction { SignedTransaction { unsigned: self, r: sig.r().into(), s: sig.s().into(), - v: sig.v() + if let Some(n) = network_id { 35 + n * 2 } else { 27 }, + v: sig.v() as u64 + if let Some(n) = network_id { 35 + n * 2 } else { 27 }, hash: Cell::new(None), sender: Cell::new(None), } @@ -211,7 +211,7 @@ pub struct SignedTransaction { unsigned: Transaction, /// The V field of the signature; the LS bit described which half of the curve our point falls /// in. The MS bits describe which network this transaction is for. If 27/28, its for all networks. - v: u8, + v: u64, /// The R field of the signature; helps describe the point on the curve. r: U256, /// The S field of the signature; helps describe the point on the curve. @@ -302,10 +302,10 @@ impl SignedTransaction { } /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if invalid. - pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => (v - 1) % 2, _ => 4 } } + pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => ((v - 1) % 2) as u8, _ => 4 } } /// The network ID, or `None` if this is a global transaction. - pub fn network_id(&self) -> Option { + pub fn network_id(&self) -> Option { match self.v { v if v > 36 => Some((v - 35) / 2), _ => None, diff --git a/parity/run.rs b/parity/run.rs index f977c450c..70c0b86f3 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -194,7 +194,7 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { let mut sync_config = SyncConfig::default(); sync_config.network_id = match cmd.network_id { Some(id) => id, - None => spec.network_id(), + None => spec.network_id() as usize, }; if spec.subprotocol_name().len() != 3 { warn!("Your chain specification's subprotocol length is not 3. Ignoring."); From 767d48601429931c2b5b846818860d712c889b72 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 5 Dec 2016 14:12:40 +0100 Subject: [PATCH 43/69] fix config test --- parity/cli/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parity/cli/config.toml b/parity/cli/config.toml index 02b4a9577..74f3477f3 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -57,7 +57,7 @@ cache_size_queue = 100 cache_size_state = 25 db_compaction = "ssd" fat_db = "off" -scale_verifiers = false, +scale_verifiers = false [snapshots] disable_periodic = true From 42fc770d30f9d0a16aa4aef2da89d31c6c966186 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 5 Dec 2016 16:25:03 +0300 Subject: [PATCH 44/69] use crates.io crate --- Cargo.lock | 4 +- Cargo.toml | 2 +- util/fdlimit/Cargo.toml | 10 ---- util/fdlimit/src/lib.rs | 19 ------- util/fdlimit/src/raise_fd_limit.rs | 88 ------------------------------ 5 files changed, 4 insertions(+), 119 deletions(-) delete mode 100644 util/fdlimit/Cargo.toml delete mode 100644 util/fdlimit/src/lib.rs delete mode 100644 util/fdlimit/src/raise_fd_limit.rs diff --git a/Cargo.lock b/Cargo.lock index 19fe95318..fce6577e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ dependencies = [ "ethcore-stratum 1.4.0", "ethcore-util 1.5.0", "ethsync 1.5.0", - "fdlimit 0.1.0", + "fdlimit 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -685,6 +685,7 @@ dependencies = [ [[package]] name = "fdlimit" version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2027,6 +2028,7 @@ dependencies = [ "checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5" "checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "" "checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f" +"checksum fdlimit 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "651810f1715f923432bf2d94eef91d46ea0b66de5041dda9ce1af3f7aea42d6f" "checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb" "checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" diff --git a/Cargo.toml b/Cargo.toml index e0e4df2e9..078d2916c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ serde = "0.8.0" serde_json = "0.8.0" hyper = { version = "0.9", default-features = false } ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" } -fdlimit = { path = "util/fdlimit" } +fdlimit = "0.1" ethcore = { path = "ethcore" } ethcore-util = { path = "util" } ethsync = { path = "sync" } diff --git a/util/fdlimit/Cargo.toml b/util/fdlimit/Cargo.toml deleted file mode 100644 index 42aa582fe..000000000 --- a/util/fdlimit/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -description = "Utility function to raise file descriptor limit on OS X" -homepage = "http://ethcore.io" -license = "GPL-3.0" -name = "fdlimit" -version = "0.1.0" -authors = ["Ethcore "] - -[dependencies] -libc = "0.2" diff --git a/util/fdlimit/src/lib.rs b/util/fdlimit/src/lib.rs deleted file mode 100644 index 92c403058..000000000 --- a/util/fdlimit/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015, 2016 Ethcore (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 .extern crate libc; - -extern crate libc; -mod raise_fd_limit; -pub use raise_fd_limit::raise_fd_limit; diff --git a/util/fdlimit/src/raise_fd_limit.rs b/util/fdlimit/src/raise_fd_limit.rs deleted file mode 100644 index d0539fda9..000000000 --- a/util/fdlimit/src/raise_fd_limit.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/// darwin_fd_limit exists to work around an issue where launchctl on Mac OS X -/// defaults the rlimit maxfiles to 256/unlimited. The default soft limit of 256 -/// ends up being far too low for our multithreaded scheduler testing, depending -/// on the number of cores available. -/// -#[cfg(any(target_os = "macos", target_os = "ios"))] -#[allow(non_camel_case_types)] -pub fn raise_fd_limit() { - use libc; - use std::cmp; - use std::io; - use std::mem::size_of_val; - use std::ptr::null_mut; - - unsafe { - static CTL_KERN: libc::c_int = 1; - static KERN_MAXFILESPERPROC: libc::c_int = 29; - - // The strategy here is to fetch the current resource limits, read the - // kern.maxfilesperproc sysctl value, and bump the soft resource limit for - // maxfiles up to the sysctl value. - - // Fetch the kern.maxfilesperproc value - let mut mib: [libc::c_int; 2] = [CTL_KERN, KERN_MAXFILESPERPROC]; - let mut maxfiles: libc::c_int = 0; - let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t; - if libc::sysctl(&mut mib[0], 2, &mut maxfiles as *mut _ as *mut _, &mut size, - null_mut(), 0) != 0 { - let err = io::Error::last_os_error(); - panic!("raise_fd_limit: error calling sysctl: {}", err); - } - - // Fetch the current resource limits - let mut rlim = libc::rlimit{rlim_cur: 0, rlim_max: 0}; - if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) != 0 { - let err = io::Error::last_os_error(); - panic!("raise_fd_limit: error calling getrlimit: {}", err); - } - - // Bump the soft limit to the smaller of kern.maxfilesperproc and the hard - // limit - rlim.rlim_cur = cmp::min(maxfiles as libc::rlim_t, rlim.rlim_max); - - // Set our newly-increased resource limit - if libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) != 0 { - let err = io::Error::last_os_error(); - panic!("raise_fd_limit: error calling setrlimit: {}", err); - } - } -} - -#[cfg(any(target_os = "linux"))] -#[allow(non_camel_case_types)] -pub fn raise_fd_limit() { - use libc; - use std::io; - - unsafe { - // Fetch the current resource limits - let mut rlim = libc::rlimit{rlim_cur: 0, rlim_max: 0}; - if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) != 0 { - let err = io::Error::last_os_error(); - panic!("raise_fd_limit: error calling getrlimit: {}", err); - } - - // Set soft limit to hard imit - rlim.rlim_cur = rlim.rlim_max; - - // Set our newly-increased resource limit - if libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) != 0 { - let err = io::Error::last_os_error(); - panic!("raise_fd_limit: error calling setrlimit: {}", err); - } - } -} - -#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))] -pub fn raise_fd_limit() {} From 43ec3d8f79b0435be55c87af1586c73e750b99d3 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 5 Dec 2016 06:54:31 -0800 Subject: [PATCH 45/69] network_id -> u64 --- parity/cli/mod.rs | 6 +++--- parity/configuration.rs | 2 +- parity/run.rs | 4 ++-- rpc/src/v1/tests/helpers/sync_provider.rs | 2 +- sync/src/api.rs | 2 +- sync/src/chain.rs | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index d33c58d9d..7fcdd2209 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -48,7 +48,7 @@ usage! { flag_testnet: bool, flag_import_geth_keys: bool, flag_datadir: Option, - flag_networkid: Option, + flag_networkid: Option, flag_peers: Option, flag_nodekey: Option, flag_nodiscover: bool, @@ -122,7 +122,7 @@ usage! { or |c: &Config| otry!(c.network).nat.clone(), flag_allow_ips: String = "all", or |c: &Config| otry!(c.network).allow_ips.clone(), - flag_network_id: Option = None, + flag_network_id: Option = None, or |c: &Config| otry!(c.network).id.clone().map(Some), flag_bootnodes: Option = None, or |c: &Config| otry!(c.network).bootnodes.clone().map(|vec| Some(vec.join(","))), @@ -328,7 +328,7 @@ struct Network { max_pending_peers: Option, nat: Option, allow_ips: Option, - id: Option, + id: Option, bootnodes: Option>, discovery: Option, node_key: Option, diff --git a/parity/configuration.rs b/parity/configuration.rs index c4a54f747..37c699521 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -527,7 +527,7 @@ impl Configuration { Ok(ret) } - fn network_id(&self) -> Option { + fn network_id(&self) -> Option { self.args.flag_network_id.or(self.args.flag_networkid) } diff --git a/parity/run.rs b/parity/run.rs index 70c0b86f3..42a972000 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -70,7 +70,7 @@ pub struct RunCmd { pub http_conf: HttpConfiguration, pub ipc_conf: IpcConfiguration, pub net_conf: NetworkConfiguration, - pub network_id: Option, + pub network_id: Option, pub warp_sync: bool, pub acc_conf: AccountsConfig, pub gas_pricer: GasPricerConfig, @@ -194,7 +194,7 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { let mut sync_config = SyncConfig::default(); sync_config.network_id = match cmd.network_id { Some(id) => id, - None => spec.network_id() as usize, + None => spec.network_id(), }; if spec.subprotocol_name().len() != 3 { warn!("Your chain specification's subprotocol length is not 3. Ignoring."); diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index 24be33417..8800d926a 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -23,7 +23,7 @@ use ethsync::{SyncProvider, SyncStatus, SyncState, PeerInfo, TransactionStats}; /// TestSyncProvider config. pub struct Config { /// Protocol version. - pub network_id: usize, + pub network_id: u64, /// Number of peers. pub num_peers: usize, } diff --git a/sync/src/api.rs b/sync/src/api.rs index cdab0983a..5613b77ff 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -44,7 +44,7 @@ pub struct SyncConfig { /// Enable ancient block download. pub download_old_blocks: bool, /// Network ID - pub network_id: usize, + pub network_id: u64, /// Main "eth" subprotocol name. pub subprotocol_name: [u8; 3], /// Fork block to check diff --git a/sync/src/chain.rs b/sync/src/chain.rs index bd312c9ee..2d53ad5ee 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -192,7 +192,7 @@ pub struct SyncStatus { /// Syncing protocol version. That's the maximum protocol version we connect to. pub protocol_version: u8, /// The underlying p2p network version. - pub network_id: usize, + pub network_id: u64, /// `BlockChain` height for the moment the sync started. pub start_block_number: BlockNumber, /// Last fully downloaded and imported block number (if any). @@ -273,7 +273,7 @@ struct PeerInfo { /// Peer chain genesis hash genesis: H256, /// Peer network id - network_id: usize, + network_id: u64, /// Peer best block hash latest_hash: H256, /// Peer total difficulty if known @@ -341,7 +341,7 @@ pub struct ChainSync { /// Last propagated block number last_sent_block_number: BlockNumber, /// Network ID - network_id: usize, + network_id: u64, /// Optional fork block to check fork_block: Option<(BlockNumber, H256)>, /// Snapshot downloader. From c61a0e97b376734d3ee035974582aa58d11de96d Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 5 Dec 2016 15:20:32 +0000 Subject: [PATCH 46/69] make engine determine block order --- ethcore/src/blockchain/blockchain.rs | 50 +++++++++++++++----------- ethcore/src/client/client.rs | 4 +-- ethcore/src/engines/authority_round.rs | 17 ++++++++- ethcore/src/engines/mod.rs | 9 ++++- ethcore/src/engines/null_engine.rs | 6 ++++ ethcore/src/ethereum/ethash.rs | 11 ++++++ ethcore/src/snapshot/service.rs | 3 +- ethcore/src/snapshot/tests/blocks.rs | 18 +++++----- ethcore/src/spec/spec.rs | 3 +- ethcore/src/tests/helpers.rs | 6 ++-- 10 files changed, 87 insertions(+), 40 deletions(-) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 68fb40483..0e8e0a271 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -34,6 +34,7 @@ use blockchain::update::ExtrasUpdate; use blockchain::{CacheSize, ImportRoute, Config}; use db::{self, Writable, Readable, CacheUpdatePolicy}; use cache_manager::CacheManager; +use engines::Engine; const LOG_BLOOMS_LEVELS: usize = 3; const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16; @@ -198,6 +199,9 @@ pub struct BlockChain { pending_block_hashes: RwLock>, pending_block_details: RwLock>, pending_transaction_addresses: RwLock>>, + + // Used for block ordering. + engine: Arc, } impl BlockProvider for BlockChain { @@ -415,9 +419,8 @@ impl<'a> Iterator for AncestryIter<'a> { } impl BlockChain { - #[cfg_attr(feature="dev", allow(useless_let_if_seq))] - /// Create new instance of blockchain from given Genesis - pub fn new(config: Config, genesis: &[u8], db: Arc) -> BlockChain { + /// Create new instance of blockchain from given Genesis and block picking rules of Engine. + pub fn new(config: Config, genesis: &[u8], db: Arc, engine: Arc) -> BlockChain { // 400 is the avarage size of the key let cache_man = CacheManager::new(config.pref_cache_size, config.max_cache_size, 400); @@ -442,6 +445,7 @@ impl BlockChain { pending_block_hashes: RwLock::new(HashMap::new()), pending_block_details: RwLock::new(HashMap::new()), pending_transaction_addresses: RwLock::new(HashMap::new()), + engine: engine, }; // load best block @@ -858,13 +862,12 @@ impl BlockChain { let number = header.number(); let parent_hash = header.parent_hash(); let parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); - let total_difficulty = parent_details.total_difficulty + header.difficulty(); - let is_new_best = total_difficulty > self.best_block_total_difficulty(); + let is_new_best = self.engine.is_new_best_block(self.best_block_total_difficulty(), HeaderView::new(&self.best_block_header()), &parent_details, header); BlockInfo { hash: hash, number: number, - total_difficulty: total_difficulty, + total_difficulty: parent_details.total_difficulty + header.difficulty(), location: if is_new_best { // on new best block we need to make sure that all ancestors // are moved to "canon chain" @@ -1319,11 +1322,16 @@ mod tests { use views::BlockView; use transaction::{Transaction, Action}; use log_entry::{LogEntry, LocalizedLogEntry}; + use spec::Spec; fn new_db(path: &str) -> Arc { Arc::new(Database::open(&DatabaseConfig::with_columns(::db::NUM_COLUMNS), path).unwrap()) } + fn new_chain(genesis: &[u8], db: Arc) -> BlockChain { + BlockChain::new(Config::default(), genesis, db, Spec::new_null().engine) + } + #[test] fn should_cache_best_block() { // given @@ -1334,7 +1342,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); assert_eq!(bc.best_block_number(), 0); // when @@ -1360,7 +1368,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); assert_eq!(bc.genesis_hash(), genesis_hash.clone()); assert_eq!(bc.best_block_hash(), genesis_hash.clone()); @@ -1391,7 +1399,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); let mut block_hashes = vec![genesis_hash.clone()]; let mut batch = db.transaction(); @@ -1427,7 +1435,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); let mut batch =db.transaction(); for b in &[&b1a, &b1b, &b2a, &b2b, &b3a, &b3b, &b4a, &b4b, &b5a, &b5b] { @@ -1489,7 +1497,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); let mut batch = db.transaction(); let _ = bc.insert_block(&mut batch, &b1a, vec![]); @@ -1577,7 +1585,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); let mut batch = db.transaction(); let _ = bc.insert_block(&mut batch, &b1a, vec![]); @@ -1639,7 +1647,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); let mut batch = db.transaction(); let ir1 = bc.insert_block(&mut batch, &b1, vec![]); @@ -1755,7 +1763,7 @@ mod tests { let temp = RandomTempPath::new(); { let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); assert_eq!(bc.best_block_hash(), genesis_hash); let mut batch =db.transaction(); bc.insert_block(&mut batch, &first, vec![]); @@ -1766,7 +1774,7 @@ mod tests { { let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); assert_eq!(bc.best_block_hash(), first_hash); } @@ -1821,7 +1829,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); let mut batch =db.transaction(); bc.insert_block(&mut batch, &b1, vec![]); db.write(batch).unwrap(); @@ -1881,7 +1889,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); insert_block(&db, &bc, &b1, vec![Receipt { state_root: H256::default(), gas_used: 10_000.into(), @@ -1985,7 +1993,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 5); let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 5); @@ -2042,7 +2050,7 @@ mod tests { { let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); let uncle = canon_chain.fork(1).generate(&mut finalizer.fork()).unwrap(); let mut batch =db.transaction(); @@ -2061,7 +2069,7 @@ mod tests { // re-loading the blockchain should load the correct best block. let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); assert_eq!(bc.best_block_number(), 5); } @@ -2078,7 +2086,7 @@ mod tests { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + let bc = new_chain(&genesis, db.clone()); let mut batch =db.transaction(); bc.insert_block(&mut batch, &first, vec![]); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 85ec05b03..98260936d 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -164,7 +164,7 @@ impl Client { let gb = spec.genesis_block(); let db = Arc::new(try!(Database::open(&db_config, &path.to_str().expect("DB path could not be converted to string.")).map_err(ClientError::Database))); - let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone())); + let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone(), spec.engine.clone())); let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone())); let trie_spec = match config.fat_db { @@ -787,7 +787,7 @@ impl snapshot::DatabaseRestore for Client { 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())); + *chain = Arc::new(BlockChain::new(self.config.blockchain.clone(), &[], db.clone(), self.engine.clone())); *tracedb = TraceDB::new(self.config.tracing.clone(), db.clone(), chain.clone()); Ok(()) } diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 6c1d0a409..c278a011a 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -21,7 +21,7 @@ use std::sync::Weak; use std::time::{UNIX_EPOCH, Duration}; use util::*; use ethkey::{verify_address, Signature}; -use rlp::{UntrustedRlp, View, encode}; +use rlp::{Rlp, UntrustedRlp, View, encode}; use account_provider::AccountProvider; use block::*; use spec::CommonParams; @@ -35,6 +35,8 @@ use service::ClientIoMessage; use transaction::SignedTransaction; use env_info::EnvInfo; use builtin::Builtin; +use blockchain::extras::BlockDetails; +use views::HeaderView; /// `AuthorityRound` params. #[derive(Debug, PartialEq)] @@ -310,6 +312,19 @@ impl Engine for AuthorityRound { let mut guard = self.message_channel.lock(); *guard = Some(message_channel); } + + fn is_new_best_block(&self, _best_total_difficulty: U256, best_header: HeaderView, _parent_details: &BlockDetails, new_header: &HeaderView) -> bool { + let new_number = new_header.number(); + let best_number = best_header.number(); + if new_number != best_number { + new_number > best_number + } else { + // Take the oldest step at given height. + let new_step: usize = Rlp::new(&new_header.seal()[0]).as_val(); + let best_step: usize = Rlp::new(&best_header.seal()[0]).as_val(); + new_step < best_step + } + } } #[cfg(test)] diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index c70a19de8..163901edc 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -38,6 +38,9 @@ use io::IoChannel; use service::ClientIoMessage; use header::Header; use transaction::SignedTransaction; +use ethereum::ethash; +use blockchain::extras::BlockDetails; +use views::HeaderView; /// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. /// Provides hooks into each of the major parts of block import. @@ -146,5 +149,9 @@ pub trait Engine : Sync + Send { /// Add a channel for communication with Client which can be used for sealing. fn register_message_channel(&self, _message_channel: IoChannel) {} - // TODO: sealing stuff - though might want to leave this for later. + + /// Check if new block should be chosen as the one in chain. + fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool { + ethash::is_new_best_block(best_total_difficulty, parent_details, new_header) + } } diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index e0906ce22..bd5a4474a 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -38,6 +38,12 @@ impl NullEngine { } } +impl Default for NullEngine { + fn default() -> Self { + Self::new(Default::default(), Default::default()) + } +} + impl Engine for NullEngine { fn name(&self) -> &str { "NullEngine" diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 38a1df525..0ca9ff00e 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -21,6 +21,7 @@ use builtin::Builtin; use env_info::EnvInfo; use error::{BlockError, TransactionError, Error}; use header::Header; +use views::HeaderView; use state::CleanupMode; use spec::CommonParams; use transaction::SignedTransaction; @@ -28,6 +29,7 @@ use engines::Engine; use evm::Schedule; use ethjson; use rlp::{self, UntrustedRlp, View}; +use blockchain::extras::BlockDetails; /// Ethash params. #[derive(Debug, PartialEq)] @@ -325,6 +327,15 @@ impl Engine for Ethash { fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> { t.sender().map(|_|()) // Perform EC recovery and cache sender } + + /// Check if a new block should replace the best blockchain block. + fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool { + is_new_best_block(best_total_difficulty, parent_details, new_header) + } +} + +pub fn is_new_best_block(best_total_difficulty: U256, parent_details: &BlockDetails, new_header: &HeaderView) -> bool { + parent_details.total_difficulty + new_header.difficulty() > best_total_difficulty } #[cfg_attr(feature="dev", allow(wrong_self_convention))] diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index c0d34a6a9..47c4f992b 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -32,6 +32,7 @@ use engines::Engine; use error::Error; use ids::BlockID; use service::ClientIoMessage; +use spec::Spec; use io::IoChannel; @@ -99,7 +100,7 @@ impl Restoration { let raw_db = Arc::new(try!(Database::open(params.db_config, &*params.db_path.to_string_lossy()) .map_err(UtilError::SimpleString))); - let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone()); + let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone(), Spec::new_null().engine); let blocks = try!(BlockRebuilder::new(chain, raw_db.clone(), &manifest)); let root = manifest.state_root.clone(); diff --git a/ethcore/src/snapshot/tests/blocks.rs b/ethcore/src/snapshot/tests/blocks.rs index 18637bad1..3d9390d2e 100644 --- a/ethcore/src/snapshot/tests/blocks.rs +++ b/ethcore/src/snapshot/tests/blocks.rs @@ -37,13 +37,14 @@ fn chunk_and_restore(amount: u64) { let genesis = canon_chain.generate(&mut finalizer).unwrap(); let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let engine = Arc::new(::engines::NullEngine::default()); let orig_path = RandomTempPath::create_dir(); let new_path = RandomTempPath::create_dir(); let mut snapshot_path = new_path.as_path().to_owned(); snapshot_path.push("SNAP"); let old_db = Arc::new(Database::open(&db_cfg, orig_path.as_str()).unwrap()); - let bc = BlockChain::new(Default::default(), &genesis, old_db.clone()); + let bc = BlockChain::new(Default::default(), &genesis, old_db.clone(), engine.clone()); // build the blockchain. let mut batch = old_db.transaction(); @@ -73,21 +74,20 @@ fn chunk_and_restore(amount: u64) { // restore it. let new_db = Arc::new(Database::open(&db_cfg, new_path.as_str()).unwrap()); - let new_chain = BlockChain::new(Default::default(), &genesis, new_db.clone()); + let new_chain = BlockChain::new(Default::default(), &genesis, new_db.clone(), engine.clone()); let mut rebuilder = BlockRebuilder::new(new_chain, new_db.clone(), &manifest).unwrap(); let reader = PackedReader::new(&snapshot_path).unwrap().unwrap(); - let engine = ::engines::NullEngine::new(Default::default(), Default::default()); let flag = AtomicBool::new(true); for chunk_hash in &reader.manifest().block_hashes { let compressed = reader.chunk(*chunk_hash).unwrap(); let chunk = snappy::decompress(&compressed).unwrap(); - rebuilder.feed(&chunk, &engine, &flag).unwrap(); + rebuilder.feed(&chunk, engine.as_ref(), &flag).unwrap(); } rebuilder.finalize(HashMap::new()).unwrap(); // and test it. - let new_chain = BlockChain::new(Default::default(), &genesis, new_db); + let new_chain = BlockChain::new(Default::default(), &genesis, new_db, engine); assert_eq!(new_chain.best_block_hash(), best_hash); } @@ -121,8 +121,8 @@ fn checks_flag() { let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); let db = Arc::new(Database::open(&db_cfg, path.as_str()).unwrap()); - let chain = BlockChain::new(Default::default(), &genesis, db.clone()); - let engine = ::engines::NullEngine::new(Default::default(), Default::default()); + let engine = Arc::new(::engines::NullEngine::default()); + let chain = BlockChain::new(Default::default(), &genesis, db.clone(), engine.clone()); let manifest = ::snapshot::ManifestData { state_hashes: Vec::new(), @@ -134,8 +134,8 @@ fn checks_flag() { let mut rebuilder = BlockRebuilder::new(chain, db.clone(), &manifest).unwrap(); - match rebuilder.feed(&chunk, &engine, &AtomicBool::new(false)) { + match rebuilder.feed(&chunk, engine.as_ref(), &AtomicBool::new(false)) { Err(Error::Snapshot(SnapshotError::RestorationAborted)) => {} _ => panic!("Wrong result on abort flag set") } -} \ No newline at end of file +} diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 71c15bca2..1e00ae57b 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -30,8 +30,7 @@ use ethjson; use rlp::{Rlp, RlpStream, View, Stream}; /// Parameters common to all engines. -#[derive(Debug, PartialEq, Clone)] -#[cfg_attr(test, derive(Default))] +#[derive(Debug, PartialEq, Clone, Default)] pub struct CommonParams { /// Account start nonce. pub account_start_nonce: U256, diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index 77a6f117a..e952fe27a 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -286,7 +286,7 @@ fn new_db(path: &str) -> Arc { pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); + let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone(), Spec::new_null().engine); let mut batch = db.transaction(); for block_order in 1..block_number { @@ -304,7 +304,7 @@ pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult GuardedTempResult { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); + let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone(), Spec::new_null().engine); let mut batch = db.transaction(); @@ -323,7 +323,7 @@ pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempRes pub fn generate_dummy_empty_blockchain() -> GuardedTempResult { let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); - let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone()); + let bc = BlockChain::new(BlockChainConfig::default(), &create_unverifiable_block(0, H256::zero()), db.clone(), Spec::new_null().engine); GuardedTempResult:: { _temp: temp, From 94302f3f610f0d1cd3ff08ae25d6663fce4afb2d Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 5 Dec 2016 15:27:44 +0000 Subject: [PATCH 47/69] throw out difficulty checks --- ethcore/src/engines/authority_round.rs | 5 ----- ethcore/src/ethereum/ethash.rs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index c278a011a..f28a06117 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -274,7 +274,6 @@ impl Engine for AuthorityRound { } fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { - // Don't calculate difficulty for genesis blocks. if header.number() == 0 { return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); } @@ -286,10 +285,6 @@ impl Engine for AuthorityRound { try!(Err(BlockError::DoubleVote(header.author().clone()))); } - // Check difficulty is correct given the two timestamps. - if header.difficulty() != parent.difficulty() { - return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() }))) - } let gas_limit_divisor = self.our_params.gas_limit_bound_divisor; let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor; let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor; diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 0ca9ff00e..96c1298cc 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -328,12 +328,12 @@ impl Engine for Ethash { t.sender().map(|_|()) // Perform EC recovery and cache sender } - /// Check if a new block should replace the best blockchain block. fn is_new_best_block(&self, best_total_difficulty: U256, _best_header: HeaderView, parent_details: &BlockDetails, new_header: &HeaderView) -> bool { is_new_best_block(best_total_difficulty, parent_details, new_header) } } +/// Check if a new block should replace the best blockchain block. pub fn is_new_best_block(best_total_difficulty: U256, parent_details: &BlockDetails, new_header: &HeaderView) -> bool { parent_details.total_difficulty + new_header.difficulty() > best_total_difficulty } From 53b479fb7a3b766ec5672dae8f55dd0f5d2b2451 Mon Sep 17 00:00:00 2001 From: keorn Date: Mon, 5 Dec 2016 15:43:46 +0000 Subject: [PATCH 48/69] pass engine to snapshot service --- ethcore/src/snapshot/service.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index 47c4f992b..89ee68de0 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -32,7 +32,6 @@ use engines::Engine; use error::Error; use ids::BlockID; use service::ClientIoMessage; -use spec::Spec; use io::IoChannel; @@ -82,6 +81,7 @@ struct Restoration { struct RestorationParams<'a> { manifest: ManifestData, // manifest to base restoration on. pruning: Algorithm, // pruning algorithm for the database. + engine: Arc, // consensus engine of the chain. db_path: PathBuf, // database path db_config: &'a DatabaseConfig, // configuration for the database. writer: Option, // writer for recovered snapshot. @@ -100,7 +100,7 @@ impl Restoration { let raw_db = Arc::new(try!(Database::open(params.db_config, &*params.db_path.to_string_lossy()) .map_err(UtilError::SimpleString))); - let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone(), Spec::new_null().engine); + let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone(), params.engine); let blocks = try!(BlockRebuilder::new(chain, raw_db.clone(), &manifest)); let root = manifest.state_root.clone(); @@ -421,6 +421,7 @@ impl Service { let params = RestorationParams { manifest: manifest, pruning: self.pruning, + engine: self.engine.clone(), db_path: self.restoration_db(), db_config: &self.db_config, writer: writer, From 61c3358447c7afc4e35e748a675acca5304bbf62 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 5 Dec 2016 16:55:33 +0100 Subject: [PATCH 49/69] move light to its own crate again --- ethcore/{src/light => light/src}/client.rs | 0 .../{src/light/mod.rs => light/src/lib.rs} | 14 +- .../light => light/src}/net/buffer_flow.rs | 0 ethcore/{src/light => light/src}/net/error.rs | 0 ethcore/{src/light => light/src}/net/mod.rs | 0 .../{src/light => light/src}/net/status.rs | 0 ethcore/light/src/provider.rs | 194 ++++++++++++++++++ ethcore/src/client/client.rs | 126 ++---------- ethcore/src/client/mod.rs | 2 +- ethcore/src/client/traits.rs | 25 ++- ethcore/src/lib.rs | 1 - ethcore/src/light/provider.rs | 77 ------- ethcore/src/types/les_request.rs | 167 --------------- ethcore/src/types/mod.rs.in | 3 +- 14 files changed, 247 insertions(+), 362 deletions(-) rename ethcore/{src/light => light/src}/client.rs (100%) rename ethcore/{src/light/mod.rs => light/src/lib.rs} (86%) rename ethcore/{src/light => light/src}/net/buffer_flow.rs (100%) rename ethcore/{src/light => light/src}/net/error.rs (100%) rename ethcore/{src/light => light/src}/net/mod.rs (100%) rename ethcore/{src/light => light/src}/net/status.rs (100%) create mode 100644 ethcore/light/src/provider.rs delete mode 100644 ethcore/src/light/provider.rs delete mode 100644 ethcore/src/types/les_request.rs diff --git a/ethcore/src/light/client.rs b/ethcore/light/src/client.rs similarity index 100% rename from ethcore/src/light/client.rs rename to ethcore/light/src/client.rs diff --git a/ethcore/src/light/mod.rs b/ethcore/light/src/lib.rs similarity index 86% rename from ethcore/src/light/mod.rs rename to ethcore/light/src/lib.rs index dca592453..e698905e8 100644 --- a/ethcore/src/light/mod.rs +++ b/ethcore/light/src/lib.rs @@ -35,5 +35,17 @@ pub mod client; pub mod net; pub mod provider; +mod types; + pub use self::provider::Provider; -pub use types::les_request as request; \ No newline at end of file +pub use types::les_request as request; + +#[macro_use] +extern crate log; + +extern crate ethcore; +extern crate ethcore_util as util; +extern crate ethcore_network as network; +extern crate ethcore_io as io; +extern crate rlp; +extern crate time; \ No newline at end of file diff --git a/ethcore/src/light/net/buffer_flow.rs b/ethcore/light/src/net/buffer_flow.rs similarity index 100% rename from ethcore/src/light/net/buffer_flow.rs rename to ethcore/light/src/net/buffer_flow.rs diff --git a/ethcore/src/light/net/error.rs b/ethcore/light/src/net/error.rs similarity index 100% rename from ethcore/src/light/net/error.rs rename to ethcore/light/src/net/error.rs diff --git a/ethcore/src/light/net/mod.rs b/ethcore/light/src/net/mod.rs similarity index 100% rename from ethcore/src/light/net/mod.rs rename to ethcore/light/src/net/mod.rs diff --git a/ethcore/src/light/net/status.rs b/ethcore/light/src/net/status.rs similarity index 100% rename from ethcore/src/light/net/status.rs rename to ethcore/light/src/net/status.rs diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs new file mode 100644 index 000000000..64357e540 --- /dev/null +++ b/ethcore/light/src/provider.rs @@ -0,0 +1,194 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! A provider for the LES protocol. This is typically a full node, who can +//! give as much data as necessary to its peers. + +use ethcore::blockchain_info::BlockChainInfo; +use ethcore::client::{BlockChainClient, ProvingBlockChainClient}; +use ethcore::transaction::SignedTransaction; + +use util::{Bytes, H256}; + +use light::request; + +/// Defines the operations that a provider for `LES` must fulfill. +/// +/// These are defined at [1], but may be subject to change. +/// Requests which can't be fulfilled should return either an empty RLP list +/// or empty vector where appropriate. +/// +/// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) +pub trait Provider: Send + Sync { + /// Provide current blockchain info. + fn chain_info(&self) -> BlockChainInfo; + + /// Find the depth of a common ancestor between two blocks. + /// If either block is unknown or an ancestor can't be found + /// then return `None`. + fn reorg_depth(&self, a: &H256, b: &H256) -> Option; + + /// Earliest block where state queries are available. + /// If `None`, no state queries are servable. + fn earliest_state(&self) -> Option; + + /// Provide a list of headers starting at the requested block, + /// possibly in reverse and skipping `skip` at a time. + /// + /// The returned vector may have any length in the range [0, `max`], but the + /// results within must adhere to the `skip` and `reverse` parameters. + fn block_headers(&self, req: request::Headers) -> Vec; + + /// Provide as many as possible of the requested blocks (minus the headers) encoded + /// in RLP format. + fn block_bodies(&self, req: request::Bodies) -> Vec; + + /// Provide the receipts as many as possible of the requested blocks. + /// Returns a vector of RLP-encoded lists of receipts. + fn receipts(&self, req: request::Receipts) -> Vec; + + /// Provide a set of merkle proofs, as requested. Each request is a + /// block hash and request parameters. + /// + /// Returns a vector of RLP-encoded lists satisfying the requests. + fn proofs(&self, req: request::StateProofs) -> Vec; + + /// Provide contract code for the specified (block_hash, account_hash) pairs. + /// Each item in the resulting vector is either the raw bytecode or empty. + fn contract_code(&self, req: request::ContractCodes) -> Vec; + + /// Provide header proofs from the Canonical Hash Tries. + fn header_proofs(&self, req: request::HeaderProofs) -> Vec; + + /// Provide pending transactions. + fn pending_transactions(&self) -> Vec; +} + +// Implementation of a light client data provider for a client. +impl light::Provider for T { + fn chain_info(&self) -> BlockChainInfo { + BlockChainClient::chain_info(self) + } + + fn reorg_depth(&self, a: &H256, b: &H256) -> Option { + self.tree_route(a, b).map(|route| route.index as u64) + } + + fn earliest_state(&self) -> Option { + Some(self.pruning_info().earliest_state) + } + + fn block_headers(&self, req: request::Headers) -> Vec { + let best_num = self.chain.read().best_block_number(); + let start_num = req.block_num; + + match self.block_hash(BlockID::Number(req.block_num)) { + Some(hash) if hash == req.block_hash => {} + _=> { + trace!(target: "les_provider", "unknown/non-canonical start block in header request: {:?}", (req.block_num, req.block_hash)); + return vec![] + } + } + + (0u64..req.max as u64) + .map(|x: u64| x.saturating_mul(req.skip)) + .take_while(|x| if req.reverse { x < &start_num } else { best_num - start_num < *x }) + .map(|x| if req.reverse { start_num - x } else { start_num + x }) + .map(|x| self.block_header(BlockID::Number(x))) + .take_while(|x| x.is_some()) + .flat_map(|x| x) + .collect() + } + + fn block_bodies(&self, req: request::Bodies) -> Vec { + use ids::BlockID; + + req.block_hashes.into_iter() + .map(|hash| self.block_body(BlockID::Hash(hash))) + .map(|body| body.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) + .collect() + } + + fn receipts(&self, req: request::Receipts) -> Vec { + req.block_hashes.into_iter() + .map(|hash| self.block_receipts(&hash)) + .map(|receipts| receipts.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) + .collect() + } + + fn proofs(&self, req: request::StateProofs) -> Vec { + use rlp::{EMPTY_LIST_RLP, RlpStream, Stream}; + + let mut results = Vec::with_capacity(req.requests.len()); + + for request in req.requests { + let state = match self.state_at(BlockID::Hash(request.block)) { + Some(state) => state, + None => { + trace!(target: "light_provider", "state for {} not available", request.block); + results.push(EMPTY_LIST_RLP.to_vec()); + continue; + } + }; + + let res = match request.key2 { + Some(storage_key) => state.prove_storage(request.key1, storage_key, request.from_level), + None => state.prove_account(request.key1, request.from_level), + }; + + match res { + Ok(records) => { + let mut stream = RlpStream::new_list(records.len()); + for record in records { + stream.append_raw(&record, 1); + } + results.push(stream.out()) + } + Err(e) => { + debug!(target: "light_provider", "encountered error {} while forming proof of state at {}", e, request.block); + results.push(EMPTY_LIST_RLP.to_vec()); + } + } + } + + results + } + + fn contract_code(&self, req: request::ContractCodes) -> Vec { + req.code_requests.into_iter() + .map(|req| { + self.state_at(BlockID::Hash(req.block_hash)) + .map(|state| { + match state.code_by_address_hash(req.account_key) { + Ok(code) => code.unwrap_or_else(Vec::new), + Err(e) => { + debug!(target: "light_provider", "encountered error {} while fetching code.", e); + Vec::new() + } + } + }).unwrap_or_else(Vec::new) + }) + .collect() + } + + fn header_proofs(&self, req: request::HeaderProofs) -> Vec { + req.requests.into_iter().map(|_| ::rlp::EMPTY_LIST_RLP.to_vec()).collect() + } + + fn pending_transactions(&self) -> Vec { + BlockChainClient::pending_transactions(self) + } +} \ No newline at end of file diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 9204d3f93..dc61cdf3c 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -52,7 +52,7 @@ use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use client::{ BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, - ChainNotify, PruningInfo, + ChainNotify, PruningInfo, ProvingBlockChainClient, }; use client::Error as ClientError; use env_info::EnvInfo; @@ -68,7 +68,6 @@ use factory::Factories; use rlp::{decode, View, UntrustedRlp}; use state_db::StateDB; use rand::OsRng; -use light::{self, request}; // re-export pub use types::blockchain_info::BlockChainInfo; @@ -1378,119 +1377,24 @@ impl MayPanic for Client { } } -// Implementation of a light client data provider for a client. -impl light::Provider for Client { - fn chain_info(&self) -> BlockChainInfo { - BlockChainClient::chain_info(self) +impl ProvingBlockChainClient for Client { + fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockID) -> Vec { + self.state_at(id) + .and_then(move |state| state.prove_storage(key1, key2, from_level).ok()) + .unwrap_or_else(Vec::new) } - fn reorg_depth(&self, a: &H256, b: &H256) -> Option { - self.tree_route(a, b).map(|route| route.index as u64) + fn prove_account(&self, key1: H256, from_level: u32, id: BlockID) -> Vec { + self.state_at(id) + .and_then(move |state| state.prove_account(key1, from_level).ok()) + .unwrap_or_else(Vec::new) } - fn earliest_state(&self) -> Option { - Some(self.pruning_info().earliest_state) - } - - fn block_headers(&self, req: request::Headers) -> Vec { - let best_num = self.chain.read().best_block_number(); - let start_num = req.block_num; - - match self.block_hash(BlockID::Number(req.block_num)) { - Some(hash) if hash == req.block_hash => {} - _=> { - trace!(target: "les_provider", "unknown/non-canonical start block in header request: {:?}", (req.block_num, req.block_hash)); - return vec![] - } - } - - (0u64..req.max as u64) - .map(|x: u64| x.saturating_mul(req.skip)) - .take_while(|x| if req.reverse { x < &start_num } else { best_num - start_num < *x }) - .map(|x| if req.reverse { start_num - x } else { start_num + x }) - .map(|x| self.block_header(BlockID::Number(x))) - .take_while(|x| x.is_some()) - .flat_map(|x| x) - .collect() - } - - fn block_bodies(&self, req: request::Bodies) -> Vec { - use ids::BlockID; - - req.block_hashes.into_iter() - .map(|hash| self.block_body(BlockID::Hash(hash))) - .map(|body| body.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) - .collect() - } - - fn receipts(&self, req: request::Receipts) -> Vec { - req.block_hashes.into_iter() - .map(|hash| self.block_receipts(&hash)) - .map(|receipts| receipts.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) - .collect() - } - - fn proofs(&self, req: request::StateProofs) -> Vec { - use rlp::{EMPTY_LIST_RLP, RlpStream, Stream}; - - let mut results = Vec::with_capacity(req.requests.len()); - - for request in req.requests { - let state = match self.state_at(BlockID::Hash(request.block)) { - Some(state) => state, - None => { - trace!(target: "light_provider", "state for {} not available", request.block); - results.push(EMPTY_LIST_RLP.to_vec()); - continue; - } - }; - - let res = match request.key2 { - Some(storage_key) => state.prove_storage(request.key1, storage_key, request.from_level), - None => state.prove_account(request.key1, request.from_level), - }; - - match res { - Ok(records) => { - let mut stream = RlpStream::new_list(records.len()); - for record in records { - stream.append_raw(&record, 1); - } - results.push(stream.out()) - } - Err(e) => { - debug!(target: "light_provider", "encountered error {} while forming proof of state at {}", e, request.block); - results.push(EMPTY_LIST_RLP.to_vec()); - } - } - } - - results - } - - fn contract_code(&self, req: request::ContractCodes) -> Vec { - req.code_requests.into_iter() - .map(|req| { - self.state_at(BlockID::Hash(req.block_hash)) - .map(|state| { - match state.code_by_address_hash(req.account_key) { - Ok(code) => code.unwrap_or_else(Vec::new), - Err(e) => { - debug!(target: "light_provider", "encountered error {} while fetching code.", e); - Vec::new() - } - } - }).unwrap_or_else(Vec::new) - }) - .collect() - } - - fn header_proofs(&self, req: request::HeaderProofs) -> Vec { - req.requests.into_iter().map(|_| ::rlp::EMPTY_LIST_RLP.to_vec()).collect() - } - - fn pending_transactions(&self) -> Vec { - BlockChainClient::pending_transactions(self) + fn code_by_hash(&self, account_key: H256, id: BlockID) -> Bytes { + self.state_at(id) + .and_then(move |state| state.code_by_address_hash(account_key).ok()) + .and_then(|x| x) + .unwrap_or_else(Vec::new) } } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 7eafbee36..af4daece6 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -27,7 +27,7 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChain pub use self::error::Error; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::chain_notify::ChainNotify; -pub use self::traits::{BlockChainClient, MiningBlockChainClient}; +pub use self::traits::{BlockChainClient, MiningBlockChainClient, ProvingBlockChainClient}; pub use types::ids::*; pub use types::trace_filter::Filter as TraceFilter; diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index d2cafa592..85f6da0e6 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -256,8 +256,10 @@ pub trait BlockChainClient : Sync + Send { fn pruning_info(&self) -> PruningInfo; } +impl IpcConfig for BlockChainClient { } + /// Extended client interface used for mining -pub trait MiningBlockChainClient : BlockChainClient { +pub trait MiningBlockChainClient: BlockChainClient { /// Returns OpenBlock prepared for closing. fn prepare_open_block(&self, author: Address, @@ -275,4 +277,23 @@ pub trait MiningBlockChainClient : BlockChainClient { fn latest_schedule(&self) -> Schedule; } -impl IpcConfig for BlockChainClient { } +/// Extended client interface for providing proofs of the state. +pub trait ProvingBlockChainClient: BlockChainClient { + /// Prove account storage at a specific block id. + /// + /// Both provided keys assume a secure trie. + /// Returns a vector of raw trie nodes (in order from the root) proving the storage query. + /// Nodes after `from_level` may be omitted. + /// An empty vector indicates unservable query. + fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockID) -> Vec; + + /// Prove account existence at a specific block id. + /// The key is the keccak hash of the account's address. + /// Returns a vector of raw trie nodes (in order from the root) proving the query. + /// Nodes after `from_level` may be omitted. + /// An empty vector indicates unservable query. + fn prove_account(&self, key1: H256, from_level: u32, id: BlockID) -> Vec; + + /// Get code by address hash. + fn code_by_hash(&self, account_key: H256, id: BlockID) -> Bytes; +} \ No newline at end of file diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index f5994129b..de09013ce 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -140,7 +140,6 @@ pub mod snapshot; pub mod action_params; pub mod db; pub mod verification; -pub mod light; #[macro_use] pub mod evm; mod cache_manager; diff --git a/ethcore/src/light/provider.rs b/ethcore/src/light/provider.rs deleted file mode 100644 index 5aa61828a..000000000 --- a/ethcore/src/light/provider.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015, 2016 Ethcore (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 . - -//! A provider for the LES protocol. This is typically a full node, who can -//! give as much data as necessary to its peers. - -use transaction::SignedTransaction; -use blockchain_info::BlockChainInfo; - -use util::{Bytes, H256}; - -use light::request; - -/// Defines the operations that a provider for `LES` must fulfill. -/// -/// These are defined at [1], but may be subject to change. -/// Requests which can't be fulfilled should return either an empty RLP list -/// or empty vector where appropriate. -/// -/// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) -pub trait Provider: Send + Sync { - /// Provide current blockchain info. - fn chain_info(&self) -> BlockChainInfo; - - /// Find the depth of a common ancestor between two blocks. - /// If either block is unknown or an ancestor can't be found - /// then return `None`. - fn reorg_depth(&self, a: &H256, b: &H256) -> Option; - - /// Earliest block where state queries are available. - /// If `None`, no state queries are servable. - fn earliest_state(&self) -> Option; - - /// Provide a list of headers starting at the requested block, - /// possibly in reverse and skipping `skip` at a time. - /// - /// The returned vector may have any length in the range [0, `max`], but the - /// results within must adhere to the `skip` and `reverse` parameters. - fn block_headers(&self, req: request::Headers) -> Vec; - - /// Provide as many as possible of the requested blocks (minus the headers) encoded - /// in RLP format. - fn block_bodies(&self, req: request::Bodies) -> Vec; - - /// Provide the receipts as many as possible of the requested blocks. - /// Returns a vector of RLP-encoded lists of receipts. - fn receipts(&self, req: request::Receipts) -> Vec; - - /// Provide a set of merkle proofs, as requested. Each request is a - /// block hash and request parameters. - /// - /// Returns a vector of RLP-encoded lists satisfying the requests. - fn proofs(&self, req: request::StateProofs) -> Vec; - - /// Provide contract code for the specified (block_hash, account_hash) pairs. - /// Each item in the resulting vector is either the raw bytecode or empty. - fn contract_code(&self, req: request::ContractCodes) -> Vec; - - /// Provide header proofs from the Canonical Hash Tries. - fn header_proofs(&self, req: request::HeaderProofs) -> Vec; - - /// Provide pending transactions. - fn pending_transactions(&self) -> Vec; -} \ No newline at end of file diff --git a/ethcore/src/types/les_request.rs b/ethcore/src/types/les_request.rs deleted file mode 100644 index d0de080ee..000000000 --- a/ethcore/src/types/les_request.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2015, 2016 Ethcore (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 . - -//! LES request types. - -use util::H256; - -/// A request for block headers. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct Headers { - /// Starting block number - pub block_num: u64, - /// Starting block hash. This and number could be combined but IPC codegen is - /// not robust enough to support it. - pub block_hash: H256, - /// The maximum amount of headers which can be returned. - pub max: usize, - /// The amount of headers to skip between each response entry. - pub skip: u64, - /// Whether the headers should proceed in falling number from the initial block. - pub reverse: bool, -} - -/// A request for specific block bodies. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct Bodies { - /// Hashes which bodies are being requested for. - pub block_hashes: Vec -} - -/// A request for transaction receipts. -/// -/// This request is answered with a list of transaction receipts for each block -/// requested. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct Receipts { - /// Block hashes to return receipts for. - pub block_hashes: Vec, -} - -/// A request for a state proof -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct StateProof { - /// Block hash to query state from. - pub block: H256, - /// Key of the state trie -- corresponds to account hash. - pub key1: H256, - /// Key in that account's storage trie; if empty, then the account RLP should be - /// returned. - pub key2: Option, - /// if greater than zero, trie nodes beyond this level may be omitted. - pub from_level: u32, // could even safely be u8; trie w/ 32-byte key can be at most 64-levels deep. -} - -/// A request for state proofs. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct StateProofs { - /// All the proof requests. - pub requests: Vec, -} - -/// A request for contract code. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct ContractCode { - /// Block hash - pub block_hash: H256, - /// Account key (== sha3(address)) - pub account_key: H256, -} - -/// A request for contract code. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct ContractCodes { - /// Block hash and account key (== sha3(address)) pairs to fetch code for. - pub code_requests: Vec, -} - -/// A request for a header proof from the Canonical Hash Trie. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct HeaderProof { - /// Number of the CHT. - pub cht_number: u64, - /// Block number requested. - pub block_number: u64, - /// If greater than zero, trie nodes beyond this level may be omitted. - pub from_level: u32, -} - -/// A request for header proofs from the CHT. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub struct HeaderProofs { - /// All the proof requests. - pub requests: Vec, -} - -/// Kinds of requests. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Binary)] -pub enum Kind { - /// Requesting headers. - Headers, - /// Requesting block bodies. - Bodies, - /// Requesting transaction receipts. - Receipts, - /// Requesting proofs of state trie nodes. - StateProofs, - /// Requesting contract code by hash. - Codes, - /// Requesting header proofs (from the CHT). - HeaderProofs, -} - -/// Encompasses all possible types of requests in a single structure. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] -pub enum Request { - /// Requesting headers. - Headers(Headers), - /// Requesting block bodies. - Bodies(Bodies), - /// Requesting transaction receipts. - Receipts(Receipts), - /// Requesting state proofs. - StateProofs(StateProofs), - /// Requesting contract codes. - Codes(ContractCodes), - /// Requesting header proofs. - HeaderProofs(HeaderProofs), -} - -impl Request { - /// Get the kind of request this is. - pub fn kind(&self) -> Kind { - match *self { - Request::Headers(_) => Kind::Headers, - Request::Bodies(_) => Kind::Bodies, - Request::Receipts(_) => Kind::Receipts, - Request::StateProofs(_) => Kind::StateProofs, - Request::Codes(_) => Kind::Codes, - Request::HeaderProofs(_) => Kind::HeaderProofs, - } - } - - /// Get the amount of requests being made. - pub fn amount(&self) -> usize { - match *self { - Request::Headers(ref req) => req.max, - Request::Bodies(ref req) => req.block_hashes.len(), - Request::Receipts(ref req) => req.block_hashes.len(), - Request::StateProofs(ref req) => req.requests.len(), - Request::Codes(ref req) => req.code_requests.len(), - Request::HeaderProofs(ref req) => req.requests.len(), - } - } -} \ No newline at end of file diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in index 4f996173e..d462d3cb9 100644 --- a/ethcore/src/types/mod.rs.in +++ b/ethcore/src/types/mod.rs.in @@ -34,5 +34,4 @@ pub mod block_import_error; pub mod restoration_status; pub mod snapshot_manifest; pub mod mode; -pub mod pruning_info; -pub mod les_request; +pub mod pruning_info; \ No newline at end of file From a6c2408562ef6da7abfaa6f2d5ec5dccd7e6349f Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 5 Dec 2016 16:56:21 +0100 Subject: [PATCH 50/69] IPC codegen in ethcore-light; remove network dependency --- ethcore/Cargo.toml | 1 - ethcore/light/Cargo.toml | 20 +++ ethcore/light/build.rs | 21 ++++ ethcore/light/src/types/les_request.rs | 167 +++++++++++++++++++++++++ ethcore/light/src/types/mod.rs | 20 +++ ethcore/light/src/types/mod.rs.in | 17 +++ ethcore/src/lib.rs | 1 - 7 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 ethcore/light/Cargo.toml create mode 100644 ethcore/light/build.rs create mode 100644 ethcore/light/src/types/les_request.rs create mode 100644 ethcore/light/src/types/mod.rs create mode 100644 ethcore/light/src/types/mod.rs.in diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 713cc1045..bd87c422f 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -42,7 +42,6 @@ ethcore-ipc-nano = { path = "../ipc/nano" } rlp = { path = "../util/rlp" } lru-cache = "0.1.0" ethcore-bloom-journal = { path = "../util/bloom" } -ethcore-network = { path = "../util/network" } [dependencies.hyper] git = "https://github.com/ethcore/hyper" diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml new file mode 100644 index 000000000..3aca67c64 --- /dev/null +++ b/ethcore/light/Cargo.toml @@ -0,0 +1,20 @@ +[package] +description = "Parity LES primitives" +homepage = "https://ethcore.io" +license = "GPL-3.0" +name = "ethcore-light" +version = "1.5.0" +authors = ["Ethcore "] +build = "build.rs" + +[build-dependencies] +"ethcore-ipc-codegen" = { path = "../../ipc/codegen" } + +[dependencies] +log = "0.3" +ethcore = { path = ".." } +ethcore-util = { path = "../../util" } +ethcore-network = { path = "../../util/network" } +ethcore-io = { path = "../../util/io" } +rlp = { path = "../../util/rlp" } +time = "0.1" \ No newline at end of file diff --git a/ethcore/light/build.rs b/ethcore/light/build.rs new file mode 100644 index 000000000..cff92a011 --- /dev/null +++ b/ethcore/light/build.rs @@ -0,0 +1,21 @@ +// Copyright 2015, 2016 Ethcore (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 . + +extern crate ethcore_ipc_codegen; + +fn main() { + ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap(); +} diff --git a/ethcore/light/src/types/les_request.rs b/ethcore/light/src/types/les_request.rs new file mode 100644 index 000000000..d0de080ee --- /dev/null +++ b/ethcore/light/src/types/les_request.rs @@ -0,0 +1,167 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! LES request types. + +use util::H256; + +/// A request for block headers. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct Headers { + /// Starting block number + pub block_num: u64, + /// Starting block hash. This and number could be combined but IPC codegen is + /// not robust enough to support it. + pub block_hash: H256, + /// The maximum amount of headers which can be returned. + pub max: usize, + /// The amount of headers to skip between each response entry. + pub skip: u64, + /// Whether the headers should proceed in falling number from the initial block. + pub reverse: bool, +} + +/// A request for specific block bodies. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct Bodies { + /// Hashes which bodies are being requested for. + pub block_hashes: Vec +} + +/// A request for transaction receipts. +/// +/// This request is answered with a list of transaction receipts for each block +/// requested. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct Receipts { + /// Block hashes to return receipts for. + pub block_hashes: Vec, +} + +/// A request for a state proof +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct StateProof { + /// Block hash to query state from. + pub block: H256, + /// Key of the state trie -- corresponds to account hash. + pub key1: H256, + /// Key in that account's storage trie; if empty, then the account RLP should be + /// returned. + pub key2: Option, + /// if greater than zero, trie nodes beyond this level may be omitted. + pub from_level: u32, // could even safely be u8; trie w/ 32-byte key can be at most 64-levels deep. +} + +/// A request for state proofs. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct StateProofs { + /// All the proof requests. + pub requests: Vec, +} + +/// A request for contract code. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct ContractCode { + /// Block hash + pub block_hash: H256, + /// Account key (== sha3(address)) + pub account_key: H256, +} + +/// A request for contract code. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct ContractCodes { + /// Block hash and account key (== sha3(address)) pairs to fetch code for. + pub code_requests: Vec, +} + +/// A request for a header proof from the Canonical Hash Trie. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct HeaderProof { + /// Number of the CHT. + pub cht_number: u64, + /// Block number requested. + pub block_number: u64, + /// If greater than zero, trie nodes beyond this level may be omitted. + pub from_level: u32, +} + +/// A request for header proofs from the CHT. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub struct HeaderProofs { + /// All the proof requests. + pub requests: Vec, +} + +/// Kinds of requests. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Binary)] +pub enum Kind { + /// Requesting headers. + Headers, + /// Requesting block bodies. + Bodies, + /// Requesting transaction receipts. + Receipts, + /// Requesting proofs of state trie nodes. + StateProofs, + /// Requesting contract code by hash. + Codes, + /// Requesting header proofs (from the CHT). + HeaderProofs, +} + +/// Encompasses all possible types of requests in a single structure. +#[derive(Debug, Clone, PartialEq, Eq, Binary)] +pub enum Request { + /// Requesting headers. + Headers(Headers), + /// Requesting block bodies. + Bodies(Bodies), + /// Requesting transaction receipts. + Receipts(Receipts), + /// Requesting state proofs. + StateProofs(StateProofs), + /// Requesting contract codes. + Codes(ContractCodes), + /// Requesting header proofs. + HeaderProofs(HeaderProofs), +} + +impl Request { + /// Get the kind of request this is. + pub fn kind(&self) -> Kind { + match *self { + Request::Headers(_) => Kind::Headers, + Request::Bodies(_) => Kind::Bodies, + Request::Receipts(_) => Kind::Receipts, + Request::StateProofs(_) => Kind::StateProofs, + Request::Codes(_) => Kind::Codes, + Request::HeaderProofs(_) => Kind::HeaderProofs, + } + } + + /// Get the amount of requests being made. + pub fn amount(&self) -> usize { + match *self { + Request::Headers(ref req) => req.max, + Request::Bodies(ref req) => req.block_hashes.len(), + Request::Receipts(ref req) => req.block_hashes.len(), + Request::StateProofs(ref req) => req.requests.len(), + Request::Codes(ref req) => req.code_requests.len(), + Request::HeaderProofs(ref req) => req.requests.len(), + } + } +} \ No newline at end of file diff --git a/ethcore/light/src/types/mod.rs b/ethcore/light/src/types/mod.rs new file mode 100644 index 000000000..2625358a3 --- /dev/null +++ b/ethcore/light/src/types/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2015, 2016 Ethcore (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 . + +//! Types used in the public (IPC) api which require custom code generation. + +#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues +include!(concat!(env!("OUT_DIR"), "/mod.rs.in")); diff --git a/ethcore/light/src/types/mod.rs.in b/ethcore/light/src/types/mod.rs.in new file mode 100644 index 000000000..2b7386b5b --- /dev/null +++ b/ethcore/light/src/types/mod.rs.in @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (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 . + +pub mod les_request; \ No newline at end of file diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index de09013ce..ca343b1a7 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -102,7 +102,6 @@ extern crate rlp; extern crate ethcore_bloom_journal as bloom_journal; extern crate byteorder; extern crate transient_hashmap; -extern crate ethcore_network as network; extern crate linked_hash_map; #[macro_use] From 5db93cd4337f47527bb6d0bbd8df1bd167dee22f Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 5 Dec 2016 17:09:05 +0100 Subject: [PATCH 51/69] light: fix compile errors --- ethcore/light/Cargo.toml | 3 +- ethcore/light/src/client.rs | 20 +++++------ ethcore/light/src/lib.rs | 1 + ethcore/light/src/net/buffer_flow.rs | 2 +- ethcore/light/src/net/mod.rs | 6 ++-- ethcore/light/src/provider.rs | 54 ++++++++-------------------- 6 files changed, 31 insertions(+), 55 deletions(-) diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 3aca67c64..74400d7ab 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -16,5 +16,6 @@ ethcore = { path = ".." } ethcore-util = { path = "../../util" } ethcore-network = { path = "../../util/network" } ethcore-io = { path = "../../util/io" } +ethcore-ipc = { path = "../../ipc/rpc" } rlp = { path = "../../util/rlp" } -time = "0.1" \ No newline at end of file +time = "0.1" \ No newline at end of file diff --git a/ethcore/light/src/client.rs b/ethcore/light/src/client.rs index 699db743a..8a5c43c48 100644 --- a/ethcore/light/src/client.rs +++ b/ethcore/light/src/client.rs @@ -19,21 +19,21 @@ use std::sync::Arc; -use engines::Engine; -use ids::BlockID; -use service::ClientIoMessage; -use block_import_error::BlockImportError; -use block_status::BlockStatus; -use verification::queue::{HeaderQueue, QueueInfo}; -use transaction::SignedTransaction; -use blockchain_info::BlockChainInfo; +use ethcore::engines::Engine; +use ethcore::ids::BlockID; +use ethcore::service::ClientIoMessage; +use ethcore::block_import_error::BlockImportError; +use ethcore::block_status::BlockStatus; +use ethcore::verification::queue::{HeaderQueue, QueueInfo}; +use ethcore::transaction::SignedTransaction; +use ethcore::blockchain_info::BlockChainInfo; use io::IoChannel; use util::hash::H256; use util::{Bytes, Mutex}; -use light::provider::Provider; -use light::request; +use provider::Provider; +use request; /// Light client implementation. pub struct Client { diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index e698905e8..e150f4ee5 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -47,5 +47,6 @@ extern crate ethcore; extern crate ethcore_util as util; extern crate ethcore_network as network; extern crate ethcore_io as io; +extern crate ethcore_ipc as ipc; extern crate rlp; extern crate time; \ No newline at end of file diff --git a/ethcore/light/src/net/buffer_flow.rs b/ethcore/light/src/net/buffer_flow.rs index 7f653f826..6730c71a7 100644 --- a/ethcore/light/src/net/buffer_flow.rs +++ b/ethcore/light/src/net/buffer_flow.rs @@ -23,7 +23,7 @@ //! This module provides an interface for configuration of buffer //! flow costs and recharge rates. -use light::request; +use request; use super::packet; use super::error::Error; diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 2d38d4fd5..a1b3b30b0 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -19,6 +19,7 @@ //! This uses a "Provider" to answer requests. //! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) +use ethcore::transaction::SignedTransaction; use io::TimerToken; use network::{NetworkProtocolHandler, NetworkContext, NetworkError, PeerId}; use rlp::{RlpStream, Stream, UntrustedRlp, View}; @@ -29,9 +30,8 @@ use time::SteadyTime; use std::collections::{HashMap, HashSet}; use std::sync::atomic::{AtomicUsize, Ordering}; -use light::provider::Provider; -use light::request::{self, Request}; -use transaction::SignedTransaction; +use provider::Provider; +use request::{self, Request}; use self::buffer_flow::{Buffer, FlowParams}; use self::error::{Error, Punishment}; diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 64357e540..264df0397 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -20,10 +20,11 @@ use ethcore::blockchain_info::BlockChainInfo; use ethcore::client::{BlockChainClient, ProvingBlockChainClient}; use ethcore::transaction::SignedTransaction; +use ethcore::ids::BlockID; use util::{Bytes, H256}; -use light::request; +use request; /// Defines the operations that a provider for `LES` must fulfill. /// @@ -78,7 +79,7 @@ pub trait Provider: Send + Sync { } // Implementation of a light client data provider for a client. -impl light::Provider for T { +impl Provider for T { fn chain_info(&self) -> BlockChainInfo { BlockChainClient::chain_info(self) } @@ -92,7 +93,7 @@ impl light::Provider for T { } fn block_headers(&self, req: request::Headers) -> Vec { - let best_num = self.chain.read().best_block_number(); + let best_num = self.chain_info().best_block_number; let start_num = req.block_num; match self.block_hash(BlockID::Number(req.block_num)) { @@ -114,8 +115,6 @@ impl light::Provider for T { } fn block_bodies(&self, req: request::Bodies) -> Vec { - use ids::BlockID; - req.block_hashes.into_iter() .map(|hash| self.block_body(BlockID::Hash(hash))) .map(|body| body.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) @@ -130,38 +129,22 @@ impl light::Provider for T { } fn proofs(&self, req: request::StateProofs) -> Vec { - use rlp::{EMPTY_LIST_RLP, RlpStream, Stream}; + use rlp::{RlpStream, Stream}; let mut results = Vec::with_capacity(req.requests.len()); for request in req.requests { - let state = match self.state_at(BlockID::Hash(request.block)) { - Some(state) => state, - None => { - trace!(target: "light_provider", "state for {} not available", request.block); - results.push(EMPTY_LIST_RLP.to_vec()); - continue; - } + let proof = match request.key2 { + Some(key2) => self.prove_storage(request.key1, key2, request.from_level, BlockID::Hash(request.block)), + None => self.prove_account(request.key1, request.from_level, BlockID::Hash(request.block)), }; - let res = match request.key2 { - Some(storage_key) => state.prove_storage(request.key1, storage_key, request.from_level), - None => state.prove_account(request.key1, request.from_level), - }; - - match res { - Ok(records) => { - let mut stream = RlpStream::new_list(records.len()); - for record in records { - stream.append_raw(&record, 1); - } - results.push(stream.out()) - } - Err(e) => { - debug!(target: "light_provider", "encountered error {} while forming proof of state at {}", e, request.block); - results.push(EMPTY_LIST_RLP.to_vec()); - } + let mut stream = RlpStream::new_list(proof.len()); + for node in proof { + stream.append_raw(&node, 1); } + + results.push(stream.out()); } results @@ -170,16 +153,7 @@ impl light::Provider for T { fn contract_code(&self, req: request::ContractCodes) -> Vec { req.code_requests.into_iter() .map(|req| { - self.state_at(BlockID::Hash(req.block_hash)) - .map(|state| { - match state.code_by_address_hash(req.account_key) { - Ok(code) => code.unwrap_or_else(Vec::new), - Err(e) => { - debug!(target: "light_provider", "encountered error {} while fetching code.", e); - Vec::new() - } - } - }).unwrap_or_else(Vec::new) + self.code_by_hash(req.account_key, BlockID::Hash(req.block_hash)) }) .collect() } From ebd4173d2177dc41cd0f4d0a36a4af3ee5ff3353 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 5 Dec 2016 20:15:05 +0300 Subject: [PATCH 52/69] bump version --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fce6577e7..907c9b3aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ dependencies = [ "ethcore-stratum 1.4.0", "ethcore-util 1.5.0", "ethsync 1.5.0", - "fdlimit 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -684,7 +684,7 @@ dependencies = [ [[package]] name = "fdlimit" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2028,7 +2028,7 @@ dependencies = [ "checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5" "checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "" "checksum ethabi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0c53453517f620847be51943db329276ae52f2e210cfc659e81182864be2f" -"checksum fdlimit 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "651810f1715f923432bf2d94eef91d46ea0b66de5041dda9ce1af3f7aea42d6f" +"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" "checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb" "checksum gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)" = "91ecd03771effb0c968fd6950b37e89476a578aaf1c70297d8e92b6516ec3312" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" From 1b6ebe1a6d3425612b4483f34c5a8e5610ce5039 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 5 Dec 2016 18:18:56 +0100 Subject: [PATCH 53/69] possible fix for queue drop deadlock (#3702) * possible fix for #3686 * queue: simplify conclusion, don't block on joining * queue: park verifiers with timeout to prevent race * more robust verification loop * queue: re-introduce wait for verifier joining --- ethcore/src/verification/queue/mod.rs | 201 +++++++++++--------------- 1 file changed, 84 insertions(+), 117 deletions(-) diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index 686a1d093..14df0819c 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -17,7 +17,7 @@ //! A queue of blocks. Sits between network or other I/O and the `BlockChain`. //! Sorts them ready for blockchain insertion. -use std::thread::{JoinHandle, self}; +use std::thread::{self, JoinHandle}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}; use std::sync::{Condvar as SCondvar, Mutex as SMutex}; use util::*; @@ -64,35 +64,11 @@ impl Default for Config { } } -struct VerifierHandle { - deleting: Arc, - sleep: Arc, - thread: JoinHandle<()>, -} - -impl VerifierHandle { - // signal to the verifier thread that it should sleep. - fn sleep(&self) { - self.sleep.store(true, AtomicOrdering::SeqCst); - } - - // signal to the verifier thread that it should wake up. - fn wake_up(&self) { - self.sleep.store(false, AtomicOrdering::SeqCst); - self.thread.thread().unpark(); - } - - // signal to the verifier thread that it should conclude its - // operations. - fn conclude(&self) { - self.wake_up(); - self.deleting.store(true, AtomicOrdering::Release); - } - - // join the verifier thread. - fn join(self) { - self.thread.join().expect("Verifier thread panicked"); - } +// pool states +enum State { + // all threads with id < inner value are to work. + Work(usize), + Exit, } /// An item which is in the process of being verified. @@ -131,7 +107,6 @@ pub struct VerificationQueue { engine: Arc, more_to_verify: Arc, verification: Arc>, - verifiers: Mutex<(Vec, usize)>, deleting: Arc, ready_signal: Arc, empty: Arc, @@ -139,6 +114,8 @@ pub struct VerificationQueue { ticks_since_adjustment: AtomicUsize, max_queue_size: usize, max_mem_use: usize, + verifier_handles: Vec>, + state: Arc<(Mutex, Condvar)>, } struct QueueSignal { @@ -224,40 +201,39 @@ impl VerificationQueue { let max_verifiers = min(::num_cpus::get(), MAX_VERIFIERS); let default_amount = max(::num_cpus::get(), 3) - 2; - let mut verifiers = Vec::with_capacity(max_verifiers); + let state = Arc::new((Mutex::new(State::Work(default_amount)), Condvar::new())); + let mut verifier_handles = Vec::with_capacity(max_verifiers); debug!(target: "verification", "Allocating {} verifiers, {} initially active", max_verifiers, default_amount); for i in 0..max_verifiers { debug!(target: "verification", "Adding verification thread #{}", i); - let deleting = deleting.clone(); let panic_handler = panic_handler.clone(); let verification = verification.clone(); let engine = engine.clone(); let wait = more_to_verify.clone(); let ready = ready_signal.clone(); let empty = empty.clone(); + let state = state.clone(); - // enable only the first few verifiers. - let sleep = if i < default_amount { - Arc::new(AtomicBool::new(false)) - } else { - Arc::new(AtomicBool::new(true)) - }; - - verifiers.push(VerifierHandle { - deleting: deleting.clone(), - sleep: sleep.clone(), - thread: thread::Builder::new() - .name(format!("Verifier #{}", i)) - .spawn(move || { - panic_handler.catch_panic(move || { - VerificationQueue::verify(verification, engine, wait, ready, deleting, empty, sleep) - }).unwrap() - }) - .expect("Failed to create verifier thread.") - }); + let handle = thread::Builder::new() + .name(format!("Verifier #{}", i)) + .spawn(move || { + panic_handler.catch_panic(move || { + VerificationQueue::verify( + verification, + engine, + wait, + ready, + empty, + state, + i, + ) + }).unwrap() + }) + .expect("Failed to create verifier thread."); + verifier_handles.push(handle); } VerificationQueue { @@ -266,13 +242,14 @@ impl VerificationQueue { ready_signal: ready_signal, more_to_verify: more_to_verify, verification: verification, - verifiers: Mutex::new((verifiers, default_amount)), deleting: deleting, processing: RwLock::new(HashSet::new()), empty: empty, ticks_since_adjustment: AtomicUsize::new(0), max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT), max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT), + verifier_handles: verifier_handles, + state: state, } } @@ -281,23 +258,30 @@ impl VerificationQueue { engine: Arc, wait: Arc, ready: Arc, - deleting: Arc, empty: Arc, - sleep: Arc, + state: Arc<(Mutex, Condvar)>, + id: usize, ) { - while !deleting.load(AtomicOrdering::Acquire) { + loop { + // check current state. { - while sleep.load(AtomicOrdering::SeqCst) { - trace!(target: "verification", "Verifier sleeping"); - ::std::thread::park(); - trace!(target: "verification", "Verifier waking up"); + let mut cur_state = state.0.lock(); + while let State::Work(x) = *cur_state { + // sleep until this thread is required. + if id < x { break } - if deleting.load(AtomicOrdering::Acquire) { - return; - } + debug!(target: "verification", "verifier {} sleeping", id); + state.1.wait(&mut cur_state); + debug!(target: "verification", "verifier {} waking up", id); + } + + if let State::Exit = *cur_state { + debug!(target: "verification", "verifier {} exiting", id); + break; } } + // wait for work if empty. { let mut more_to_verify = verification.more_to_verify.lock().unwrap(); @@ -305,15 +289,22 @@ impl VerificationQueue { empty.notify_all(); } - while verification.unverified.lock().is_empty() && !deleting.load(AtomicOrdering::Acquire) { + while verification.unverified.lock().is_empty() { + if let State::Exit = *state.0.lock() { + debug!(target: "verification", "verifier {} exiting", id); + return; + } + more_to_verify = wait.wait(more_to_verify).unwrap(); } - if deleting.load(AtomicOrdering::Acquire) { + if let State::Exit = *state.0.lock() { + debug!(target: "verification", "verifier {} exiting", id); return; } } + // do work. let item = { // acquire these locks before getting the item to verify. let mut unverified = verification.unverified.lock(); @@ -568,6 +559,14 @@ impl VerificationQueue { } } + /// Get the current number of working verifiers. + pub fn num_verifiers(&self) -> usize { + match *self.state.0.lock() { + State::Work(x) => x, + State::Exit => panic!("state only set to exit on drop; queue live now; qed"), + } + } + /// Optimise memory footprint of the heap fields, and adjust the number of threads /// to better suit the workload. pub fn collect_garbage(&self) { @@ -604,7 +603,7 @@ impl VerificationQueue { return; } - let current = self.verifiers.lock().1; + let current = self.num_verifiers(); let diff = (v_len - u_len).abs(); let total = v_len + u_len; @@ -626,27 +625,14 @@ impl VerificationQueue { // possible, never going over the amount of initially allocated threads // or below 1. fn scale_verifiers(&self, target: usize) { - let mut verifiers = self.verifiers.lock(); - let &mut (ref mut verifiers, ref mut verifier_count) = &mut *verifiers; - - let target = min(verifiers.len(), target); + let current = self.num_verifiers(); + let target = min(self.verifier_handles.len(), target); let target = max(1, target); - debug!(target: "verification", "Scaling from {} to {} verifiers", verifier_count, target); + debug!(target: "verification", "Scaling from {} to {} verifiers", current, target); - // scaling up - for i in *verifier_count..target { - debug!(target: "verification", "Waking up verifier {}", i); - verifiers[i].wake_up(); - } - - // scaling down. - for i in target..*verifier_count { - debug!(target: "verification", "Putting verifier {} to sleep", i); - verifiers[i].sleep(); - } - - *verifier_count = target; + *self.state.0.lock() = State::Work(target); + self.state.1.notify_all(); } } @@ -660,22 +646,18 @@ impl Drop for VerificationQueue { fn drop(&mut self) { trace!(target: "shutdown", "[VerificationQueue] Closing..."); self.clear(); - self.deleting.store(true, AtomicOrdering::Release); + self.deleting.store(true, AtomicOrdering::SeqCst); - let mut verifiers = self.verifiers.get_mut(); - let mut verifiers = &mut verifiers.0; - - // first pass to signal conclusion. must be done before - // notify or deadlock possible. - for handle in verifiers.iter() { - handle.conclude(); - } + // set exit state; should be done before `more_to_verify` notification. + *self.state.0.lock() = State::Exit; + self.state.1.notify_all(); + // wake up all threads waiting for more work. self.more_to_verify.notify_all(); - // second pass to join. - for handle in verifiers.drain(..) { - handle.join(); + // wait for all verifier threads to join. + for thread in self.verifier_handles.drain(..) { + thread.join().expect("Propagating verifier thread panic on shutdown"); } trace!(target: "shutdown", "[VerificationQueue] Closed."); @@ -687,7 +669,7 @@ mod tests { use util::*; use io::*; use spec::*; - use super::{BlockQueue, Config}; + use super::{BlockQueue, Config, State}; use super::kind::blocks::Unverified; use tests::helpers::*; use error::*; @@ -784,11 +766,11 @@ mod tests { let queue = get_test_queue(); queue.scale_verifiers(MAX_VERIFIERS + 1); - assert!(queue.verifiers.lock().1 < MAX_VERIFIERS + 1); + assert!(queue.num_verifiers() < MAX_VERIFIERS + 1); queue.scale_verifiers(0); - assert!(queue.verifiers.lock().1 == 1); + assert!(queue.num_verifiers() == 1); } #[test] @@ -797,14 +779,7 @@ mod tests { // put all the verifiers to sleep to ensure // the test isn't timing sensitive. - let num_verifiers = { - let verifiers = queue.verifiers.lock(); - for i in 0..verifiers.1 { - verifiers.0[i].sleep(); - } - - verifiers.1 - }; + *queue.state.0.lock() = State::Work(0); for block in get_good_dummy_block_seq(5000) { queue.import(Unverified::new(block)).expect("Block good by definition; qed"); @@ -812,20 +787,12 @@ mod tests { // almost all unverified == bump verifier count. queue.collect_garbage(); - assert_eq!(queue.verifiers.lock().1, num_verifiers + 1); - - // wake them up again and verify everything. - { - let verifiers = queue.verifiers.lock(); - for i in 0..verifiers.1 { - verifiers.0[i].wake_up(); - } - } + assert_eq!(queue.num_verifiers(), 1); queue.flush(); // nothing to verify == use minimum number of verifiers. queue.collect_garbage(); - assert_eq!(queue.verifiers.lock().1, 1); + assert_eq!(queue.num_verifiers(), 1); } } From 873f451df123fe17ac91169b6fc75d84831af873 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 5 Dec 2016 18:42:44 +0100 Subject: [PATCH 54/69] Move decoding for contract deployment logic earlier (#3714) * Move decoding deployment logic earlier * Removed rendunant isContract --- js/src/ui/MethodDecoding/methodDecoding.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/js/src/ui/MethodDecoding/methodDecoding.js b/js/src/ui/MethodDecoding/methodDecoding.js index 70258f744..efb22b67b 100644 --- a/js/src/ui/MethodDecoding/methodDecoding.js +++ b/js/src/ui/MethodDecoding/methodDecoding.js @@ -457,6 +457,14 @@ class MethodDecoding extends Component { return; } + const { signature, paramdata } = api.util.decodeCallData(input); + this.setState({ methodSignature: signature, methodParams: paramdata }); + + if (!signature || signature === CONTRACT_CREATE || transaction.creates) { + this.setState({ isDeploy: true }); + return; + } + if (contractAddress === '0x') { return; } @@ -472,14 +480,6 @@ class MethodDecoding extends Component { return; } - const { signature, paramdata } = api.util.decodeCallData(input); - this.setState({ methodSignature: signature, methodParams: paramdata }); - - if (!signature || signature === CONTRACT_CREATE || transaction.creates) { - this.setState({ isDeploy: true }); - return; - } - return Contracts.get() .signatureReg .lookup(signature) From 98bfbdc5cb20516da71ede43ae3b86dcb9895fe1 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Mon, 5 Dec 2016 17:50:43 +0000 Subject: [PATCH 55/69] [ci skip] js-precompiled 20161205-174841 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a6fa12b3a..660389a4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1270,7 +1270,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#7700411d2b0ba1372e0d6cf72a84ecf873a181f3" +source = "git+https://github.com/ethcore/js-precompiled.git#3cf6c68b7d08be71d12ff142e255a42043e50c75" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 5341cee31..42836bb7c 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.91", + "version": "0.2.92", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From ff7b918d82bf8c6bf8a51802794dc022b65cb8fa Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 5 Dec 2016 10:55:53 -0800 Subject: [PATCH 56/69] Fix build. --- ethcore/src/types/transaction.rs | 2 +- rpc/src/v1/types/transaction.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 2cc25a745..1c6ef92e3 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -305,7 +305,7 @@ impl SignedTransaction { pub fn standard_v(&self) -> u8 { match self.v { v if v == 27 || v == 28 || v > 36 => ((v - 1) % 2) as u8, _ => 4 } } /// The `v` value that appears in the RLP. - pub fn original_v(&self) -> u8 { self.v } + pub fn original_v(&self) -> u64 { self.v } /// The network ID, or `None` if this is a global transaction. pub fn network_id(&self) -> Option { diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 7f26adf41..933a4a482 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -59,7 +59,7 @@ pub struct Transaction { pub public_key: Option, /// The network id of the transaction, if any. #[serde(rename="networkId")] - pub network_id: Option, + pub network_id: Option, /// The standardised V field of the signature (0 or 1). #[serde(rename="standardV")] pub standard_v: U256, From bec35396512313a3793a337ca7320e1beb343b21 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Tue, 6 Dec 2016 09:37:59 +0100 Subject: [PATCH 57/69] First draft of the MultiSig Wallet (#3700) * Wallet Creation Modal #3282 * Name and description to Wallet #3282 * Add Wallet to the Account Page and Wallet Page #3282 * Fix Linting * Crete MobX store for Transfer modal * WIP Wallet Redux Store * Basic Details for Wallet #3282 * Fixing linting * Refactoring Transfer store for Wallet * Working wallet init transfer #3282 * Optional gas in MethodDecoding + better input * Show confirmations for Wallet #3282 * Order confirmations * Method Decoding selections * MultiSig txs and confirm pending #3282 * MultiSig Wallet Revoke #3282 * Confirmations and Txs Update #3282 * Feedback for Confirmations #3282 * Merging master fixes... * Remove unused CSS --- js/src/api/contract/contract.js | 4 +- js/src/contracts/code/index.js | 21 + js/src/contracts/code/wallet.js | 23 + js/src/contracts/snippets/wallet.sol | 388 ++++++++++++ js/src/main.js | 3 +- .../CreateAccount/NewAccount/newAccount.js | 5 +- .../CreateWallet/WalletDetails/index.js | 17 + .../WalletDetails/walletDetails.js | 111 ++++ .../modals/CreateWallet/WalletInfo/index.js | 17 + .../CreateWallet/WalletInfo/walletInfo.js | 85 +++ js/src/modals/CreateWallet/createWallet.css | 39 ++ js/src/modals/CreateWallet/createWallet.js | 182 ++++++ .../modals/CreateWallet/createWalletStore.js | 199 ++++++ js/src/modals/CreateWallet/index.js | 17 + js/src/modals/Transfer/Details/details.js | 49 +- js/src/modals/Transfer/store.js | 209 ++++--- js/src/modals/Transfer/transfer.js | 41 +- js/src/modals/index.js | 2 + js/src/redux/providers/index.js | 1 + js/src/redux/providers/personalActions.js | 36 +- js/src/redux/providers/personalReducer.js | 24 +- js/src/redux/providers/walletActions.js | 567 ++++++++++++++++++ js/src/redux/providers/walletReducer.js | 89 +++ js/src/redux/reducers.js | 5 +- js/src/redux/store.js | 4 + js/src/ui/Errors/errors.js | 3 +- js/src/ui/Form/AddressSelect/addressSelect.js | 5 +- js/src/ui/Form/Input/input.js | 2 +- js/src/ui/Form/InputAddress/inputAddress.css | 26 + js/src/ui/Form/InputAddress/inputAddress.js | 37 +- .../InputAddressSelect/inputAddressSelect.js | 9 +- js/src/ui/Form/TypedInput/typedInput.js | 17 +- js/src/ui/TxList/txList.js | 14 +- js/src/views/Account/Header/header.js | 4 + js/src/views/Accounts/accounts.js | 90 ++- js/src/views/Application/TabBar/tabBar.js | 1 + .../Wallet/Confirmations/confirmations.js | 372 ++++++++++++ js/src/views/Wallet/Confirmations/index.js | 17 + js/src/views/Wallet/Details/details.js | 102 ++++ js/src/views/Wallet/Details/index.js | 17 + js/src/views/Wallet/Transactions/index.js | 17 + .../views/Wallet/Transactions/transactions.js | 85 +++ js/src/views/Wallet/index.js | 17 + js/src/views/Wallet/wallet.css | 107 ++++ js/src/views/Wallet/wallet.js | 273 +++++++++ .../views/WriteContract/writeContractStore.js | 5 + js/src/views/index.js | 4 +- 47 files changed, 3202 insertions(+), 160 deletions(-) create mode 100644 js/src/contracts/code/index.js create mode 100644 js/src/contracts/code/wallet.js create mode 100644 js/src/contracts/snippets/wallet.sol create mode 100644 js/src/modals/CreateWallet/WalletDetails/index.js create mode 100644 js/src/modals/CreateWallet/WalletDetails/walletDetails.js create mode 100644 js/src/modals/CreateWallet/WalletInfo/index.js create mode 100644 js/src/modals/CreateWallet/WalletInfo/walletInfo.js create mode 100644 js/src/modals/CreateWallet/createWallet.css create mode 100644 js/src/modals/CreateWallet/createWallet.js create mode 100644 js/src/modals/CreateWallet/createWalletStore.js create mode 100644 js/src/modals/CreateWallet/index.js create mode 100644 js/src/redux/providers/walletActions.js create mode 100644 js/src/redux/providers/walletReducer.js create mode 100644 js/src/views/Wallet/Confirmations/confirmations.js create mode 100644 js/src/views/Wallet/Confirmations/index.js create mode 100644 js/src/views/Wallet/Details/details.js create mode 100644 js/src/views/Wallet/Details/index.js create mode 100644 js/src/views/Wallet/Transactions/index.js create mode 100644 js/src/views/Wallet/Transactions/transactions.js create mode 100644 js/src/views/Wallet/index.js create mode 100644 js/src/views/Wallet/wallet.css create mode 100644 js/src/views/Wallet/wallet.js diff --git a/js/src/api/contract/contract.js b/js/src/api/contract/contract.js index 28bad8a3b..d9ec9b03e 100644 --- a/js/src/api/contract/contract.js +++ b/js/src/api/contract/contract.js @@ -209,8 +209,10 @@ export default class Contract { _bindFunction = (func) => { func.call = (options, values = []) => { + const callData = this._encodeOptions(func, this._addOptionsTo(options), values); + return this._api.eth - .call(this._encodeOptions(func, this._addOptionsTo(options), values)) + .call(callData) .then((encoded) => func.decodeOutput(encoded)) .then((tokens) => tokens.map((token) => token.value)) .then((returns) => returns.length === 1 ? returns[0] : returns); diff --git a/js/src/contracts/code/index.js b/js/src/contracts/code/index.js new file mode 100644 index 000000000..baa144979 --- /dev/null +++ b/js/src/contracts/code/index.js @@ -0,0 +1,21 @@ +// Copyright 2015, 2016 Ethcore (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 . + +import wallet from './wallet'; + +export { + wallet +}; diff --git a/js/src/contracts/code/wallet.js b/js/src/contracts/code/wallet.js new file mode 100644 index 000000000..a4db4459b --- /dev/null +++ b/js/src/contracts/code/wallet.js @@ -0,0 +1,23 @@ +// Copyright 2015, 2016 Ethcore (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 . + +/** + * @version Solidity v0.4.6 + * @from https://github.com/ethereum/dapp-bin/blob/dd5c485359074d49f571693ae064ce78970f3d6d/wallet/wallet.sol + * @date 22-Nov-2016 @ 15h00 UTC + */ +export default '0x606060405234620000005760405162001a0638038062001a06833981016040528080518201919060200180519060200190919080519060200190919050505b805b83835b600060018351016001819055503373ffffffffffffffffffffffffffffffffffffffff166002600161010081101562000000570160005b5081905550600161010260003373ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600090505b825181101562000158578281815181101562000000579060200190602002015173ffffffffffffffffffffffffffffffffffffffff1660028260020161010081101562000000570160005b50819055508060020161010260008584815181101562000000579060200190602002015173ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b806001019050620000b4565b816000819055505b5050508061010581905550620001896200019c6401000000000262001832176401000000009004565b610107819055505b505b505050620001b1565b60006201518042811562000000570490505b90565b61184680620001c06000396000f3606060405236156100f4576000357c010000000000000000000000000000000000000000000000000000000090048063173825d91461015c5780632f54bf6e146101795780634123cb6b146101ac57806352375093146101cf5780635c52c2f5146101f2578063659010e7146102015780637065cb4814610224578063746c917114610241578063797af62714610264578063b20d30a914610297578063b61d27f6146102b4578063b75c7dc614610306578063ba51a6df14610323578063c2cf732614610340578063c41a360a1461037c578063cbf0b0c0146103c3578063f00d4b5d146103e0578063f1736d8614610406575b61015a5b6000341115610157577fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c3334604051808373ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15b5b565b005b34610000576101776004808035906020019091905050610429565b005b34610000576101946004808035906020019091905050610553565b60405180821515815260200191505060405180910390f35b34610000576101b961058b565b6040518082815260200191505060405180910390f35b34610000576101dc610591565b6040518082815260200191505060405180910390f35b34610000576101ff610598565b005b346100005761020e6105d3565b6040518082815260200191505060405180910390f35b346100005761023f60048080359060200190919050506105da565b005b346100005761024e61070d565b6040518082815260200191505060405180910390f35b346100005761027f6004808035906020019091905050610713565b60405180821515815260200191505060405180910390f35b34610000576102b26004808035906020019091905050610abf565b005b34610000576102ec60048080359060200190919080359060200190919080359060200190820180359060200191909192905050610afa565b604051808260001916815260200191505060405180910390f35b34610000576103216004808035906020019091905050610e68565b005b346100005761033e6004808035906020019091905050610f5f565b005b34610000576103646004808035906020019091908035906020019091905050610fe7565b60405180821515815260200191505060405180910390f35b34610000576103976004808035906020019091905050611065565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576103de6004808035906020019091905050611085565b005b346100005761040460048080359060200190919080359060200190919050506110d0565b005b3461000057610413611254565b6040518082815260200191505060405180910390f35b60006000366040518083838082843782019150509250505060405180910390206104528161125b565b1561054d5761010260008473ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054915060008214156104925761054c565b60016001540360005411156104a65761054c565b6000600283610100811015610000570160005b5081905550600061010260008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506104f661147f565b6104fe61157a565b7f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da83604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15b5b5b505050565b6000600061010260008473ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541190505b919050565b60015481565b6101075481565b6000366040518083838082843782019150509250505060405180910390206105bf8161125b565b156105cf576000610106819055505b5b5b50565b6101065481565b6000366040518083838082843782019150509250505060405180910390206106018161125b565b156107085761060f82610553565b1561061957610707565b61062161147f565b60fa6001541015156106365761063561157a565b5b60fa60015410151561064757610707565b6001600081548092919060010191905055508173ffffffffffffffffffffffffffffffffffffffff166002600154610100811015610000570160005b508190555060015461010260008473ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c382604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15b5b5b5050565b60005481565b60008161071f8161125b565b15610ab857600061010860008560001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610ab65761010860008460001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1661010860008560001916815260200190815260200160002060010154610108600086600019168152602001908152602001600020600201604051808280546001816001161561010002031660029004801561086d5780601f106108425761010080835404028352916020019161086d565b820191906000526020600020905b81548152906001019060200180831161085057829003601f168201915b505091505060006040518083038185876185025a03f192505050507fe7c957c06e9a662c1a6c77366179f5b702b97651dc28eee7d5bf1dff6e40bb4a33846101086000876000191681526020019081526020016000206001015461010860008860001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610108600089600019168152602001908152602001600020600201604051808673ffffffffffffffffffffffffffffffffffffffff168152602001856000191681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff168152602001806020018281038252838181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156109ef5780601f106109c4576101008083540402835291602001916109ef565b820191906000526020600020905b8154815290600101906020018083116109d257829003601f168201915b5050965050505050505060405180910390a161010860008460001916815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600182016000905560028201805460018160011615610100020316600290046000825580601f10610a735750610aaa565b601f016020900490600052602060002090810190610aa991905b80821115610aa5576000816000905550600101610a8d565b5090565b5b50505060019150610ab7565b5b5b5b50919050565b600036604051808383808284378201915050925050506040518091039020610ae68161125b565b15610af55781610105819055505b5b5b5050565b6000610b0533610553565b15610e5f57610b13846116c9565b15610bfc577f92ca3a80853e6663fa31fa10b99225f18d4902939b4c53a9caae9043f6efd0043385878686604051808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff1681526020018060200182810382528484828181526020019250808284378201915050965050505050505060405180910390a18473ffffffffffffffffffffffffffffffffffffffff168484846040518083838082843782019150509250505060006040518083038185876185025a03f1925050505060006001029050610e5e565b60003643604051808484808284378201915050828152602001935050505060405180910390209050610c2d81610713565b158015610c8b5750600061010860008360001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b15610e5d578461010860008360001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690836c01000000000000000000000000908102040217905550836101086000836000191681526020019081526020016000206001018190555082826101086000846000191681526020019081526020016000206002019190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610d6657803560ff1916838001178555610d94565b82800160010185558215610d94579182015b82811115610d93578235825591602001919060010190610d78565b5b509050610db991905b80821115610db5576000816000905550600101610d9d565b5090565b50507f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf3281338688878760405180876000191681526020018673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff168152602001806020018281038252848482818152602001925080828437820191505097505050505050505060405180910390a15b5b5b5b949350505050565b60006000600061010260003373ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205492506000831415610ea957610f59565b8260020a915061010360008560001916815260200190815260200160002090506000828260010154161115610f585780600001600081548092919060010191905055508181600101600082825403925050819055507fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b3385604051808373ffffffffffffffffffffffffffffffffffffffff168152602001826000191681526020019250505060405180910390a15b5b50505050565b600036604051808383808284378201915050925050506040518091039020610f868161125b565b15610fe257600154821115610f9a57610fe1565b81600081905550610fa961147f565b7facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da826040518082815260200191505060405180910390a15b5b5b5050565b6000600060006000610103600087600019168152602001908152602001600020925061010260008673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205491506000821415611048576000935061105c565b8160020a9050600081846001015416141593505b50505092915050565b6000600260018301610100811015610000570160005b505490505b919050565b6000366040518083838082843782019150509250505060405180910390206110ac8161125b565b156110cb578173ffffffffffffffffffffffffffffffffffffffff16ff5b5b5b5050565b60006000366040518083838082843782019150509250505060405180910390206110f98161125b565b1561124d5761110783610553565b156111115761124c565b61010260008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549150600082141561114c5761124c565b61115461147f565b8273ffffffffffffffffffffffffffffffffffffffff16600283610100811015610000570160005b5081905550600061010260008673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508161010260008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c8484604051808373ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a15b5b5b50505050565b6101055481565b600060006000600061010260003373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549250600083141561129e57611477565b61010360008660001916815260200190815260200160002091506000826000015414156113555760005482600001819055506000826001018190555061010480548091906001018154818355818115116113245781836000526020600020918201910161132391905b8082111561131f576000816000905550600101611307565b5090565b5b5050508260020181905550846101048360020154815481101561000057906000526020600020900160005b50819055505b8260020a90506000818360010154161415611476577fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda3386604051808373ffffffffffffffffffffffffffffffffffffffff168152602001826000191681526020019250505060405180910390a16001826000015411151561144d5761010461010360008760001916815260200190815260200160002060020154815481101561000057906000526020600020900160005b5060009055610103600086600019168152602001908152602001600020600060008201600090556001820160009055600282016000905550506001935061147756611475565b8160000160008154809291906001900391905055508082600101600082825417925050819055505b5b5b505050919050565b60006000610104805490509150600090505b8181101561156d57610108600061010483815481101561000057906000526020600020900160005b505460001916815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600182016000905560028201805460018160011615610100020316600290046000825580601f10611527575061155e565b601f01602090049060005260206000209081019061155d91905b80821115611559576000816000905550600101611541565b5090565b5b5050505b806001019050611491565b61157561174f565b5b5050565b6000600190505b6001548110156116c5575b600154811080156115b057506000600282610100811015610000570160005b505414155b156115c257808060010191505061158c565b5b60016001541180156115e9575060006002600154610100811015610000570160005b5054145b1561160657600160008154809291906001900391905055506115c3565b6001548110801561162c575060006002600154610100811015610000570160005b505414155b801561164a57506000600282610100811015610000570160005b5054145b156116c0576002600154610100811015610000570160005b5054600282610100811015610000570160005b5081905550806101026000600284610100811015610000570160005b505481526020019081526020016000208190555060006002600154610100811015610000570160005b50819055505b611581565b5b50565b60006116d433610553565b1561174957610107546116e5611832565b1115611704576000610106819055506116fc611832565b610107819055505b610106548261010654011015801561172457506101055482610106540111155b1561174357816101066000828254019250508190555060019050611748565b600090505b5b5b919050565b60006000610104805490509150600090505b818110156117f357600060010261010482815481101561000057906000526020600020900160005b5054600019161415156117e757610103600061010483815481101561000057906000526020600020900160005b5054600019168152602001908152602001600020600060008201600090556001820160009055600282016000905550505b5b806001019050611761565b6101048054600082559060005260206000209081019061182b91905b8082111561182757600081600090555060010161180f565b5090565b5b505b5050565b600062015180428115610000570490505b9056'; + diff --git a/js/src/contracts/snippets/wallet.sol b/js/src/contracts/snippets/wallet.sol new file mode 100644 index 000000000..b369eea76 --- /dev/null +++ b/js/src/contracts/snippets/wallet.sol @@ -0,0 +1,388 @@ +//sol Wallet +// Multi-sig, daily-limited account proxy/wallet. +// @authors: +// Gav Wood +// inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a +// single, or, crucially, each of a number of, designated owners. +// usage: +// use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by +// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the +// interior is executed. +pragma solidity ^0.4.6; + +contract multiowned { + + // TYPES + + // struct for the status of a pending operation. + struct PendingState { + uint yetNeeded; + uint ownersDone; + uint index; + } + + // EVENTS + + // this contract only has six types of events: it can accept a confirmation, in which case + // we record owner and operation (hash) alongside it. + event Confirmation(address owner, bytes32 operation); + event Revoke(address owner, bytes32 operation); + // some others are in the case of an owner changing. + event OwnerChanged(address oldOwner, address newOwner); + event OwnerAdded(address newOwner); + event OwnerRemoved(address oldOwner); + // the last one is emitted if the required signatures change + event RequirementChanged(uint newRequirement); + + // MODIFIERS + + // simple single-sig function modifier. + modifier onlyowner { + if (isOwner(msg.sender)) + _; + } + // multi-sig function modifier: the operation must have an intrinsic hash in order + // that later attempts can be realised as the same underlying operation and + // thus count as confirmations. + modifier onlymanyowners(bytes32 _operation) { + if (confirmAndCheck(_operation)) + _; + } + + // METHODS + + // constructor is given number of sigs required to do protected "onlymanyowners" transactions + // as well as the selection of addresses capable of confirming them. + function multiowned(address[] _owners, uint _required) { + m_numOwners = _owners.length + 1; + m_owners[1] = uint(msg.sender); + m_ownerIndex[uint(msg.sender)] = 1; + for (uint i = 0; i < _owners.length; ++i) + { + m_owners[2 + i] = uint(_owners[i]); + m_ownerIndex[uint(_owners[i])] = 2 + i; + } + m_required = _required; + } + + // Revokes a prior confirmation of the given operation + function revoke(bytes32 _operation) external { + uint ownerIndex = m_ownerIndex[uint(msg.sender)]; + // make sure they're an owner + if (ownerIndex == 0) return; + uint ownerIndexBit = 2**ownerIndex; + var pending = m_pending[_operation]; + if (pending.ownersDone & ownerIndexBit > 0) { + pending.yetNeeded++; + pending.ownersDone -= ownerIndexBit; + Revoke(msg.sender, _operation); + } + } + + // Replaces an owner `_from` with another `_to`. + function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external { + if (isOwner(_to)) return; + uint ownerIndex = m_ownerIndex[uint(_from)]; + if (ownerIndex == 0) return; + + clearPending(); + m_owners[ownerIndex] = uint(_to); + m_ownerIndex[uint(_from)] = 0; + m_ownerIndex[uint(_to)] = ownerIndex; + OwnerChanged(_from, _to); + } + + function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external { + if (isOwner(_owner)) return; + + clearPending(); + if (m_numOwners >= c_maxOwners) + reorganizeOwners(); + if (m_numOwners >= c_maxOwners) + return; + m_numOwners++; + m_owners[m_numOwners] = uint(_owner); + m_ownerIndex[uint(_owner)] = m_numOwners; + OwnerAdded(_owner); + } + + function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external { + uint ownerIndex = m_ownerIndex[uint(_owner)]; + if (ownerIndex == 0) return; + if (m_required > m_numOwners - 1) return; + + m_owners[ownerIndex] = 0; + m_ownerIndex[uint(_owner)] = 0; + clearPending(); + reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot + OwnerRemoved(_owner); + } + + function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external { + if (_newRequired > m_numOwners) return; + m_required = _newRequired; + clearPending(); + RequirementChanged(_newRequired); + } + + // Gets an owner by 0-indexed position (using numOwners as the count) + function getOwner(uint ownerIndex) external constant returns (address) { + return address(m_owners[ownerIndex + 1]); + } + + function isOwner(address _addr) returns (bool) { + return m_ownerIndex[uint(_addr)] > 0; + } + + function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) { + var pending = m_pending[_operation]; + uint ownerIndex = m_ownerIndex[uint(_owner)]; + + // make sure they're an owner + if (ownerIndex == 0) return false; + + // determine the bit to set for this owner. + uint ownerIndexBit = 2**ownerIndex; + return !(pending.ownersDone & ownerIndexBit == 0); + } + + // INTERNAL METHODS + + function confirmAndCheck(bytes32 _operation) internal returns (bool) { + // determine what index the present sender is: + uint ownerIndex = m_ownerIndex[uint(msg.sender)]; + // make sure they're an owner + if (ownerIndex == 0) return; + + var pending = m_pending[_operation]; + // if we're not yet working on this operation, switch over and reset the confirmation status. + if (pending.yetNeeded == 0) { + // reset count of confirmations needed. + pending.yetNeeded = m_required; + // reset which owners have confirmed (none) - set our bitmap to 0. + pending.ownersDone = 0; + pending.index = m_pendingIndex.length++; + m_pendingIndex[pending.index] = _operation; + } + // determine the bit to set for this owner. + uint ownerIndexBit = 2**ownerIndex; + // make sure we (the message sender) haven't confirmed this operation previously. + if (pending.ownersDone & ownerIndexBit == 0) { + Confirmation(msg.sender, _operation); + // ok - check if count is enough to go ahead. + if (pending.yetNeeded <= 1) { + // enough confirmations: reset and run interior. + delete m_pendingIndex[m_pending[_operation].index]; + delete m_pending[_operation]; + return true; + } + else + { + // not enough: record that this owner in particular confirmed. + pending.yetNeeded--; + pending.ownersDone |= ownerIndexBit; + } + } + } + + function reorganizeOwners() private { + uint free = 1; + while (free < m_numOwners) + { + while (free < m_numOwners && m_owners[free] != 0) free++; + while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--; + if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0) + { + m_owners[free] = m_owners[m_numOwners]; + m_ownerIndex[m_owners[free]] = free; + m_owners[m_numOwners] = 0; + } + } + } + + function clearPending() internal { + uint length = m_pendingIndex.length; + for (uint i = 0; i < length; ++i) + if (m_pendingIndex[i] != 0) + delete m_pending[m_pendingIndex[i]]; + delete m_pendingIndex; + } + + // FIELDS + + // the number of owners that must confirm the same operation before it is run. + uint public m_required; + // pointer used to find a free slot in m_owners + uint public m_numOwners; + + // list of owners + uint[256] m_owners; + uint constant c_maxOwners = 250; + // index on the list of owners to allow reverse lookup + mapping(uint => uint) m_ownerIndex; + // the ongoing operations. + mapping(bytes32 => PendingState) m_pending; + bytes32[] m_pendingIndex; +} + +// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable) +// on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method +// uses is specified in the modifier. +contract daylimit is multiowned { + + // MODIFIERS + + // simple modifier for daily limit. + modifier limitedDaily(uint _value) { + if (underLimit(_value)) + _; + } + + // METHODS + + // constructor - stores initial daily limit and records the present day's index. + function daylimit(uint _limit) { + m_dailyLimit = _limit; + m_lastDay = today(); + } + // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. + function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { + m_dailyLimit = _newLimit; + } + // resets the amount already spent today. needs many of the owners to confirm. + function resetSpentToday() onlymanyowners(sha3(msg.data)) external { + m_spentToday = 0; + } + + // INTERNAL METHODS + + // checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and + // returns true. otherwise just returns false. + function underLimit(uint _value) internal onlyowner returns (bool) { + // reset the spend limit if we're on a different day to last time. + if (today() > m_lastDay) { + m_spentToday = 0; + m_lastDay = today(); + } + // check to see if there's enough left - if so, subtract and return true. + // overflow protection // dailyLimit check + if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) { + m_spentToday += _value; + return true; + } + return false; + } + // determines today's index. + function today() private constant returns (uint) { return now / 1 days; } + + // FIELDS + + uint public m_dailyLimit; + uint public m_spentToday; + uint public m_lastDay; +} + +// interface contract for multisig proxy contracts; see below for docs. +contract multisig { + + // EVENTS + + // logged events: + // Funds has arrived into the wallet (record how much). + event Deposit(address _from, uint value); + // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going). + event SingleTransact(address owner, uint value, address to, bytes data); + // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going). + event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data); + // Confirmation still needed for a transaction. + event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data); + + // FUNCTIONS + + // TODO: document + function changeOwner(address _from, address _to) external; + function execute(address _to, uint _value, bytes _data) external returns (bytes32); + function confirm(bytes32 _h) returns (bool); +} + +// usage: +// bytes32 h = Wallet(w).from(oneOwner).execute(to, value, data); +// Wallet(w).from(anotherOwner).confirm(h); +contract Wallet is multisig, multiowned, daylimit { + + // TYPES + + // Transaction structure to remember details of transaction lest it need be saved for a later call. + struct Transaction { + address to; + uint value; + bytes data; + } + + // METHODS + + // constructor - just pass on the owner array to the multiowned and + // the limit to daylimit + function Wallet(address[] _owners, uint _required, uint _daylimit) + multiowned(_owners, _required) daylimit(_daylimit) { + } + + // kills the contract sending everything to `_to`. + function kill(address _to) onlymanyowners(sha3(msg.data)) external { + suicide(_to); + } + + // gets called when no other function matches + function() payable { + // just being sent some cash? + if (msg.value > 0) + Deposit(msg.sender, msg.value); + } + + // Outside-visible transact entry point. Executes transaction immediately if below daily spend limit. + // If not, goes into multisig process. We provide a hash on return to allow the sender to provide + // shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value + // and _data arguments). They still get the option of using them if they want, anyways. + function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 _r) { + // first, take the opportunity to check that we're under the daily limit. + if (underLimit(_value)) { + SingleTransact(msg.sender, _value, _to, _data); + // yes - just execute the call. + _to.call.value(_value)(_data); + return 0; + } + // determine our operation hash. + _r = sha3(msg.data, block.number); + if (!confirm(_r) && m_txs[_r].to == 0) { + m_txs[_r].to = _to; + m_txs[_r].value = _value; + m_txs[_r].data = _data; + ConfirmationNeeded(_r, msg.sender, _value, _to, _data); + } + } + + // confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order + // to determine the body of the transaction from the hash provided. + function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) { + if (m_txs[_h].to != 0) { + m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data); + MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data); + delete m_txs[_h]; + return true; + } + } + + // INTERNAL METHODS + + function clearPending() internal { + uint length = m_pendingIndex.length; + for (uint i = 0; i < length; ++i) + delete m_txs[m_pendingIndex[i]]; + super.clearPending(); + } + + // FIELDS + + // pending transactions we have at present. + mapping (bytes32 => Transaction) m_txs; +} diff --git a/js/src/main.js b/js/src/main.js index ae785cbb6..d508c50fc 100644 --- a/js/src/main.js +++ b/js/src/main.js @@ -17,7 +17,7 @@ import React, { Component, PropTypes } from 'react'; import { Redirect, Router, Route } from 'react-router'; -import { Accounts, Account, Addresses, Address, Application, Contract, Contracts, WriteContract, Dapp, Dapps, Settings, SettingsBackground, SettingsParity, SettingsProxy, SettingsViews, Signer, Status } from '~/views'; +import { Accounts, Account, Addresses, Address, Application, Contract, Contracts, WriteContract, Wallet, Dapp, Dapps, Settings, SettingsBackground, SettingsParity, SettingsProxy, SettingsViews, Signer, Status } from '~/views'; import styles from './reset.css'; @@ -37,6 +37,7 @@ export default class MainApplication extends Component { + diff --git a/js/src/modals/CreateAccount/NewAccount/newAccount.js b/js/src/modals/CreateAccount/NewAccount/newAccount.js index a100ab19a..bbbf6fed3 100644 --- a/js/src/modals/CreateAccount/NewAccount/newAccount.js +++ b/js/src/modals/CreateAccount/NewAccount/newAccount.js @@ -192,8 +192,6 @@ export default class CreateAccount extends Component { }; }); - console.log(accounts); - this.setState({ selectedAddress: addresses[0], accounts: accounts @@ -201,8 +199,7 @@ export default class CreateAccount extends Component { }); }) .catch((error) => { - console.log('createIdentities', error); - + console.error('createIdentities', error); setTimeout(this.createIdentities, 1000); this.newError(error); }); diff --git a/js/src/modals/CreateWallet/WalletDetails/index.js b/js/src/modals/CreateWallet/WalletDetails/index.js new file mode 100644 index 000000000..266385511 --- /dev/null +++ b/js/src/modals/CreateWallet/WalletDetails/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (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 . + +export default from './walletDetails'; diff --git a/js/src/modals/CreateWallet/WalletDetails/walletDetails.js b/js/src/modals/CreateWallet/WalletDetails/walletDetails.js new file mode 100644 index 000000000..9126fdb72 --- /dev/null +++ b/js/src/modals/CreateWallet/WalletDetails/walletDetails.js @@ -0,0 +1,111 @@ +// Copyright 2015, 2016 Ethcore (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 . + +import React, { Component, PropTypes } from 'react'; + +import { Form, TypedInput, Input, AddressSelect } from '../../../ui'; +import { parseAbiType } from '../../../util/abi'; + +export default class WalletDetails extends Component { + static propTypes = { + accounts: PropTypes.object.isRequired, + wallet: PropTypes.object.isRequired, + errors: PropTypes.object.isRequired, + onChange: PropTypes.func.isRequired + }; + + render () { + const { accounts, wallet, errors } = this.props; + + return ( +
+ + + + + + + + + + + + + ); + } + + onAccoutChange = (_, account) => { + this.props.onChange({ account }); + } + + onNameChange = (_, name) => { + this.props.onChange({ name }); + } + + onDescriptionChange = (_, description) => { + this.props.onChange({ description }); + } + + onOwnersChange = (owners) => { + this.props.onChange({ owners }); + } + + onRequiredChange = (required) => { + this.props.onChange({ required }); + } + + onDaylimitChange = (daylimit) => { + this.props.onChange({ daylimit }); + } +} diff --git a/js/src/modals/CreateWallet/WalletInfo/index.js b/js/src/modals/CreateWallet/WalletInfo/index.js new file mode 100644 index 000000000..975e7bd59 --- /dev/null +++ b/js/src/modals/CreateWallet/WalletInfo/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (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 . + +export default from './walletInfo'; diff --git a/js/src/modals/CreateWallet/WalletInfo/walletInfo.js b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js new file mode 100644 index 000000000..301263314 --- /dev/null +++ b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js @@ -0,0 +1,85 @@ +// Copyright 2015, 2016 Ethcore (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 . + +import React, { Component, PropTypes } from 'react'; + +import { CompletedStep, IdentityIcon, CopyToClipboard } from '../../../ui'; + +import styles from '../createWallet.css'; + +export default class WalletInfo extends Component { + static propTypes = { + accounts: PropTypes.object.isRequired, + account: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + address: PropTypes.string.isRequired, + owners: PropTypes.array.isRequired, + required: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number + ]).isRequired, + daylimit: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number + ]).isRequired + }; + + render () { + const { address, required, daylimit, name } = this.props; + + return ( + +
{ name } has been deployed at
+
+ + +
{ address }
+
+
with the following owners
+
+ { this.renderOwners() } +
+

+ { required } owners are required to confirm a transaction. +

+

+ The daily limit is set to { daylimit }. +

+
+ ); + } + + renderOwners () { + const { account, owners } = this.props; + + return [].concat(account, owners).map((address, id) => ( +
+ +
{ this.addressToString(address) }
+
+ )); + } + + addressToString (address) { + const { accounts } = this.props; + + if (accounts[address]) { + return accounts[address].name || address; + } + + return address; + } +} diff --git a/js/src/modals/CreateWallet/createWallet.css b/js/src/modals/CreateWallet/createWallet.css new file mode 100644 index 000000000..a450466f0 --- /dev/null +++ b/js/src/modals/CreateWallet/createWallet.css @@ -0,0 +1,39 @@ +/* Copyright 2015, 2016 Ethcore (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 . +*/ + +.address { + vertical-align: top; + display: inline-block; +} + +.identityicon { + margin: -8px 0.5em; +} + +.owner { + height: 40px; + color: lightgrey; + + display: flex; + align-items: center; + justify-content: center; + + .identityicon { + width: 24px; + height: 24px; + } +} diff --git a/js/src/modals/CreateWallet/createWallet.js b/js/src/modals/CreateWallet/createWallet.js new file mode 100644 index 000000000..8f38fec12 --- /dev/null +++ b/js/src/modals/CreateWallet/createWallet.js @@ -0,0 +1,182 @@ +// Copyright 2015, 2016 Ethcore (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 . + +import React, { Component, PropTypes } from 'react'; +import { observer } from 'mobx-react'; + +import ActionDone from 'material-ui/svg-icons/action/done'; +import ContentClear from 'material-ui/svg-icons/content/clear'; +import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward'; + +import { Button, Modal, TxHash, BusyStep } from '../../ui'; + +import WalletDetails from './WalletDetails'; +import WalletInfo from './WalletInfo'; +import CreateWalletStore from './createWalletStore'; +// import styles from './createWallet.css'; + +@observer +export default class CreateWallet extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + }; + + static propTypes = { + accounts: PropTypes.object.isRequired, + onClose: PropTypes.func.isRequired + }; + + store = new CreateWalletStore(this.context.api, this.props.accounts); + + render () { + const { stage, steps, waiting, rejected } = this.store; + + if (rejected) { + return ( + + + + ); + } + + return ( + + { this.renderPage() } + + ); + } + + renderPage () { + const { step } = this.store; + const { accounts } = this.props; + + switch (step) { + case 'DEPLOYMENT': + return ( + + { this.store.txhash ? () : null } + + ); + + case 'INFO': + return ( + + ); + + default: + case 'DETAILS': + return ( + + ); + } + } + + renderDialogActions () { + const { step, hasErrors, rejected, onCreate } = this.store; + + const cancelBtn = ( +