From e3c469527489dc0ad2d8bf6afe80d2dad481daa0 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 19 Sep 2016 11:29:50 +0200 Subject: [PATCH 001/155] 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 002/155] 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 003/155] 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 004/155] 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 005/155] 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 006/155] 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 007/155] 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 008/155] 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 009/155] 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 010/155] 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 011/155] 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 012/155] 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 013/155] 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 014/155] 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 015/155] 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 016/155] 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 017/155] 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 018/155] 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 019/155] 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 020/155] 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 021/155] 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 022/155] 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 023/155] 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 024/155] 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 025/155] 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 026/155] 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 25b0b8641ecaf627fdf9dcd677294c0214fa2fb9 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 14 Nov 2016 17:47:56 +0100 Subject: [PATCH 027/155] indent state tests --- ethcore/src/state/mod.rs | 2102 +++++++++++++++++++------------------- 1 file changed, 1051 insertions(+), 1051 deletions(-) diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 01a7e3b15..23a9cf0d7 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -783,1107 +783,1107 @@ impl Clone for State { #[cfg(test)] mod tests { -use std::sync::Arc; -use std::str::FromStr; -use rustc_serialize::hex::FromHex; -use super::*; -use util::{U256, H256, FixedHash, Address, Hashable}; -use tests::helpers::*; -use devtools::*; -use env_info::EnvInfo; -use spec::*; -use transaction::*; -use util::log::init_log; -use trace::{FlatTrace, TraceError, trace}; -use types::executed::CallType; + use std::sync::Arc; + use std::str::FromStr; + use rustc_serialize::hex::FromHex; + use super::*; + use util::{U256, H256, FixedHash, Address, Hashable}; + use tests::helpers::*; + use devtools::*; + use env_info::EnvInfo; + use spec::*; + use transaction::*; + use util::log::init_log; + use trace::{FlatTrace, TraceError, trace}; + use types::executed::CallType; -#[test] -fn should_apply_create_transaction() { - init_log(); + #[test] + fn should_apply_create_transaction() { + init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), - }.sign(&"".sha3(), None); - - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Create(trace::Create { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - value: 100.into(), - gas: 77412.into(), - init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], - }), - result: trace::Res::Create(trace::CreateResult { - gas_used: U256::from(3224), - address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), - code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_work_when_cloned() { - init_log(); - - let a = Address::zero(); - - let temp = RandomTempPath::new(); - let mut state = { + let temp = RandomTempPath::new(); let mut state = get_temp_state_in(temp.as_path()); - assert_eq!(state.exists(&a), false); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), + }.sign(&"".sha3(), None); + + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Create(trace::Create { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + value: 100.into(), + gas: 77412.into(), + init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], + }), + result: trace::Res::Create(trace::CreateResult { + gas_used: U256::from(3224), + address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), + code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_work_when_cloned() { + init_log(); + + let a = Address::zero(); + + let temp = RandomTempPath::new(); + let mut state = { + let mut state = get_temp_state_in(temp.as_path()); + assert_eq!(state.exists(&a), false); + state.inc_nonce(&a); + state.commit().unwrap(); + state.clone() + }; + state.inc_nonce(&a); state.commit().unwrap(); - state.clone() - }; + } - state.inc_nonce(&a); - state.commit().unwrap(); -} + #[test] + fn should_trace_failed_create_transaction() { + init_log(); -#[test] -fn should_trace_failed_create_transaction() { - init_log(); + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("5b600056").unwrap(), - }.sign(&"".sha3(), None); - - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Create(trace::Create { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, value: 100.into(), - gas: 78792.into(), - init: vec![91, 96, 0, 86], - }), - result: trace::Res::FailedCreate(TraceError::OutOfGas), - subtraces: 0 - }]; + data: FromHex::from_hex("5b600056").unwrap(), + }.sign(&"".sha3(), None); - assert_eq!(result.trace, expected_trace); -} + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Create(trace::Create { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + value: 100.into(), + gas: 78792.into(), + init: vec![91, 96, 0, 86], + }), + result: trace::Res::FailedCreate(TraceError::OutOfGas), + subtraces: 0 + }]; -#[test] -fn should_trace_call_transaction() { - init_log(); + assert_eq!(result.trace, expected_trace); + } - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + #[test] + fn should_trace_call_transaction() { + init_log(); - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3(), None); + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); - state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_basic_call_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(0), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_call_transaction_to_builtin() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = &*Spec::new_test().engine; - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0x1.into()), - value: 0.into(), - data: vec![], - }.sign(&"".sha3(), None); - - let result = state.apply(&info, engine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: "0000000000000000000000000000000000000001".into(), - value: 0.into(), - gas: 79_000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3000), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_not_trace_subcall_transaction_to_builtin() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = &*Spec::new_test().engine; - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); - let result = state.apply(&info, engine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(28_061), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_not_trace_callcode() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = &*Spec::new_test().engine; - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - let result = state.apply(&info, engine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 64.into(), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xa.into(), - value: 0.into(), - gas: 4096.into(), - input: vec![], - call_type: CallType::CallCode, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![], - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_not_trace_delegatecall() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - info.number = 0x789b0; - let engine = &*Spec::new_test().engine; - - println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - let result = state.apply(&info, engine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(61), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 32768.into(), - input: vec![], - call_type: CallType::DelegateCall, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![], - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_failed_call_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_call_with_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_call_with_basic_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 69.into(), - gas: 2300.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult::default()), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_not_trace_call_with_invalid_basic_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_failed_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_call_with_subcall_with_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); - state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(135), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); -} - -#[test] -fn should_trace_failed_subcall_with_subcall_transaction() { - init_log(); - - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); - state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); - state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); - state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }) - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - call_type: CallType::Call, - input: vec![], - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + subtraces: 0, + }]; - assert_eq!(result.trace, expected_trace); -} + assert_eq!(result.trace, expected_trace); + } -#[test] -fn should_trace_suicide() { - init_log(); + #[test] + fn should_trace_basic_call_transaction() { + init_log(); - let temp = RandomTempPath::new(); - let mut state = get_temp_state_in(temp.as_path()); + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let engine = TestEngine::new(5); + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&"".sha3(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); - state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty); - state.add_balance(t.sender().as_ref().unwrap(), &100.into(), CleanupMode::NoEmpty); - let result = state.apply(&info, &engine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Suicide(trace::Suicide { - address: 0xa.into(), - refund_address: 0xb.into(), - balance: 150.into(), - }), - result: trace::Res::None, - }]; + data: vec![], + }.sign(&"".sha3(), None); - assert_eq!(result.trace, expected_trace); -} + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(0), + output: vec![] + }), + subtraces: 0, + }]; -#[test] -fn code_from_database() { - let a = Address::zero(); - let temp = RandomTempPath::new(); - let (root, db) = { + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_transaction_to_builtin() { + init_log(); + + let temp = RandomTempPath::new(); let mut state = get_temp_state_in(temp.as_path()); - state.require_or_from(&a, false, ||Account::new_contract(42.into(), 0.into()), |_|{}); - state.init_code(&a, vec![1, 2, 3]); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = &*Spec::new_test().engine; + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0x1.into()), + value: 0.into(), + data: vec![], + }.sign(&"".sha3(), None); + + let result = state.apply(&info, engine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: "0000000000000000000000000000000000000001".into(), + value: 0.into(), + gas: 79_000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3000), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_subcall_transaction_to_builtin() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = &*Spec::new_test().engine; + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); + let result = state.apply(&info, engine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(28_061), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_callcode() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = &*Spec::new_test().engine; + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); + let result = state.apply(&info, engine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 64.into(), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xa.into(), + value: 0.into(), + gas: 4096.into(), + input: vec![], + call_type: CallType::CallCode, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_delegatecall() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + info.number = 0x789b0; + let engine = &*Spec::new_test().engine; + + println!("schedule.have_delegate_call: {:?}", engine.schedule(&info).have_delegate_call); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 0.into(), + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); + let result = state.apply(&info, engine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(61), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 0.into(), + gas: 32768.into(), + input: vec![], + call_type: CallType::DelegateCall, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_call_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_basic_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 69.into(), + gas: 2300.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult::default()), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_call_with_invalid_basic_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![],//600480600b6000396000f35b600056 + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_with_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); + state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(135), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xb.into(), + to: 0xc.into(), + value: 0.into(), + gas: 78868.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_with_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![],//600480600b6000396000f35b600056 + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); + state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); + state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &(100.into()), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![] + }) + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: 0xa.into(), + to: 0xb.into(), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: 0xb.into(), + to: 0xc.into(), + value: 0.into(), + gas: 78868.into(), + call_type: CallType::Call, + input: vec![], + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_suicide() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let engine = TestEngine::new(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(0xa.into()), + value: 100.into(), + data: vec![], + }.sign(&"".sha3(), None); + + state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); + state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty); + state.add_balance(t.sender().as_ref().unwrap(), &100.into(), CleanupMode::NoEmpty); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), + to: 0xa.into(), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: CallType::Call, + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Suicide(trace::Suicide { + address: 0xa.into(), + refund_address: 0xb.into(), + balance: 150.into(), + }), + result: trace::Res::None, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn code_from_database() { + let a = Address::zero(); + let temp = RandomTempPath::new(); + let (root, db) = { + let mut state = get_temp_state_in(temp.as_path()); + state.require_or_from(&a, false, ||Account::new_contract(42.into(), 0.into()), |_|{}); + state.init_code(&a, vec![1, 2, 3]); + assert_eq!(state.code(&a), Some(Arc::new([1u8, 2, 3].to_vec()))); + state.commit().unwrap(); + assert_eq!(state.code(&a), Some(Arc::new([1u8, 2, 3].to_vec()))); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); assert_eq!(state.code(&a), Some(Arc::new([1u8, 2, 3].to_vec()))); - state.commit().unwrap(); - assert_eq!(state.code(&a), Some(Arc::new([1u8, 2, 3].to_vec()))); - state.drop() - }; + } - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.code(&a), Some(Arc::new([1u8, 2, 3].to_vec()))); -} + #[test] + fn storage_at_from_database() { + let a = Address::zero(); + let temp = RandomTempPath::new(); + let (root, db) = { + let mut state = get_temp_state_in(temp.as_path()); + state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(69u64))); + state.commit().unwrap(); + state.drop() + }; -#[test] -fn storage_at_from_database() { - let a = Address::zero(); - let temp = RandomTempPath::new(); - let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); - state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(69u64))); - state.commit().unwrap(); - state.drop() - }; + let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(s.storage_at(&a, &H256::from(&U256::from(1u64))), H256::from(&U256::from(69u64))); + } - let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(s.storage_at(&a, &H256::from(&U256::from(1u64))), H256::from(&U256::from(69u64))); -} + #[test] + fn get_from_database() { + let a = Address::zero(); + let temp = RandomTempPath::new(); + let (root, db) = { + let mut state = get_temp_state_in(temp.as_path()); + state.inc_nonce(&a); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.drop() + }; -#[test] -fn get_from_database() { - let a = Address::zero(); - let temp = RandomTempPath::new(); - let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); - state.inc_nonce(&a); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); - state.commit().unwrap(); + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); assert_eq!(state.balance(&a), U256::from(69u64)); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.balance(&a), U256::from(69u64)); - assert_eq!(state.nonce(&a), U256::from(1u64)); -} - -#[test] -fn remove() { - let a = Address::zero(); - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - assert_eq!(state.exists(&a), false); - assert_eq!(state.exists_and_not_null(&a), false); - state.inc_nonce(&a); - assert_eq!(state.exists(&a), true); - assert_eq!(state.exists_and_not_null(&a), true); - assert_eq!(state.nonce(&a), U256::from(1u64)); - state.kill_account(&a); - assert_eq!(state.exists(&a), false); - assert_eq!(state.exists_and_not_null(&a), false); - assert_eq!(state.nonce(&a), U256::from(0u64)); -} - -#[test] -fn empty_account_is_not_created() { - let a = Address::zero(); - let path = RandomTempPath::new(); - let db = get_temp_state_db_in(path.as_path()); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty); // create an empty account - state.commit().unwrap(); - state.drop() - }; - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert!(!state.exists(&a)); - assert!(!state.exists_and_not_null(&a)); -} - -#[test] -fn empty_account_exists_when_creation_forced() { - let a = Address::zero(); - let path = RandomTempPath::new(); - let db = get_temp_state_db_in(path.as_path()); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate); // create an empty account - state.commit().unwrap(); - state.drop() - }; - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert!(state.exists(&a)); - assert!(!state.exists_and_not_null(&a)); -} - -#[test] -fn remove_from_database() { - let a = Address::zero(); - let temp = RandomTempPath::new(); - let (root, db) = { - let mut state = get_temp_state_in(temp.as_path()); - state.inc_nonce(&a); - state.commit().unwrap(); - assert_eq!(state.exists(&a), true); assert_eq!(state.nonce(&a), U256::from(1u64)); - state.drop() - }; + } - let (root, db) = { - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + #[test] + fn remove() { + let a = Address::zero(); + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + assert_eq!(state.exists(&a), false); + assert_eq!(state.exists_and_not_null(&a), false); + state.inc_nonce(&a); assert_eq!(state.exists(&a), true); + assert_eq!(state.exists_and_not_null(&a), true); assert_eq!(state.nonce(&a), U256::from(1u64)); state.kill_account(&a); - state.commit().unwrap(); + assert_eq!(state.exists(&a), false); + assert_eq!(state.exists_and_not_null(&a), false); + assert_eq!(state.nonce(&a), U256::from(0u64)); + } + + #[test] + fn empty_account_is_not_created() { + let a = Address::zero(); + let path = RandomTempPath::new(); + let db = get_temp_state_db_in(path.as_path()); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty); // create an empty account + state.commit().unwrap(); + state.drop() + }; + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert!(!state.exists(&a)); + assert!(!state.exists_and_not_null(&a)); + } + + #[test] + fn empty_account_exists_when_creation_forced() { + let a = Address::zero(); + let path = RandomTempPath::new(); + let db = get_temp_state_db_in(path.as_path()); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate); // create an empty account + state.commit().unwrap(); + state.drop() + }; + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert!(state.exists(&a)); + assert!(!state.exists_and_not_null(&a)); + } + + #[test] + fn remove_from_database() { + let a = Address::zero(); + let temp = RandomTempPath::new(); + let (root, db) = { + let mut state = get_temp_state_in(temp.as_path()); + state.inc_nonce(&a); + state.commit().unwrap(); + assert_eq!(state.exists(&a), true); + assert_eq!(state.nonce(&a), U256::from(1u64)); + state.drop() + }; + + let (root, db) = { + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.exists(&a), true); + assert_eq!(state.nonce(&a), U256::from(1u64)); + state.kill_account(&a); + state.commit().unwrap(); + assert_eq!(state.exists(&a), false); + assert_eq!(state.nonce(&a), U256::from(0u64)); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); assert_eq!(state.exists(&a), false); assert_eq!(state.nonce(&a), U256::from(0u64)); - state.drop() - }; + } - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.exists(&a), false); - assert_eq!(state.nonce(&a), U256::from(0u64)); -} + #[test] + fn alter_balance() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + let b = 1u64.into(); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.sub_balance(&a, &U256::from(42u64)); + assert_eq!(state.balance(&a), U256::from(27u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(27u64)); + state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty); + assert_eq!(state.balance(&a), U256::from(9u64)); + assert_eq!(state.balance(&b), U256::from(18u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(9u64)); + assert_eq!(state.balance(&b), U256::from(18u64)); + } -#[test] -fn alter_balance() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - let b = 1u64.into(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.sub_balance(&a, &U256::from(42u64)); - assert_eq!(state.balance(&a), U256::from(27u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a), U256::from(27u64)); - state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty); - assert_eq!(state.balance(&a), U256::from(9u64)); - assert_eq!(state.balance(&b), U256::from(18u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a), U256::from(9u64)); - assert_eq!(state.balance(&b), U256::from(18u64)); -} + #[test] + fn alter_nonce() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + state.inc_nonce(&a); + assert_eq!(state.nonce(&a), U256::from(1u64)); + state.inc_nonce(&a); + assert_eq!(state.nonce(&a), U256::from(2u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a), U256::from(2u64)); + state.inc_nonce(&a); + assert_eq!(state.nonce(&a), U256::from(3u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a), U256::from(3u64)); + } -#[test] -fn alter_nonce() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - state.inc_nonce(&a); - assert_eq!(state.nonce(&a), U256::from(1u64)); - state.inc_nonce(&a); - assert_eq!(state.nonce(&a), U256::from(2u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a), U256::from(2u64)); - state.inc_nonce(&a); - assert_eq!(state.nonce(&a), U256::from(3u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a), U256::from(3u64)); -} + #[test] + fn balance_nonce() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + assert_eq!(state.balance(&a), U256::from(0u64)); + assert_eq!(state.nonce(&a), U256::from(0u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a), U256::from(0u64)); + assert_eq!(state.nonce(&a), U256::from(0u64)); + } -#[test] -fn balance_nonce() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - assert_eq!(state.balance(&a), U256::from(0u64)); - assert_eq!(state.nonce(&a), U256::from(0u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a), U256::from(0u64)); - assert_eq!(state.nonce(&a), U256::from(0u64)); -} + #[test] + fn ensure_cached() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + state.require(&a, false); + state.commit().unwrap(); + assert_eq!(state.root().hex(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785"); + } -#[test] -fn ensure_cached() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - state.require(&a, false); - state.commit().unwrap(); - assert_eq!(state.root().hex(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785"); -} + #[test] + fn checkpoint_basic() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + state.checkpoint(); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.discard_checkpoint(); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.checkpoint(); + state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty); + assert_eq!(state.balance(&a), U256::from(70u64)); + state.revert_to_checkpoint(); + assert_eq!(state.balance(&a), U256::from(69u64)); + } -#[test] -fn checkpoint_basic() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - state.checkpoint(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.discard_checkpoint(); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.checkpoint(); - state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty); - assert_eq!(state.balance(&a), U256::from(70u64)); - state.revert_to_checkpoint(); - assert_eq!(state.balance(&a), U256::from(69u64)); -} + #[test] + fn checkpoint_nested() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + let a = Address::zero(); + state.checkpoint(); + state.checkpoint(); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.discard_checkpoint(); + assert_eq!(state.balance(&a), U256::from(69u64)); + state.revert_to_checkpoint(); + assert_eq!(state.balance(&a), U256::from(0)); + } -#[test] -fn checkpoint_nested() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - let a = Address::zero(); - state.checkpoint(); - state.checkpoint(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.discard_checkpoint(); - assert_eq!(state.balance(&a), U256::from(69u64)); - state.revert_to_checkpoint(); - assert_eq!(state.balance(&a), U256::from(0)); -} + #[test] + fn create_empty() { + let mut state_result = get_temp_state(); + let mut state = state_result.reference_mut(); + state.commit().unwrap(); + assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); + } -#[test] -fn create_empty() { - let mut state_result = get_temp_state(); - let mut state = state_result.reference_mut(); - state.commit().unwrap(); - assert_eq!(state.root().hex(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); -} + #[test] + fn should_not_panic_on_state_diff_with_storage() { + let state = get_temp_state(); + let mut state = state.reference().clone(); -#[test] -fn should_not_panic_on_state_diff_with_storage() { - let state = get_temp_state(); - let mut state = state.reference().clone(); + let a: Address = 0xa.into(); + state.init_code(&a, b"abcdefg".to_vec()); + state.add_balance(&a, &256.into(), CleanupMode::NoEmpty); + state.set_storage(&a, 0xb.into(), 0xc.into()); - let a: Address = 0xa.into(); - state.init_code(&a, b"abcdefg".to_vec()); - state.add_balance(&a, &256.into(), CleanupMode::NoEmpty); - state.set_storage(&a, 0xb.into(), 0xc.into()); + let mut new_state = state.clone(); + new_state.set_storage(&a, 0xb.into(), 0xd.into()); - let mut new_state = state.clone(); - new_state.set_storage(&a, 0xb.into(), 0xd.into()); - - new_state.diff_from(state); -} + new_state.diff_from(state); + } } From abf39fde0a3339f6b190932784f6a125b85014cd Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 15 Nov 2016 14:53:30 +0100 Subject: [PATCH 028/155] 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 029/155] 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 030/155] 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 031/155] 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 032/155] 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 033/155] 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 034/155] 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 035/155] 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 cd6f565f69d71deead5336be2b71f3ff396ba37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 22 Nov 2016 11:56:27 +0100 Subject: [PATCH 036/155] RPC Middleware & Get/Set dapp-specific accounts --- Cargo.lock | 92 +++---- Cargo.toml | 1 - dapps/Cargo.toml | 4 +- dapps/src/rpc.rs | 94 ++++++- .../mod.rs} | 136 +++------- ethcore/src/account_provider/stores.rs | 247 ++++++++++++++++++ json/src/misc/account_meta.rs | 2 +- json/src/misc/dapps_settings.rs | 51 ++++ json/src/misc/mod.rs | 2 + parity/main.rs | 2 - parity/rpc.rs | 8 +- rpc/Cargo.toml | 6 +- rpc/src/lib.rs | 7 +- rpc/src/v1/impls/eth.rs | 15 +- rpc/src/v1/impls/parity_accounts.rs | 11 +- rpc/src/v1/tests/eth.rs | 2 +- rpc/src/v1/tests/mocked/eth.rs | 16 +- rpc/src/v1/tests/mocked/net.rs | 2 +- rpc/src/v1/tests/mocked/parity.rs | 2 +- rpc/src/v1/tests/mocked/parity_accounts.rs | 17 +- rpc/src/v1/tests/mocked/parity_set.rs | 2 +- rpc/src/v1/tests/mocked/personal.rs | 2 +- rpc/src/v1/tests/mocked/rpc.rs | 2 +- rpc/src/v1/tests/mocked/signer.rs | 2 +- rpc/src/v1/tests/mocked/signing.rs | 48 ++-- rpc/src/v1/tests/mocked/web3.rs | 2 +- rpc/src/v1/traits/eth.rs | 4 +- rpc/src/v1/traits/parity_accounts.rs | 8 +- rpc/src/v1/types/dapp_id.rs | 60 +++++ rpc/src/v1/types/mod.rs.in | 2 + signer/Cargo.toml | 2 +- signer/src/ws_server/session.rs | 20 +- stratum/Cargo.toml | 4 +- stratum/src/lib.rs | 4 +- 34 files changed, 655 insertions(+), 224 deletions(-) rename ethcore/src/{account_provider.rs => account_provider/mod.rs} (80%) create mode 100644 ethcore/src/account_provider/stores.rs create mode 100644 json/src/misc/dapps_settings.rs create mode 100644 rpc/src/v1/types/dapp_id.rs diff --git a/Cargo.lock b/Cargo.lock index 02e45b49e..eaede16ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,6 @@ dependencies = [ "fdlimit 0.1.0", "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)", - "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -341,8 +340,8 @@ dependencies = [ "ethcore-util 1.5.0", "fetch 0.1.0", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -503,9 +502,9 @@ dependencies = [ "ethstore 0.1.0", "ethsync 1.5.0", "fetch 0.1.0", - "json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -526,7 +525,7 @@ dependencies = [ "ethcore-io 1.5.0", "ethcore-rpc 1.5.0", "ethcore-util 1.5.0", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-ui 1.4.0", @@ -545,8 +544,8 @@ dependencies = [ "ethcore-ipc-codegen 1.4.0", "ethcore-ipc-nano 1.4.0", "ethcore-util 1.5.0", - "json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)", @@ -831,39 +830,10 @@ name = "itoa" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "json-ipc-server" -version = "0.2.4" -source = "git+https://github.com/ethcore/json-ipc-server.git#4642cd03ec1d23db89df80d22d5a88e7364ab885" -dependencies = [ - "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "json-tcp-server" -version = "0.1.0" -source = "git+https://github.com/ethcore/json-tcp-server#c2858522274ae56042472bb5d22845a1b85e5338" -dependencies = [ - "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "jsonrpc-core" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "4.0.0" +source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -875,14 +845,44 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" version = "6.1.1" -source = "git+https://github.com/ethcore/jsonrpc-http-server.git#cd6d4cb37d672cc3057aecd0692876f9e85f3ba5" +source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" dependencies = [ "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "jsonrpc-ipc-server" +version = "0.2.4" +source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" +dependencies = [ + "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jsonrpc-tcp-server" +version = "0.1.0" +source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" +dependencies = [ + "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -2032,10 +2032,10 @@ dependencies = [ "checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c" "checksum itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "086e1fa5fe48840b1cfdef3a20c7e3115599f8d5c4c87ef32a794a7cdd184d76" "checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1" -"checksum json-ipc-server 0.2.4 (git+https://github.com/ethcore/json-ipc-server.git)" = "" -"checksum json-tcp-server 0.1.0 (git+https://github.com/ethcore/json-tcp-server)" = "" -"checksum jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3c5094610b07f28f3edaf3947b732dadb31dbba4941d4d0c1c7a8350208f4414" -"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)" = "" +"checksum jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" +"checksum jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)" = "" +"checksum jsonrpc-ipc-server 0.2.4 (git+https://github.com/ethcore/jsonrpc.git)" = "" +"checksum jsonrpc-tcp-server 0.1.0 (git+https://github.com/ethcore/jsonrpc.git)" = "" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" diff --git a/Cargo.toml b/Cargo.toml index a8d7ba794..871e2f0a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,6 @@ 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" } -json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" } fdlimit = { path = "util/fdlimit" } ethcore = { path = "ethcore" } ethcore-util = { path = "util" } diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 15e537820..b8b0e3c78 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -12,8 +12,8 @@ build = "build.rs" rand = "0.3.14" log = "0.3" env_logger = "0.3" -jsonrpc-core = "3.0" -jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" } +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } +jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" } hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } unicase = "1.3" url = "1.0" diff --git a/dapps/src/rpc.rs b/dapps/src/rpc.rs index 625bfc269..cb595e1e1 100644 --- a/dapps/src/rpc.rs +++ b/dapps/src/rpc.rs @@ -16,13 +16,14 @@ use std::sync::{Arc, Mutex}; use hyper; -use jsonrpc_core::IoHandler; -use jsonrpc_http_server::{ServerHandler, PanicHandler, AccessControlAllowOrigin}; + +use jsonrpc_core::{IoHandler, ResponseHandler, Request, Response}; +use jsonrpc_http_server::{ServerHandler, PanicHandler, AccessControlAllowOrigin, RpcHandler}; use endpoint::{Endpoint, EndpointPath, Handler}; pub fn rpc(handler: Arc, panic_handler: Arc () + Send>>>>) -> Box { Box::new(RpcEndpoint { - handler: handler, + handler: Arc::new(RpcMiddleware::new(handler)), panic_handler: panic_handler, cors_domain: None, // NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router. @@ -31,7 +32,7 @@ pub fn rpc(handler: Arc, panic_handler: Arc } struct RpcEndpoint { - handler: Arc, + handler: Arc, panic_handler: Arc () + Send>>>>, cors_domain: Option>, allowed_hosts: Option>, @@ -49,3 +50,88 @@ impl Endpoint for RpcEndpoint { )) } } + +const MIDDLEWARE_METHOD: &'static str = "eth_accounts"; + +struct RpcMiddleware { + handler: Arc, +} + +impl RpcMiddleware { + fn new(handler: Arc) -> Self { + RpcMiddleware { + handler: handler, + } + } + + /// Appends additional parameter for specific calls. + fn augment_request(&self, request: &mut Request, meta: Option) { + use jsonrpc_core::{Call, Params, to_value}; + + fn augment_call(call: &mut Call, meta: Option<&Meta>) { + match (call, meta) { + (&mut Call::MethodCall(ref mut method_call), Some(meta)) if &method_call.method == MIDDLEWARE_METHOD => { + let session = to_value(&meta.app_id); + + let params = match method_call.params { + Some(Params::Array(ref vec)) if vec.len() == 0 => Some(Params::Array(vec![session])), + // invalid params otherwise + _ => None, + }; + + method_call.params = params; + }, + _ => {} + } + } + + match *request { + Request::Single(ref mut call) => augment_call(call, meta.as_ref()), + Request::Batch(ref mut vec) => { + for mut call in vec { + augment_call(call, meta.as_ref()) + } + }, + } + } +} + +#[derive(Debug)] +struct Meta { + app_id: String, +} + +impl RpcHandler for RpcMiddleware { + type Metadata = Meta; + + fn read_metadata(&self, request: &hyper::server::Request) -> Option { + let meta = request.headers().get::() + .and_then(|referer| hyper::Url::parse(referer).ok()) + .and_then(|url| { + url.path_segments() + .and_then(|mut split| split.next()) + .map(|app_id| Meta { + app_id: app_id.to_owned(), + }) + }); + println!("{:?}, {:?}", meta, request.headers()); + meta + } + + fn handle_request(&self, request_str: &str, response_handler: H, meta: Option) where + H: ResponseHandler, Option> + 'static + { + let handler = IoHandler::convert_handler(response_handler); + let request = IoHandler::read_request(request_str); + trace!(target: "rpc", "Request metadata: {:?}", meta); + + match request { + Ok(mut request) => { + self.augment_request(&mut request, meta); + self.handler.request_handler().handle_request(request, handler, None) + }, + Err(error) => handler.send(Some(Response::from(error))), + } + } +} + diff --git a/ethcore/src/account_provider.rs b/ethcore/src/account_provider/mod.rs similarity index 80% rename from ethcore/src/account_provider.rs rename to ethcore/src/account_provider/mod.rs index e906aefe9..75c3e2683 100644 --- a/ethcore/src/account_provider.rs +++ b/ethcore/src/account_provider/mod.rs @@ -16,9 +16,12 @@ //! Account management. -use std::{fs, fmt}; +mod stores; + +use self::stores::{AddressBook, DappsSettingsStore}; + +use std::fmt; use std::collections::HashMap; -use std::path::PathBuf; use std::time::{Instant, Duration}; use util::{Mutex, RwLock}; use ethstore::{SecretStore, Error as SSError, SafeAccount, EthStore}; @@ -91,84 +94,16 @@ impl KeyDirectory for NullDir { } } -/// Disk-backed map from Address to String. Uses JSON. -struct AddressBook { - path: PathBuf, - cache: HashMap, - transient: bool, -} - -impl AddressBook { - pub fn new(path: String) -> Self { - trace!(target: "addressbook", "new({})", path); - let mut path: PathBuf = path.into(); - path.push("address_book.json"); - trace!(target: "addressbook", "path={:?}", path); - let mut r = AddressBook { - path: path, - cache: HashMap::new(), - transient: false, - }; - r.revert(); - r - } - - pub fn transient() -> Self { - let mut book = AddressBook::new(Default::default()); - book.transient = true; - book - } - - pub fn get(&self) -> HashMap { - self.cache.clone() - } - - pub fn set_name(&mut self, a: Address, name: String) { - let mut x = self.cache.get(&a) - .cloned() - .unwrap_or_else(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None}); - x.name = name; - self.cache.insert(a, x); - self.save(); - } - - pub fn set_meta(&mut self, a: Address, meta: String) { - let mut x = self.cache.get(&a) - .cloned() - .unwrap_or_else(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None}); - x.meta = meta; - self.cache.insert(a, x); - self.save(); - } - - fn revert(&mut self) { - if self.transient { return; } - trace!(target: "addressbook", "revert"); - let _ = fs::File::open(self.path.clone()) - .map_err(|e| trace!(target: "addressbook", "Couldn't open address book: {}", e)) - .and_then(|f| AccountMeta::read_address_map(&f) - .map_err(|e| warn!(target: "addressbook", "Couldn't read address book: {}", e)) - .and_then(|m| { self.cache = m; Ok(()) }) - ); - } - - fn save(&mut self) { - if self.transient { return; } - trace!(target: "addressbook", "save"); - let _ = fs::File::create(self.path.clone()) - .map_err(|e| warn!(target: "addressbook", "Couldn't open address book for writing: {}", e)) - .and_then(|mut f| AccountMeta::write_address_map(&self.cache, &mut f) - .map_err(|e| warn!(target: "addressbook", "Couldn't write to address book: {}", e)) - ); - } -} +/// Dapp identifier +pub type DappId = String; /// Account management. /// Responsible for unlocking accounts. pub struct AccountProvider { unlocked: Mutex>, sstore: Box, - address_book: Mutex, + address_book: RwLock, + dapps_settings: RwLock, } impl AccountProvider { @@ -176,7 +111,8 @@ impl AccountProvider { pub fn new(sstore: Box) -> Self { AccountProvider { unlocked: Mutex::new(HashMap::new()), - address_book: Mutex::new(AddressBook::new(sstore.local_path().into())), + address_book: RwLock::new(AddressBook::new(sstore.local_path().into())), + dapps_settings: RwLock::new(DappsSettingsStore::new(sstore.local_path().into())), sstore: sstore, } } @@ -185,7 +121,8 @@ impl AccountProvider { pub fn transient_provider() -> Self { AccountProvider { unlocked: Mutex::new(HashMap::new()), - address_book: Mutex::new(AddressBook::transient()), + address_book: RwLock::new(AddressBook::transient()), + dapps_settings: RwLock::new(DappsSettingsStore::transient()), sstore: Box::new(EthStore::open(Box::new(NullDir::default())) .expect("NullDir load always succeeds; qed")) } @@ -230,19 +167,31 @@ impl AccountProvider { Ok(accounts) } + /// Gets addresses visile for dapp. + pub fn dapps_addresses(&self, dapp: DappId) -> Result, Error> { + let accounts = self.dapps_settings.read().get(); + Ok(accounts.get(&dapp).map(|settings| settings.accounts.clone()).unwrap_or_else(Vec::new)) + } + + /// Sets addresses visile for dapp. + pub fn set_dapps_addresses(&self, dapp: DappId, addresses: Vec
) -> Result<(), Error> { + self.dapps_settings.write().set_accounts(dapp, addresses); + Ok(()) + } + /// Returns each address along with metadata. pub fn addresses_info(&self) -> Result, Error> { - Ok(self.address_book.lock().get()) + Ok(self.address_book.read().get()) } /// Returns each address along with metadata. pub fn set_address_name(&self, account: Address, name: String) -> Result<(), Error> { - Ok(self.address_book.lock().set_name(account, name)) + Ok(self.address_book.write().set_name(account, name)) } /// Returns each address along with metadata. pub fn set_address_meta(&self, account: Address, meta: String) -> Result<(), Error> { - Ok(self.address_book.lock().set_meta(account, meta)) + Ok(self.address_book.write().set_meta(account, meta)) } /// Returns each account along with name and meta. @@ -373,23 +322,9 @@ impl AccountProvider { #[cfg(test)] mod tests { - use super::{AccountProvider, AddressBook, Unlock}; - use std::collections::HashMap; + use super::{AccountProvider, Unlock}; use std::time::Instant; - use ethjson::misc::AccountMeta; use ethstore::ethkey::{Generator, Random}; - use devtools::RandomTempPath; - - #[test] - fn should_save_and_reload_address_book() { - let temp = RandomTempPath::create_dir(); - let path = temp.as_str().to_owned(); - let mut b = AddressBook::new(path.clone()); - b.set_name(1.into(), "One".to_owned()); - b.set_meta(1.into(), "{1:1}".to_owned()); - let b = AddressBook::new(path); - assert_eq!(b.get(), hash_map![1.into() => AccountMeta{name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}]); - } #[test] fn unlock_account_temp() { @@ -427,4 +362,17 @@ mod tests { ap.unlocked.lock().get_mut(&kp.address()).unwrap().unlock = Unlock::Timed(Instant::now()); assert!(ap.sign(kp.address(), None, Default::default()).is_err()); } + + #[test] + fn should_set_dapps_addresses() { + // given + let ap = AccountProvider::transient_provider(); + let app = "app1".to_owned(); + + // when + ap.set_dapps_addresses(app.clone(), vec![1.into(), 2.into()]).unwrap(); + + // then + assert_eq!(ap.dapps_addresses(app.clone()).unwrap(), vec![1.into(), 2.into()]); + } } diff --git a/ethcore/src/account_provider/stores.rs b/ethcore/src/account_provider/stores.rs new file mode 100644 index 000000000..cfc81f495 --- /dev/null +++ b/ethcore/src/account_provider/stores.rs @@ -0,0 +1,247 @@ +// 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 Book and Dapps Settings Store + +use std::{fs, fmt, hash, ops}; +use std::collections::HashMap; +use std::path::PathBuf; + +use ethstore::ethkey::Address; +use ethjson::misc::{AccountMeta, DappsSettings as JsonSettings}; +use account_provider::DappId; + +/// Disk-backed map from Address to String. Uses JSON. +pub struct AddressBook { + cache: DiskMap, +} + +impl AddressBook { + /// Creates new address book at given directory. + pub fn new(path: String) -> Self { + let mut r = AddressBook { + cache: DiskMap::new(path, "address_book.json".into()) + }; + r.cache.revert(AccountMeta::read_address_map); + r + } + + /// Creates transient address book (no changes are saved to disk). + pub fn transient() -> Self { + AddressBook { + cache: DiskMap::transient() + } + } + + /// Get the address book. + pub fn get(&self) -> HashMap { + self.cache.clone() + } + + fn save(&self) { + self.cache.save(AccountMeta::write_address_map) + } + + /// Sets new name for given address. + pub fn set_name(&mut self, a: Address, name: String) { + { + let mut x = self.cache.entry(a) + .or_insert_with(|| AccountMeta {name: Default::default(), meta: "{}".to_owned(), uuid: None}); + x.name = name; + } + self.save(); + } + + /// Sets new meta for given address. + pub fn set_meta(&mut self, a: Address, meta: String) { + { + let mut x = self.cache.entry(a) + .or_insert_with(|| AccountMeta {name: "Anonymous".to_owned(), meta: Default::default(), uuid: None}); + x.meta = meta; + } + self.save(); + } +} + +/// Dapps user settings +#[derive(Debug, Default, Clone, Eq, PartialEq)] +pub struct DappsSettings { + /// A list of visible accounts + pub accounts: Vec
, +} + +impl From for DappsSettings { + fn from(s: JsonSettings) -> Self { + DappsSettings { + accounts: s.accounts.into_iter().map(Into::into).collect(), + } + } +} + +impl From for JsonSettings { + fn from(s: DappsSettings) -> Self { + JsonSettings { + accounts: s.accounts.into_iter().map(Into::into).collect(), + } + } +} + +/// Disk-backed map from DappId to Settings. Uses JSON. +pub struct DappsSettingsStore { + cache: DiskMap, +} + +impl DappsSettingsStore { + /// Creates new store at given directory path. + pub fn new(path: String) -> Self { + let mut r = DappsSettingsStore { + cache: DiskMap::new(path, "dapps_accounts.json".into()) + }; + r.cache.revert(JsonSettings::read_dapps_settings); + r + } + + /// Creates transient store (no changes are saved to disk). + pub fn transient() -> Self { + DappsSettingsStore { + cache: DiskMap::transient() + } + } + + /// Get copy of the dapps settings + pub fn get(&self) -> HashMap { + self.cache.clone() + } + + fn save(&self) { + self.cache.save(JsonSettings::write_dapps_settings) + } + + pub fn set_accounts(&mut self, id: DappId, accounts: Vec
) { + { + let mut settings = self.cache.entry(id).or_insert_with(DappsSettings::default); + settings.accounts = accounts; + } + self.save(); + } +} + +/// Disk-serializable HashMap +#[derive(Debug)] +struct DiskMap { + path: PathBuf, + cache: HashMap, + transient: bool, +} + +impl ops::Deref for DiskMap { + type Target = HashMap; + fn deref(&self) -> &Self::Target { + &self.cache + } +} + +impl ops::DerefMut for DiskMap { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cache + } +} + +impl DiskMap { + pub fn new(path: String, file_name: String) -> Self { + trace!(target: "diskmap", "new({})", path); + let mut path: PathBuf = path.into(); + path.push(file_name); + trace!(target: "diskmap", "path={:?}", path); + DiskMap { + path: path, + cache: HashMap::new(), + transient: false, + } + } + + pub fn transient() -> Self { + let mut map = DiskMap::new(Default::default(), "diskmap.json".into()); + map.transient = true; + map + } + + fn revert(&mut self, read: F) where + F: Fn(fs::File) -> Result, E>, + E: fmt::Display, + { + if self.transient { return; } + trace!(target: "diskmap", "revert {:?}", self.path); + let _ = fs::File::open(self.path.clone()) + .map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e)) + .and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map: {}", e))) + .and_then(|m| { + self.cache = m; + Ok(()) + }); + } + + fn save(&self, write: F) where + F: Fn(&HashMap, &mut fs::File) -> Result<(), E>, + E: fmt::Display, + { + if self.transient { return; } + trace!(target: "diskmap", "save {:?}", self.path); + let _ = fs::File::create(self.path.clone()) + .map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e)) + .and_then(|mut f| { + write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map: {}", e)) + }); + } +} + +#[cfg(test)] +mod tests { + use super::{AddressBook, DappsSettingsStore, DappsSettings}; + use std::collections::HashMap; + use ethjson::misc::AccountMeta; + use devtools::RandomTempPath; + + #[test] + fn should_save_and_reload_address_book() { + let temp = RandomTempPath::create_dir(); + let path = temp.as_str().to_owned(); + let mut b = AddressBook::new(path.clone()); + b.set_name(1.into(), "One".to_owned()); + b.set_meta(1.into(), "{1:1}".to_owned()); + let b = AddressBook::new(path); + assert_eq!(b.get(), hash_map![1.into() => AccountMeta{name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}]); + } + + #[test] + fn should_save_and_reload_dapps_settings() { + // given + let temp = RandomTempPath::create_dir(); + let path = temp.as_str().to_owned(); + let mut b = DappsSettingsStore::new(path.clone()); + + // when + b.set_accounts("dappOne".into(), vec![1.into(), 2.into()]); + + // then + let b = DappsSettingsStore::new(path); + assert_eq!(b.get(), hash_map![ + "dappOne".into() => DappsSettings { + accounts: vec![1.into(), 2.into()], + } + ]); + } +} diff --git a/json/src/misc/account_meta.rs b/json/src/misc/account_meta.rs index 242b58a01..400f9b8df 100644 --- a/json/src/misc/account_meta.rs +++ b/json/src/misc/account_meta.rs @@ -51,7 +51,7 @@ impl AccountMeta { ) } - /// Write a hash map of Address -> AccountMeta. + /// Write a hash map of Address -> AccountMeta. pub fn write_address_map(m: &HashMap, writer: &mut W) -> Result<(), serde_json::Error> where W: Write { serde_json::to_writer(writer, &m.iter().map(|(a, m)| (a.clone().into(), m)).collect::>()) } diff --git a/json/src/misc/dapps_settings.rs b/json/src/misc/dapps_settings.rs new file mode 100644 index 000000000..893e7e93e --- /dev/null +++ b/json/src/misc/dapps_settings.rs @@ -0,0 +1,51 @@ +// 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 . + +//! Dapps settings de/serialization. + +use std::io; +use std::collections::HashMap; +use serde_json; +use hash; + +type DappId = String; + +/// Settings for specific dapp. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DappsSettings { + /// A list of accounts this Dapp can see. + pub accounts: Vec, +} + +impl DappsSettings { + /// Read a hash map of DappId -> DappsSettings + pub fn read_dapps_settings(reader: R) -> Result, serde_json::Error> where + R: io::Read, + S: From + Clone, + { + serde_json::from_reader(reader).map(|ok: HashMap| + ok.into_iter().map(|(a, m)| (a.into(), m.into())).collect() + ) + } + + /// Write a hash map of DappId -> DappsSettings + pub fn write_dapps_settings(m: &HashMap, writer: &mut W) -> Result<(), serde_json::Error> where + W: io::Write, + S: Into + Clone, + { + serde_json::to_writer(writer, &m.iter().map(|(a, m)| (a.clone().into(), m.clone().into())).collect::>()) + } +} diff --git a/json/src/misc/mod.rs b/json/src/misc/mod.rs index 5db868d03..baab83d08 100644 --- a/json/src/misc/mod.rs +++ b/json/src/misc/mod.rs @@ -17,5 +17,7 @@ //! Misc deserialization. mod account_meta; +mod dapps_settings; +pub use self::dapps_settings::DappsSettings; pub use self::account_meta::AccountMeta; diff --git a/parity/main.rs b/parity/main.rs index 274d29de2..c125e87f6 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -44,8 +44,6 @@ extern crate serde_json; extern crate rlp; extern crate ethcore_hash_fetch as hash_fetch; -extern crate json_ipc_server as jsonipc; - extern crate ethcore_ipc_hypervisor as hypervisor; extern crate ethcore_rpc; diff --git a/parity/rpc.rs b/parity/rpc.rs index 59279eaea..52a5bcc0f 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -19,14 +19,12 @@ use std::sync::Arc; use std::net::SocketAddr; use std::io; use io::PanicHandler; -use ethcore_rpc::{RpcServerError, RpcServer as Server}; -use jsonipc; +use ethcore_rpc::{RpcServerError, RpcServer as Server, IpcServerError}; use rpc_apis; use rpc_apis::ApiSet; use helpers::parity_ipc_path; -pub use jsonipc::Server as IpcServer; -pub use ethcore_rpc::Server as HttpServer; +pub use ethcore_rpc::{IpcServer, Server as HttpServer}; #[derive(Debug, PartialEq)] pub struct HttpConfiguration { @@ -126,7 +124,7 @@ pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Result Result { let server = try!(setup_rpc_server(apis, dependencies)); match server.start_ipc(addr) { - Err(jsonipc::Error::Io(io_error)) => Err(format!("RPC io error: {}", io_error)), + Err(IpcServerError::Io(io_error)) => Err(format!("RPC io error: {}", io_error)), Err(any_error) => Err(format!("Rpc error: {:?}", any_error)), Ok(server) => Ok(server) } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 4a8c4d76a..68470b963 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -12,8 +12,9 @@ build = "build.rs" log = "0.3" serde = "0.8" serde_json = "0.8" -jsonrpc-core = "3.0" -jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" } +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } +jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" } +jsonrpc-ipc-server = { git = "https://github.com/ethcore/jsonrpc.git" } ethcore-io = { path = "../util/io" } ethcore-util = { path = "../util" } ethcore = { path = "../ethcore" } @@ -30,7 +31,6 @@ rustc-serialize = "0.3" transient-hashmap = "0.1" serde_macros = { version = "0.8.0", optional = true } clippy = { version = "0.0.96", optional = true} -json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" } ethcore-ipc = { path = "../ipc/rpc" } time = "0.1" diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 3b67ae7f0..e02a18509 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -32,7 +32,7 @@ extern crate ethcrypto as crypto; extern crate ethstore; extern crate ethsync; extern crate transient_hashmap; -extern crate json_ipc_server as ipc; +extern crate jsonrpc_ipc_server as ipc; extern crate ethcore_ipc; extern crate time; extern crate rlp; @@ -51,8 +51,9 @@ extern crate ethcore_devtools as devtools; use std::sync::Arc; use std::net::SocketAddr; use io::PanicHandler; -use self::jsonrpc_core::{IoHandler, IoDelegate}; +use jsonrpc_core::{IoHandler, IoDelegate}; +pub use ipc::{Server as IpcServer, Error as IpcServerError}; pub use jsonrpc_http_server::{ServerBuilder, Server, RpcServerError}; pub mod v1; pub use v1::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings}; @@ -66,7 +67,7 @@ pub trait Extendable { /// Http server. pub struct RpcServer { - handler: Arc, + handler: Arc, } impl Extendable for RpcServer { diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 5f1449e07..6b9f47de3 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -20,7 +20,6 @@ extern crate ethash; use std::io::{Write}; use std::process::{Command, Stdio}; -use std::collections::BTreeSet; use std::thread; use std::time::{Instant, Duration}; use std::sync::{Arc, Weak}; @@ -46,7 +45,7 @@ use self::ethash::SeedHashCompute; use v1::traits::Eth; use v1::types::{ RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, - Transaction, CallRequest, Index, Filter, Log, Receipt, Work, + Transaction, CallRequest, Index, Filter, Log, Receipt, Work, DappId, H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256, }; use v1::helpers::{CallRequest as CRequest, errors, limit_logs}; @@ -335,15 +334,15 @@ impl Eth for EthClient where Ok(RpcU256::from(default_gas_price(&*client, &*miner))) } - fn accounts(&self) -> Result, Error> { + fn accounts(&self, id: Trailing) -> Result, Error> { try!(self.active()); - let store = take_weak!(self.accounts); - let accounts = try!(store.accounts().map_err(|e| errors::internal("Could not fetch accounts.", e))); - let addresses = try!(store.addresses_info().map_err(|e| errors::internal("Could not fetch accounts.", e))); + let dapp = id.0; - let set: BTreeSet
= accounts.into_iter().chain(addresses.keys().cloned()).collect(); - Ok(set.into_iter().map(Into::into).collect()) + let store = take_weak!(self.accounts); + let accounts = try!(store.dapps_addresses(dapp.into()).map_err(|e| errors::internal("Could not fetch accounts.", e))); + + Ok(accounts.into_iter().map(Into::into).collect()) } fn block_number(&self) -> Result { diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index 2644c59e3..25d3c0904 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -25,7 +25,7 @@ use ethcore::client::MiningBlockChainClient; use jsonrpc_core::{Value, Error, to_value}; use v1::traits::ParityAccounts; -use v1::types::{H160 as RpcH160, H256 as RpcH256}; +use v1::types::{H160 as RpcH160, H256 as RpcH256, DappId}; use v1::helpers::errors; /// Account management (personal) rpc implementation. @@ -143,6 +143,15 @@ impl ParityAccounts for ParityAccountsClient where C: MiningBlock Ok(false) } + fn set_dapps_addresses(&self, dapp: DappId, addresses: Vec) -> Result { + let store = take_weak!(self.accounts); + let addresses = addresses.into_iter().map(Into::into).collect(); + + store.set_dapps_addresses(dapp.into(), addresses) + .map_err(|e| errors::account("Couldn't set dapps addresses.", e)) + .map(|_| true) + } + fn import_geth_accounts(&self, addresses: Vec) -> Result, Error> { let store = take_weak!(self.accounts); diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 2f5131f32..7894fa111 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -30,7 +30,7 @@ use devtools::RandomTempPath; use util::Hashable; use io::IoChannel; use util::{U256, H256, Uint, Address}; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use ethjson::blockchain::BlockChain; use v1::impls::{EthClient, SigningUnsafeClient}; diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 861bb5234..2f31aa4e1 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -31,7 +31,7 @@ use ethcore::transaction::{Transaction, Action}; use ethcore::miner::{ExternalMiner, MinerService}; use ethsync::SyncState; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService}; @@ -357,15 +357,15 @@ fn rpc_eth_accounts() { let address = tester.accounts_provider.new_account("").unwrap(); let address2 = Address::default(); - tester.accounts_provider.set_address_name(address2, "Test Account".into()).unwrap(); - + // even with some account it should return empty list (no dapp detected) let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned() - + &format!("0x{:?}", address2) - + r#"",""# - + &format!("0x{:?}", address) - + r#""],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + // when we add visible address it should return that. + tester.accounts_provider.set_dapps_addresses("app1".into(), vec![10.into()]).unwrap(); + let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": ["app1"], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":["0x000000000000000000000000000000000000000a"],"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/net.rs b/rpc/src/v1/tests/mocked/net.rs index 0a5eb43e7..37ef84fca 100644 --- a/rpc/src/v1/tests/mocked/net.rs +++ b/rpc/src/v1/tests/mocked/net.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::sync::Arc; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{Net, NetClient}; use v1::tests::helpers::{Config, TestSyncProvider}; diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 5226e2f96..9b4daaccd 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -23,7 +23,7 @@ use ethcore::client::{TestBlockChainClient}; use ethcore::miner::LocalTransactionStatus; use ethstore::ethkey::{Generator, Random}; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{Parity, ParityClient}; use v1::helpers::{SignerService, NetworkSettings}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService}; diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index c5ed4172e..bd6e1ffba 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use ethcore::account_provider::AccountProvider; use ethcore::client::TestBlockChainClient; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{ParityAccounts, ParityAccountsClient}; struct ParityAccountsTester { @@ -116,3 +116,18 @@ fn should_be_able_to_set_meta() { assert_eq!(res, Some(response)); } + +#[test] +fn rpc_parity_set_dapps_accounts() { + // given + let tester = setup(); + assert_eq!(tester.accounts.dapps_addresses("app1".into()).unwrap(), vec![]); + + // when + let request = r#"{"jsonrpc": "2.0", "method": "parity_setDappsAddresses","params":["app1",["0x000000000000000000000000000000000000000a"]], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); + + // then + assert_eq!(tester.accounts.dapps_addresses("app1".into()).unwrap(), vec![10.into()]); +} diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 3202374a7..01f33e251 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -23,7 +23,7 @@ use ethcore::miner::MinerService; use ethcore::client::TestBlockChainClient; use ethsync::ManageNetwork; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{ParitySet, ParitySetClient}; use v1::tests::helpers::{TestMinerService, TestFetch}; use super::manage_network::TestManageNetwork; diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index 6e2de1e2e..a1e8fe982 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use std::str::FromStr; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use util::{U256, Uint, Address}; use ethcore::account_provider::AccountProvider; use v1::{PersonalClient, Personal}; diff --git a/rpc/src/v1/tests/mocked/rpc.rs b/rpc/src/v1/tests/mocked/rpc.rs index b2c340d94..44406f4e3 100644 --- a/rpc/src/v1/tests/mocked/rpc.rs +++ b/rpc/src/v1/tests/mocked/rpc.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::collections::BTreeMap; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{Rpc, RpcClient}; diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index e2ba580e0..912dddc81 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -23,7 +23,7 @@ use ethcore::client::TestBlockChainClient; use ethcore::transaction::{Transaction, Action}; use rlp::encode; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use v1::{SignerClient, Signer}; use v1::tests::helpers::TestMinerService; use v1::helpers::{SigningQueue, SignerService, FilledTransactionRequest, ConfirmationPayload}; diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 7431bc45e..629fbe707 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -15,10 +15,10 @@ // along with Parity. If not, see . use std::str::FromStr; -use std::sync::Arc; +use std::sync::{mpsc, Arc}; use rlp; -use jsonrpc_core::{IoHandler, Success}; +use jsonrpc_core::{IoHandler, Success, GenericIoHandler}; use v1::impls::SigningQueueClient; use v1::traits::{EthSigning, ParitySigning, Parity}; use v1::helpers::{SignerService, SigningQueue}; @@ -87,13 +87,16 @@ fn should_add_sign_to_queue() { let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","id":1}"#; // then - let async_result = tester.io.handle_request(&request).unwrap(); + let (tx, rx) = mpsc::channel(); + tester.io.handle_request(&request, move |response| { + tx.send(response).unwrap(); + }); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Signature(0.into()))); - assert!(async_result.on_result(move |res| { - assert_eq!(res, response.to_owned()); - })); + + let res = rx.try_recv().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] @@ -227,13 +230,16 @@ fn should_add_transaction_to_queue() { let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#; // then - let async_result = tester.io.handle_request(&request).unwrap(); + let (tx, rx) = mpsc::channel(); + tester.io.handle_request(&request, move |response| { + tx.send(response).unwrap(); + }); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SendTransaction(0.into()))); - assert!(async_result.on_result(move |res| { - assert_eq!(res, response.to_owned()); - })); + + let res = rx.try_recv().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] @@ -289,14 +295,17 @@ fn should_add_sign_transaction_to_the_queue() { r#"}},"id":1}"#; // then + let (tx, rx) = mpsc::channel(); tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); - let async_result = tester.io.handle_request(&request).unwrap(); + tester.io.handle_request(&request, move |response| { + tx.send(response).unwrap(); + }); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction(t.into()))); - assert!(async_result.on_result(move |res| { - assert_eq!(res, response.to_owned()); - })); + + let res = rx.try_recv().unwrap(); + assert_eq!(res, Some(response.to_owned())); } #[test] @@ -387,11 +396,14 @@ fn should_add_decryption_to_the_queue() { let response = r#"{"jsonrpc":"2.0","result":"0x0102","id":1}"#; // then - let async_result = tester.io.handle_request(&request).unwrap(); + let (tx, rx) = mpsc::channel(); + tester.io.handle_request(&request, move |response| { + tx.send(response).unwrap(); + }); assert_eq!(tester.signer.requests().len(), 1); // respond tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into()))); - assert!(async_result.on_result(move |res| { - assert_eq!(res, response.to_owned()); - })); + + let res = rx.try_recv().unwrap(); + assert_eq!(res, Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/web3.rs b/rpc/src/v1/tests/mocked/web3.rs index b9f80b0a8..c3bd79110 100644 --- a/rpc/src/v1/tests/mocked/web3.rs +++ b/rpc/src/v1/tests/mocked/web3.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, GenericIoHandler}; use util::version; use v1::{Web3, Web3Client}; diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 6308be324..64a87c175 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -17,7 +17,7 @@ //! Eth rpc interface. use jsonrpc_core::Error; -use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index}; +use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index, DappId}; use v1::types::{Log, Receipt, SyncStatus, Transaction, Work}; use v1::types::{H64, H160, H256, U256}; @@ -52,7 +52,7 @@ build_rpc_trait! { /// Returns accounts list. #[rpc(name = "eth_accounts")] - fn accounts(&self) -> Result, Error>; + fn accounts(&self, Trailing) -> Result, Error>; /// Returns highest block number. #[rpc(name = "eth_blockNumber")] diff --git a/rpc/src/v1/traits/parity_accounts.rs b/rpc/src/v1/traits/parity_accounts.rs index 0f62f59d1..e8a24bbaf 100644 --- a/rpc/src/v1/traits/parity_accounts.rs +++ b/rpc/src/v1/traits/parity_accounts.rs @@ -19,7 +19,7 @@ use std::collections::BTreeMap; use jsonrpc_core::{Value, Error}; use v1::helpers::auto_args::Wrap; -use v1::types::{H160, H256}; +use v1::types::{H160, H256, DappId}; build_rpc_trait! { /// Personal Parity rpc interface. @@ -61,10 +61,14 @@ build_rpc_trait! { #[rpc(name = "parity_setAccountMeta")] fn set_account_meta(&self, H160, String) -> Result; - /// Returns accounts information. + /// Sets account visibility #[rpc(name = "parity_setAccountVisiblity")] fn set_account_visibility(&self, H160, H256, bool) -> Result; + /// Sets accounts exposed for particular dapp. + #[rpc(name = "parity_setDappsAddresses")] + fn set_dapps_addresses(&self, DappId, Vec) -> Result; + /// Imports a number of Geth accounts, with the list provided as the argument. #[rpc(name = "parity_importGethAccounts")] fn import_geth_accounts(&self, Vec) -> Result, Error>; diff --git a/rpc/src/v1/types/dapp_id.rs b/rpc/src/v1/types/dapp_id.rs new file mode 100644 index 000000000..04aa80e3a --- /dev/null +++ b/rpc/src/v1/types/dapp_id.rs @@ -0,0 +1,60 @@ +// 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 . + +//! Dapp Id type + +/// Dapplication Internal Id +#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct DappId(pub String); + +impl Into for DappId { + fn into(self) -> String { + self.0 + } +} + +#[cfg(test)] +mod tests { + + use serde_json; + use super::DappId; + + #[test] + fn should_serialize_dapp_id() { + // given + let id = DappId("testapp".into()); + + // when + let res = serde_json::to_string(&id).unwrap(); + + // then + assert_eq!(res, r#""testapp""#); + } + + #[test] + fn should_deserialize_dapp_id() { + // given + let id = r#""testapp""#; + + // when + let res: DappId = serde_json::from_str(id).unwrap(); + + // then + assert_eq!(res, DappId("testapp".into())); + } + + +} diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index c10d6e36f..282d70c27 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -19,6 +19,7 @@ mod block; mod block_number; mod call_request; mod confirmations; +mod dapp_id; mod filter; mod hash; mod index; @@ -39,6 +40,7 @@ pub use self::block::{RichBlock, Block, BlockTransactions}; pub use self::block_number::BlockNumber; pub use self::call_request::CallRequest; pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, TransactionModification, SignRequest, DecryptRequest, Either}; +pub use self::dapp_id::DappId; pub use self::filter::{Filter, FilterChanges}; pub use self::hash::{H64, H160, H256, H512, H520, H2048}; pub use self::index::Index; diff --git a/signer/Cargo.toml b/signer/Cargo.toml index 2a3742ec8..1b91e1a33 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -12,7 +12,7 @@ rustc_version = "0.1" [dependencies] rand = "0.3.14" -jsonrpc-core = "3.0" +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } log = "0.3" env_logger = "0.3" parity-dapps-glue = { version = "1.4", optional = true } diff --git a/signer/src/ws_server/session.rs b/signer/src/ws_server/session.rs index 5adc3fa80..0c9283f6d 100644 --- a/signer/src/ws_server/session.rs +++ b/signer/src/ws_server/session.rs @@ -21,8 +21,8 @@ use authcode_store::AuthCodes; use std::path::{PathBuf, Path}; use std::sync::Arc; use std::str::FromStr; -use jsonrpc_core::IoHandler; -use util::{H256, Mutex, version}; +use jsonrpc_core::{IoHandler, GenericIoHandler}; +use util::{H256, version}; #[cfg(feature = "parity-ui")] mod ui { @@ -130,7 +130,7 @@ fn add_headers(mut response: ws::Response, mime: &str) -> ws::Response { } pub struct Session { - out: Arc>, + out: ws::Sender, skip_origin_validation: bool, self_origin: String, authcodes_path: PathBuf, @@ -208,15 +208,15 @@ impl ws::Handler for Session { fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { let req = try!(msg.as_text()); - if let Some(async) = self.handler.handle_request(req) { - let out = self.out.clone(); - async.on_result(move |result| { - let res = out.lock().send(result); + let out = self.out.clone(); + self.handler.handle_request(req, move |response| { + if let Some(result) = response { + let res = out.send(result); if let Err(e) = res { warn!(target: "signer", "Error while sending response: {:?}", e); } - }); - } + } + }); Ok(()) } } @@ -246,7 +246,7 @@ impl ws::Factory for Factory { fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { Session { - out: Arc::new(Mutex::new(sender)), + out: sender, handler: self.handler.clone(), skip_origin_validation: self.skip_origin_validation, self_origin: self.self_origin.clone(), diff --git a/stratum/Cargo.toml b/stratum/Cargo.toml index d300106aa..28f5208dd 100644 --- a/stratum/Cargo.toml +++ b/stratum/Cargo.toml @@ -11,8 +11,8 @@ ethcore-ipc-codegen = { path = "../ipc/codegen" } [dependencies] log = "0.3" -json-tcp-server = { git = "https://github.com/ethcore/json-tcp-server" } -jsonrpc-core = "3.0" +jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" } +jsonrpc-tcp-server = { git = "https://github.com/ethcore/jsonrpc.git" } mio = { git = "https://github.com/ethcore/mio", branch = "v0.5.x" } ethcore-util = { path = "../util" } ethcore-devtools = { path = "../devtools" } diff --git a/stratum/src/lib.rs b/stratum/src/lib.rs index ecec535c2..0743fba6d 100644 --- a/stratum/src/lib.rs +++ b/stratum/src/lib.rs @@ -16,7 +16,7 @@ //! Stratum protocol implementation for parity ethereum/bitcoin clients -extern crate json_tcp_server; +extern crate jsonrpc_tcp_server; extern crate jsonrpc_core; #[macro_use] extern crate log; extern crate ethcore_util as util; @@ -44,7 +44,7 @@ pub use traits::{ RemoteWorkHandler, RemoteJobDispatcher, }; -use json_tcp_server::Server as JsonRpcServer; +use jsonrpc_tcp_server::Server as JsonRpcServer; use jsonrpc_core::{IoHandler, Params, IoDelegate, to_value, from_params}; use std::sync::Arc; From f080f33c4171fb30d4dcea1de1e576ec1c3eefc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 24 Nov 2016 11:22:26 +0100 Subject: [PATCH 037/155] JSON-RPC bump / update hyper Conflicts: Cargo.lock --- Cargo.lock | 78 ++++++++++++++++++++++------------- dapps/src/handlers/content.rs | 1 - dapps/src/handlers/mod.rs | 2 +- dapps/src/page/handler.rs | 3 +- 4 files changed, 51 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eaede16ba..61c36078a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,6 +174,15 @@ dependencies = [ "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cookie" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam" version = "0.2.9" @@ -295,7 +304,7 @@ dependencies = [ "ethstore 0.1.0", "evmjit 1.4.0", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", + "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -339,9 +348,9 @@ dependencies = [ "ethcore-rpc 1.5.0", "ethcore-util 1.5.0", "fetch 0.1.0", - "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", - "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)", + "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", + "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -685,7 +694,7 @@ name = "fetch" version = "0.1.0" dependencies = [ "https-fetch 0.1.0", - "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", + "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -747,27 +756,6 @@ dependencies = [ "rustls 0.1.2 (git+https://github.com/ctz/rustls)", ] -[[package]] -name = "hyper" -version = "0.9.4" -source = "git+https://github.com/ethcore/hyper#9e346c1d4bc30cd4142dea9d8a0b117d30858ca4" -dependencies = [ - "cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rotor 0.6.3 (git+https://github.com/ethcore/rotor)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "hyper" version = "0.9.10" @@ -788,6 +776,25 @@ dependencies = [ "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hyper" +version = "0.10.0-a.0" +source = "git+https://github.com/ethcore/hyper#7d4f7fa0baddcb2b0c523f7c05855d67de94fe88" +dependencies = [ + "cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rotor 0.6.3 (git+https://github.com/ethcore/rotor)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "spmc 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "vecio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "idna" version = "0.1.0" @@ -883,6 +890,17 @@ dependencies = [ "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "jsonrpc-http-server" +version = "6.1.1" +source = "git+https://github.com/ethcore/jsonrpc-http-server.git#cd6d4cb37d672cc3057aecd0692876f9e85f3ba5" +dependencies = [ + "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", + "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -1503,11 +1521,12 @@ dependencies = [ [[package]] name = "rotor" version = "0.6.3" -source = "git+https://github.com/ethcore/rotor#e63d45137b2eb66d1e085a7c6321a5db8b187576" +source = "git+https://github.com/ethcore/rotor#c1a2dd0046c5ea2517a5b637fca8ee2e77021e82" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)", + "mio 0.6.1 (git+https://github.com/ethcore/mio)", "quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2008,6 +2027,7 @@ dependencies = [ "checksum clippy 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)" = "6eacf01b0aad84a0817703498f72d252df7c0faf6a5b86d0be4265f1829e459f" "checksum clippy_lints 0.0.96 (registry+https://github.com/rust-lang/crates.io-index)" = "a49960c9aab544ce86b004dcb61620e8b898fea5fc0f697a028f460f48221ed6" "checksum cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90266f45846f14a1e986c77d1e9c2626b8c342ed806fe60241ec38cc8697b245" +"checksum cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d53b80dde876f47f03cda35303e368a79b91c70b0d65ecba5fd5280944a08591" "checksum crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "fb974f835e90390c5f9dfac00f05b06dc117299f5ea4e85fbc7bb443af4911cc" "checksum ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)" = "" "checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf" @@ -2025,8 +2045,8 @@ dependencies = [ "checksum heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "abb306abb8d398e053cfb1b3e7b72c2f580be048b85745c52652954f8ad1439c" "checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58" "checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae" +"checksum hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)" = "" "checksum hyper 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "eb27e8a3e8f17ac43ffa41bbda9cf5ad3f9f13ef66fa4873409d4902310275f7" -"checksum hyper 0.9.4 (git+https://github.com/ethcore/hyper)" = "" "checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11" "checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484" "checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c" diff --git a/dapps/src/handlers/content.rs b/dapps/src/handlers/content.rs index 738a9a890..fde5fbcf0 100644 --- a/dapps/src/handlers/content.rs +++ b/dapps/src/handlers/content.rs @@ -16,7 +16,6 @@ //! Simple Content Handler -use std::io::Write; use hyper::{header, server, Decoder, Encoder, Next}; use hyper::net::HttpStream; use hyper::mime::Mime; diff --git a/dapps/src/handlers/mod.rs b/dapps/src/handlers/mod.rs index b575509a5..1299a9c12 100644 --- a/dapps/src/handlers/mod.rs +++ b/dapps/src/handlers/mod.rs @@ -58,7 +58,7 @@ pub fn extract_url(req: &server::Request) -> Option { _ => None, } }, - uri::RequestUri::AbsolutePath(ref path) => { + uri::RequestUri::AbsolutePath { ref path, .. } => { // Attempt to prepend the Host header (mandatory in HTTP/1.1) let url_string = match req.headers().get::() { Some(ref host) => { diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs index 74eabf917..1494a04c7 100644 --- a/dapps/src/page/handler.rs +++ b/dapps/src/page/handler.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::io::Write; use time::{self, Duration}; use hyper::header; @@ -126,7 +125,7 @@ impl PageHandler { impl server::Handler for PageHandler { fn on_request(&mut self, req: server::Request) -> Next { self.file = match *req.uri() { - RequestUri::AbsolutePath(ref path) => { + RequestUri::AbsolutePath { ref path, .. } => { self.app.file(&self.extract_path(path)) }, RequestUri::AbsoluteUri(ref url) => { From 789d6608cf40bf667b59788499eeaf07fd18f0a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 25 Nov 2016 16:13:48 +0100 Subject: [PATCH 038/155] Bumping jsonrpc --- Cargo.lock | 44 +++++++++++++++++++--------------- dapps/src/lib.rs | 2 +- rpc/src/v1/tests/mocked/eth.rs | 3 +-- stratum/src/lib.rs | 2 +- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61c36078a..1cd3f87f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,8 +349,8 @@ dependencies = [ "ethcore-util 1.5.0", "fetch 0.1.0", "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc-http-server.git)", + "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", + "jsonrpc-http-server 6.1.1 (git+https://github.com/ethcore/jsonrpc.git)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -840,7 +840,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" version = "4.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" +source = "git+https://github.com/ethcore/jsonrpc.git#20c7e55b84d7fd62732f062dc3058e1b71133e4a" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -852,9 +852,9 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" version = "6.1.1" -source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" +source = "git+https://github.com/ethcore/jsonrpc.git#20c7e55b84d7fd62732f062dc3058e1b71133e4a" dependencies = [ - "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", + "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", "jsonrpc-core 4.0.0 (git+https://github.com/ethcore/jsonrpc.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -863,7 +863,7 @@ dependencies = [ [[package]] name = "jsonrpc-ipc-server" version = "0.2.4" -source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" +source = "git+https://github.com/ethcore/jsonrpc.git#20c7e55b84d7fd62732f062dc3058e1b71133e4a" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -878,7 +878,7 @@ dependencies = [ [[package]] name = "jsonrpc-tcp-server" version = "0.1.0" -source = "git+https://github.com/ethcore/jsonrpc.git#59919f9f0a2ebb675670b72430803605d868904c" +source = "git+https://github.com/ethcore/jsonrpc.git#20c7e55b84d7fd62732f062dc3058e1b71133e4a" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -890,17 +890,6 @@ dependencies = [ "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "jsonrpc-http-server" -version = "6.1.1" -source = "git+https://github.com/ethcore/jsonrpc-http-server.git#cd6d4cb37d672cc3057aecd0692876f9e85f3ba5" -dependencies = [ - "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "kernel32-sys" version = "0.2.2" @@ -1057,6 +1046,22 @@ dependencies = [ "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mio" +version = "0.6.1" +source = "git+https://github.com/ethcore/mio.git#ef182bae193a9c7457cd2cf661fcaffb226e3eef" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "miow" version = "0.1.3" @@ -1524,7 +1529,7 @@ version = "0.6.3" source = "git+https://github.com/ethcore/rotor#c1a2dd0046c5ea2517a5b637fca8ee2e77021e82" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.1 (git+https://github.com/ethcore/mio)", + "mio 0.6.1 (git+https://github.com/ethcore/mio.git)", "quick-error 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2074,6 +2079,7 @@ dependencies = [ "checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e" "checksum mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "" "checksum mio 0.6.1 (git+https://github.com/carllerche/mio)" = "" +"checksum mio 0.6.1 (git+https://github.com/ethcore/mio.git)" = "" "checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a" "checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8" "checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "" diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 7c7ea0a86..b185eb57a 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -266,7 +266,7 @@ impl Server { #[cfg(test)] /// Returns address that this server is bound to. pub fn addr(&self) -> &SocketAddr { - self.server.as_ref().expect("server is always Some at the start; it's consumed only when object is dropped; qed").addr() + &self.server.as_ref().expect("server is always Some at the start; it's consumed only when object is dropped; qed").addrs()[0] } } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 2f31aa4e1..2b5942af6 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -354,8 +354,7 @@ fn rpc_eth_gas_price() { #[test] fn rpc_eth_accounts() { let tester = EthTester::default(); - let address = tester.accounts_provider.new_account("").unwrap(); - let address2 = Address::default(); + let _address = tester.accounts_provider.new_account("").unwrap(); // even with some account it should return empty list (no dapp detected) let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; diff --git a/stratum/src/lib.rs b/stratum/src/lib.rs index 0743fba6d..45d8a3639 100644 --- a/stratum/src/lib.rs +++ b/stratum/src/lib.rs @@ -72,7 +72,7 @@ impl Stratum { addr: &SocketAddr, dispatcher: Arc, secret: Option, - ) -> Result, json_tcp_server::Error> { + ) -> Result, jsonrpc_tcp_server::Error> { let handler = Arc::new(IoHandler::new()); let server = try!(JsonRpcServer::new(addr, &handler)); let stratum = Arc::new(Stratum { From 3b6d886860cbe89736ed4448d992b3b20572d990 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 27 Nov 2016 14:11:37 +0100 Subject: [PATCH 039/155] 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 040/155] 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 3e69ff0b887fc14efb463def8d7dd3806cd767ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 28 Nov 2016 11:41:46 +0100 Subject: [PATCH 041/155] Separating serialization of send and signTransaction --- rpc/src/v1/tests/mocked/signer.rs | 2 +- rpc/src/v1/types/confirmations.rs | 48 +++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index e2ba580e0..447c809cd 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -89,7 +89,7 @@ fn should_return_list_of_items_to_confirm() { let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; let response = concat!( r#"{"jsonrpc":"2.0","result":["#, - r#"{"id":"0x1","payload":{"transaction":{"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, + r#"{"id":"0x1","payload":{"sendTransaction":{"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#, r#"{"id":"0x2","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","hash":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#, r#"],"id":1}"# ); diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index 2b7813df9..5b396725b 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -105,10 +105,10 @@ impl Serialize for ConfirmationResponse { #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)] pub enum ConfirmationPayload { /// Send Transaction - #[serde(rename="transaction")] + #[serde(rename="sendTransaction")] SendTransaction(TransactionRequest), /// Sign Transaction - #[serde(rename="transaction")] + #[serde(rename="signTransaction")] SignTransaction(TransactionRequest), /// Signature #[serde(rename="sign")] @@ -220,7 +220,49 @@ mod tests { // when let res = serde_json::to_string(&ConfirmationRequest::from(request)); - let expected = r#"{"id":"0xf","payload":{"transaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1"}}}"#; + let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1"}}}"#; + + // then + assert_eq!(res.unwrap(), expected.to_owned()); + } + + #[test] + fn should_serialize_sign_transaction_confirmation() { + // given + let request = helpers::ConfirmationRequest { + id: 15.into(), + payload: helpers::ConfirmationPayload::SignTransaction(helpers::FilledTransactionRequest { + from: 0.into(), + to: None, + gas: 15_000.into(), + gas_price: 10_000.into(), + value: 100_000.into(), + data: vec![1, 2, 3], + nonce: Some(1.into()), + }), + }; + + // when + let res = serde_json::to_string(&ConfirmationRequest::from(request)); + let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1"}}}"#; + + // then + assert_eq!(res.unwrap(), expected.to_owned()); + } + + #[test] + fn should_serialize_decrypt_confirmation() { + // given + let request = helpers::ConfirmationRequest { + id: 15.into(), + payload: helpers::ConfirmationPayload::Decrypt( + 10.into(), vec![1, 2, 3].into(), + ), + }; + + // when + let res = serde_json::to_string(&ConfirmationRequest::from(request)); + let expected = r#"{"id":"0xf","payload":{"decrypt":{"address":"0x000000000000000000000000000000000000000a","msg":"0x010203"}}}"#; // then assert_eq!(res.unwrap(), expected.to_owned()); From 8686339b0c742a7b79752cbf3647c644791569fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 28 Nov 2016 11:55:29 +0100 Subject: [PATCH 042/155] Fixing UI to support both send and signTransaction --- js/src/api/format/output.js | 3 ++- js/src/redux/providers/signerMiddleware.js | 4 ++-- .../Signer/components/RequestFinished/requestFinished.js | 7 ++++--- .../Signer/components/RequestPending/requestPending.js | 7 ++++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/js/src/api/format/output.js b/js/src/api/format/output.js index 262a275a0..1094cdb83 100644 --- a/js/src/api/format/output.js +++ b/js/src/api/format/output.js @@ -144,7 +144,8 @@ export function outSignerRequest (request) { break; case 'payload': - request[key].transaction = outTransaction(request[key].transaction); + request[key].signTransaction = outTransaction(request[key].signTransaction); + request[key].sendTransaction = outTransaction(request[key].sendTransaction); break; } }); diff --git a/js/src/redux/providers/signerMiddleware.js b/js/src/redux/providers/signerMiddleware.js index 4cc877ced..2af1bfe65 100644 --- a/js/src/redux/providers/signerMiddleware.js +++ b/js/src/redux/providers/signerMiddleware.js @@ -72,8 +72,8 @@ export default class SignerMiddleware { }; // Sign request in-browser - if (wallet && payload.transaction) { - const { transaction } = payload; + if (wallet && (payload.sendTransaction || payload.signTransaction)) { + const transaction = payload.sendTransaction || payload.signTransaction; (transaction.nonce.isZero() ? this._api.parity.nextNonce(transaction.from) diff --git a/js/src/views/Signer/components/RequestFinished/requestFinished.js b/js/src/views/Signer/components/RequestFinished/requestFinished.js index bce9e4038..edc5c4a47 100644 --- a/js/src/views/Signer/components/RequestFinished/requestFinished.js +++ b/js/src/views/Signer/components/RequestFinished/requestFinished.js @@ -25,7 +25,8 @@ export default class RequestFinished extends Component { result: PropTypes.any.isRequired, date: PropTypes.instanceOf(Date).isRequired, payload: PropTypes.oneOfType([ - PropTypes.shape({ transaction: PropTypes.object.isRequired }), + PropTypes.shape({ signTransaction: PropTypes.object.isRequired }), + PropTypes.shape({ sendTransaction: PropTypes.object.isRequired }), PropTypes.shape({ sign: PropTypes.object.isRequired }) ]).isRequired, msg: PropTypes.string, @@ -58,8 +59,8 @@ export default class RequestFinished extends Component { ); } - if (payload.transaction) { - const { transaction } = payload; + if (payload.sendTransaction || payload.signTransaction) { + const transaction = payload.sendTransaction || payload.signTransaction; return ( Date: Tue, 29 Nov 2016 11:52:55 +0100 Subject: [PATCH 043/155] Addressing tiny grumbles --- js/src/redux/providers/signerMiddleware.js | 5 ++--- .../Signer/components/RequestFinished/requestFinished.js | 5 ++--- .../views/Signer/components/RequestPending/requestPending.js | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/js/src/redux/providers/signerMiddleware.js b/js/src/redux/providers/signerMiddleware.js index 2af1bfe65..f5cbbd8f9 100644 --- a/js/src/redux/providers/signerMiddleware.js +++ b/js/src/redux/providers/signerMiddleware.js @@ -72,9 +72,8 @@ export default class SignerMiddleware { }; // Sign request in-browser - if (wallet && (payload.sendTransaction || payload.signTransaction)) { - const transaction = payload.sendTransaction || payload.signTransaction; - + const transaction = payload.sendTransaction || payload.signTransaction; + if (wallet && transaction) { (transaction.nonce.isZero() ? this._api.parity.nextNonce(transaction.from) : Promise.resolve(transaction.nonce) diff --git a/js/src/views/Signer/components/RequestFinished/requestFinished.js b/js/src/views/Signer/components/RequestFinished/requestFinished.js index edc5c4a47..fcca55540 100644 --- a/js/src/views/Signer/components/RequestFinished/requestFinished.js +++ b/js/src/views/Signer/components/RequestFinished/requestFinished.js @@ -59,9 +59,8 @@ export default class RequestFinished extends Component { ); } - if (payload.sendTransaction || payload.signTransaction) { - const transaction = payload.sendTransaction || payload.signTransaction; - + const transaction = payload.sendTransaction || payload.signTransaction; + if (transaction) { return ( Date: Tue, 29 Nov 2016 11:58:00 +0100 Subject: [PATCH 044/155] DRY-er Container with title --- js/src/views/Contract/Events/events.js | 5 ++--- js/src/views/Contract/Queries/queries.js | 5 ++--- js/src/views/Settings/Background/background.js | 5 ++--- js/src/views/Settings/Parity/parity.js | 5 ++--- js/src/views/Settings/Proxy/proxy.js | 5 ++--- js/src/views/Settings/Views/views.js | 5 ++--- js/src/views/Status/components/Debug/Debug.js | 6 ++---- 7 files changed, 14 insertions(+), 22 deletions(-) diff --git a/js/src/views/Contract/Events/events.js b/js/src/views/Contract/Events/events.js index 382a1c658..5558be499 100644 --- a/js/src/views/Contract/Events/events.js +++ b/js/src/views/Contract/Events/events.js @@ -16,7 +16,7 @@ import React, { Component, PropTypes } from 'react'; -import { Container, ContainerTitle } from '../../../ui'; +import { Container } from '../../../ui'; import Event from './Event'; import styles from '../contract.css'; @@ -48,8 +48,7 @@ export default class Events extends Component { }); return ( - - + { list }
diff --git a/js/src/views/Contract/Queries/queries.js b/js/src/views/Contract/Queries/queries.js index 99fe9ff2a..5c69ab76e 100644 --- a/js/src/views/Contract/Queries/queries.js +++ b/js/src/views/Contract/Queries/queries.js @@ -19,7 +19,7 @@ import React, { Component, PropTypes } from 'react'; import { Card, CardTitle, CardText } from 'material-ui/Card'; import InputQuery from './inputQuery'; -import { Container, ContainerTitle, Input, InputAddress } from '../../../ui'; +import { Container, Input, InputAddress } from '../../../ui'; import styles from './queries.css'; @@ -55,8 +55,7 @@ export default class Queries extends Component { .map((fn) => this.renderInputQuery(fn)); return ( - - +
{ noInputQueries } diff --git a/js/src/views/Settings/Background/background.js b/js/src/views/Settings/Background/background.js index 4b210881a..d771c4239 100644 --- a/js/src/views/Settings/Background/background.js +++ b/js/src/views/Settings/Background/background.js @@ -19,7 +19,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import NavigationRefresh from 'material-ui/svg-icons/navigation/refresh'; -import { Button, Container, ContainerTitle, ParityBackground } from '../../../ui'; +import { Button, Container, ParityBackground } from '../../../ui'; import { updateBackground } from '../actions'; @@ -55,8 +55,7 @@ class Background extends Component { render () { return ( - - +
The background pattern you can see right now is unique to your Parity installation. It will change every time you create a new Signer token. This is so that decentralized applications cannot pretend to be trustworthy.
diff --git a/js/src/views/Settings/Parity/parity.js b/js/src/views/Settings/Parity/parity.js index abec8cc8a..e5e5233d0 100644 --- a/js/src/views/Settings/Parity/parity.js +++ b/js/src/views/Settings/Parity/parity.js @@ -17,7 +17,7 @@ import React, { Component, PropTypes } from 'react'; import { MenuItem } from 'material-ui'; -import { Select, Container, ContainerTitle } from '../../../ui'; +import { Select, Container } from '../../../ui'; import layout from '../layout.css'; @@ -43,8 +43,7 @@ export default class Parity extends Component { render () { return ( - - +
Control the Parity node settings and mode of operation via this interface.
diff --git a/js/src/views/Settings/Proxy/proxy.js b/js/src/views/Settings/Proxy/proxy.js index 69e415d1f..3d2a607bc 100644 --- a/js/src/views/Settings/Proxy/proxy.js +++ b/js/src/views/Settings/Proxy/proxy.js @@ -16,7 +16,7 @@ import React, { Component, PropTypes } from 'react'; -import { Container, ContainerTitle } from '../../../ui'; +import { Container } from '../../../ui'; import layout from '../layout.css'; import styles from './proxy.css'; @@ -31,8 +31,7 @@ export default class Proxy extends Component { const proxyurl = `${dappsUrl}/proxy/proxy.pac`; return ( - - +
The proxy setup allows you to access Parity and all associated decentralized applications via memorable addresses.
diff --git a/js/src/views/Settings/Views/views.js b/js/src/views/Settings/Views/views.js index a485876c8..e5fdedf5c 100644 --- a/js/src/views/Settings/Views/views.js +++ b/js/src/views/Settings/Views/views.js @@ -19,7 +19,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { Checkbox } from 'material-ui'; -import { Container, ContainerTitle } from '../../../ui'; +import { Container } from '../../../ui'; import { toggleView } from '../actions'; @@ -34,8 +34,7 @@ class Views extends Component { render () { return ( - - +
Manage the available application views, using only the parts of the application that is applicable to you.
diff --git a/js/src/views/Status/components/Debug/Debug.js b/js/src/views/Status/components/Debug/Debug.js index 654662b84..d0e0c1d79 100644 --- a/js/src/views/Status/components/Debug/Debug.js +++ b/js/src/views/Status/components/Debug/Debug.js @@ -20,7 +20,7 @@ import AvPlay from 'material-ui/svg-icons/av/play-arrow'; import AvReplay from 'material-ui/svg-icons/av/replay'; import ReorderIcon from 'material-ui/svg-icons/action/reorder'; -import { Container, ContainerTitle } from '../../../../ui'; +import { Container } from '../../../../ui'; import styles from './Debug.css'; @@ -42,9 +42,7 @@ export default class Debug extends Component { const { devLogsLevels } = nodeStatus; return ( - - + { this.renderActions() }

{ devLogsLevels || '-' } From 436016ef026c9444f8e2b1fcb1fd0df1255c462d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 29 Nov 2016 13:46:06 +0100 Subject: [PATCH 045/155] 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 046/155] 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 6a69e22b288bcff03f9727ca7fcd7738d3ba5739 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 29 Nov 2016 16:36:54 +0100 Subject: [PATCH 047/155] Methods use either input or data elements --- js/src/ui/MethodDecoding/methodDecoding.css | 4 ++-- js/src/ui/MethodDecoding/methodDecoding.js | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/js/src/ui/MethodDecoding/methodDecoding.css b/js/src/ui/MethodDecoding/methodDecoding.css index 2de91cf9b..48070c45f 100644 --- a/js/src/ui/MethodDecoding/methodDecoding.css +++ b/js/src/ui/MethodDecoding/methodDecoding.css @@ -30,9 +30,9 @@ } .gasDetails { - padding-top: 1em; + padding-top: 0.75em; font-size: 0.75em; - line-height: 2em; + line-height: 1.5em; opacity: 0.5; } diff --git a/js/src/ui/MethodDecoding/methodDecoding.js b/js/src/ui/MethodDecoding/methodDecoding.js index d446a19b4..e82139adf 100644 --- a/js/src/ui/MethodDecoding/methodDecoding.js +++ b/js/src/ui/MethodDecoding/methodDecoding.js @@ -135,21 +135,23 @@ class MethodDecoding extends Component { renderInputValue () { const { api } = this.context; const { transaction } = this.props; + const input = transaction.input || transaction.data; - if (!/^(0x)?([0]*[1-9a-f]+[0]*)+$/.test(transaction.input)) { + if (!/^(0x)?([0]*[1-9a-f]+[0]*)+$/.test(input)) { return null; } - const ascii = api.util.hex2Ascii(transaction.input); - + const ascii = api.util.hex2Ascii(input); const text = ASCII_INPUT.test(ascii) ? ascii - : transaction.input; + : input; return ( -
- with the input   - { text } +
+
+ with the input   + { text } +
); } @@ -385,11 +387,12 @@ class MethodDecoding extends Component { const isReceived = transaction.to === address; const contractAddress = isReceived ? transaction.from : transaction.to; + const input = transaction.input || transaction.data; const token = (tokens || {})[contractAddress]; this.setState({ token, isReceived, contractAddress }); - if (!transaction.input || transaction.input === '0x') { + if (!input || input === '0x') { return; } @@ -408,7 +411,7 @@ class MethodDecoding extends Component { return; } - const { signature, paramdata } = api.util.decodeCallData(transaction.input); + const { signature, paramdata } = api.util.decodeCallData(input); this.setState({ methodSignature: signature, methodParams: paramdata }); if (!signature || signature === CONTRACT_CREATE || transaction.creates) { From efd4e6f96af73f0b82f24a1d684acbc81e547081 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 29 Nov 2016 16:37:25 +0100 Subject: [PATCH 048/155] Pass MethodDecoding as to/destination view --- js/src/views/Signer/_layout.css | 2 +- .../RequestPending/requestPending.js | 7 +-- .../TransactionMainDetails.css | 37 ++++++----- .../TransactionMainDetails.js | 61 +++---------------- .../TransactionPending/TransactionPending.css | 6 +- .../TransactionPending/TransactionPending.js | 43 +++++++------ .../TransactionPendingForm.css | 2 +- 7 files changed, 56 insertions(+), 102 deletions(-) diff --git a/js/src/views/Signer/_layout.css b/js/src/views/Signer/_layout.css index 3970a7e02..40e5df3e8 100644 --- a/js/src/views/Signer/_layout.css +++ b/js/src/views/Signer/_layout.css @@ -19,6 +19,6 @@ $pendingHeight: 190px; $finishedHeight: 120px; $embedWidth: 920px; -$statusWidth: 260px; +$statusWidth: 270px; $accountPadding: 75px; diff --git a/js/src/views/Signer/components/RequestPending/requestPending.js b/js/src/views/Signer/components/RequestPending/requestPending.js index d8e2e0565..aa797f526 100644 --- a/js/src/views/Signer/components/RequestPending/requestPending.js +++ b/js/src/views/Signer/components/RequestPending/requestPending.js @@ -74,12 +74,7 @@ export default class RequestPending extends Component { onReject={ onReject } isSending={ isSending } id={ id } - gasPrice={ transaction.gasPrice } - gas={ transaction.gas } - data={ transaction.data } - from={ transaction.from } - to={ transaction.to } - value={ transaction.value } + transaction={ transaction } date={ date } isTest={ isTest } store={ store } diff --git a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.css b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.css index 107694b8e..92ed968d1 100644 --- a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.css +++ b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.css @@ -30,27 +30,26 @@ text-align: center; } -.from, .to { - width: 50%; +.from { + width: 40%; + vertical-align: top; + + img { + display: inline-block; + width: 50px; + height: 50px; + margin: 5px; + } + + span { + display: block; + } } -.from .account { - padding-right: $accountPadding; -} - -.to .account { - padding-left: $accountPadding; -} - -.from img, .to img { - display: inline-block; - width: 50px; - height: 50px; - margin: 5px; -} - -.from span, .to span { - display: block; +.method { + width: 60%; + vertical-align: top; + line-height: 1em; } .tx { diff --git a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js index 6e83e0c7e..fb10392d3 100644 --- a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js +++ b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js @@ -16,9 +16,10 @@ import React, { Component, PropTypes } from 'react'; -import ContractIcon from 'material-ui/svg-icons/action/code'; import ReactTooltip from 'react-tooltip'; +import { MethodDecoding } from '../../../../ui'; + import * as tUtil from '../util/transaction'; import Account from '../Account'; import styles from './TransactionMainDetails.css'; @@ -32,6 +33,7 @@ export default class TransactionMainDetails extends Component { totalValue: PropTypes.object.isRequired, // wei BigNumber isTest: PropTypes.bool.isRequired, to: PropTypes.string, // undefined if it's a contract + transaction: PropTypes.object.isRequired, toBalance: PropTypes.object, // eth BigNumber - undefined if it's a contract or until it's fetched children: PropTypes.node }; @@ -59,15 +61,7 @@ export default class TransactionMainDetails extends Component { } render () { - const { to } = this.props; - - return to - ? this.renderTransfer() - : this.renderContract(); - } - - renderTransfer () { - const { children, from, fromBalance, to, toBalance, isTest } = this.props; + const { children, from, fromBalance, transaction, isTest } = this.props; return (
@@ -79,48 +73,11 @@ export default class TransactionMainDetails extends Component { isTest={ isTest } />
-
- { this.renderValue() } -
- { this.renderTotalValue() } -
-
-
- -
-
- { children } -

- ); - } - - renderContract () { - const { children, from, fromBalance, isTest } = this.props; - - return ( -
-
-
- -
-
-
- { this.renderValue() } -
- { this.renderTotalValue() } -
-
-
- -
- Contract -
+
+
{ children }
diff --git a/js/src/views/Signer/components/TransactionPending/TransactionPending.css b/js/src/views/Signer/components/TransactionPending/TransactionPending.css index 5cd2d10f5..877066e57 100644 --- a/js/src/views/Signer/components/TransactionPending/TransactionPending.css +++ b/js/src/views/Signer/components/TransactionPending/TransactionPending.css @@ -19,9 +19,13 @@ .container { display: flex; - padding: 1.5em 0 1em; + padding: 1em 0 1em; & > * { vertical-align: middle; } } + +.container+.container { + padding-top: 2em; +} diff --git a/js/src/views/Signer/components/TransactionPending/TransactionPending.js b/js/src/views/Signer/components/TransactionPending/TransactionPending.js index 013d887a5..98c83be3b 100644 --- a/js/src/views/Signer/components/TransactionPending/TransactionPending.js +++ b/js/src/views/Signer/components/TransactionPending/TransactionPending.js @@ -19,7 +19,6 @@ import { observer } from 'mobx-react'; import TransactionMainDetails from '../TransactionMainDetails'; import TransactionPendingForm from '../TransactionPendingForm'; -import TransactionSecondaryDetails from '../TransactionSecondaryDetails'; import styles from './TransactionPending.css'; @@ -29,13 +28,15 @@ import * as tUtil from '../util/transaction'; export default class TransactionPending extends Component { static propTypes = { id: PropTypes.object.isRequired, - from: PropTypes.string.isRequired, - value: PropTypes.object.isRequired, // wei hex - gasPrice: PropTypes.object.isRequired, // wei hex - gas: PropTypes.object.isRequired, // hex + transaction: PropTypes.shape({ + from: PropTypes.string.isRequired, + value: PropTypes.object.isRequired, // wei hex + gasPrice: PropTypes.object.isRequired, // wei hex + gas: PropTypes.object.isRequired, // hex + data: PropTypes.string, // hex + to: PropTypes.string // undefined if it's a contract + }).isRequired, date: PropTypes.instanceOf(Date).isRequired, - to: PropTypes.string, // undefined if it's a contract - data: PropTypes.string, // hex nonce: PropTypes.number, onConfirm: PropTypes.func.isRequired, onReject: PropTypes.func.isRequired, @@ -50,7 +51,8 @@ export default class TransactionPending extends Component { }; componentWillMount () { - const { gas, gasPrice, value, from, to, store } = this.props; + const { transaction, store } = this.props; + const { gas, gasPrice, value, from, to } = transaction; const fee = tUtil.getFee(gas, gasPrice); // BigNumber object const totalValue = tUtil.getTotalValue(fee, value); @@ -62,8 +64,9 @@ export default class TransactionPending extends Component { } render () { - const { className, id, date, data, from, to, store } = this.props; - const { totalValue, gasPriceEthmDisplay, gasToDisplay } = this.state; + const { className, id, transaction, store } = this.props; + const { from, to, value } = transaction; + const { totalValue } = this.state; const fromBalance = store.balances[from]; const toBalance = store.balances[to]; @@ -71,20 +74,15 @@ export default class TransactionPending extends Component { return (
- - + transaction={ transaction } + totalValue={ totalValue } /> { - const { id, gasPrice } = this.props; + const { id, transaction } = this.props; + const { gasPrice } = transaction; const { password, wallet } = data; this.props.onConfirm({ id, password, wallet, gasPrice }); diff --git a/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingForm.css b/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingForm.css index aaea1de8d..feb456bce 100644 --- a/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingForm.css +++ b/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingForm.css @@ -19,7 +19,7 @@ .container { box-sizing: border-box; - padding: 1em 1em 0 1em; + padding: 1em 0 0 2em; flex: 0 0 $statusWidth; } From 0ddd33c643a99be51f32ff02859fe953811092b0 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 29 Nov 2016 16:55:59 +0100 Subject: [PATCH 049/155] Trim unused components --- .../TransactionFinished.css | 54 ------ .../TransactionFinished.js | 120 ------------ .../components/TransactionFinished/index.js | 17 -- .../TransactionMainDetails.js | 2 - .../TransactionPending/TransactionPending.js | 5 +- .../TransactionSecondaryDetails.css | 90 --------- .../TransactionSecondaryDetails.js | 178 ------------------ .../TransactionSecondaryDetails/index.js | 1 - .../containers/RequestsPage/RequestsPage.js | 42 +---- 9 files changed, 2 insertions(+), 507 deletions(-) delete mode 100644 js/src/views/Signer/components/TransactionFinished/TransactionFinished.css delete mode 100644 js/src/views/Signer/components/TransactionFinished/TransactionFinished.js delete mode 100644 js/src/views/Signer/components/TransactionFinished/index.js delete mode 100644 js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.css delete mode 100644 js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.js delete mode 100644 js/src/views/Signer/components/TransactionSecondaryDetails/index.js diff --git a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.css b/js/src/views/Signer/components/TransactionFinished/TransactionFinished.css deleted file mode 100644 index 3617f666a..000000000 --- a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.css +++ /dev/null @@ -1,54 +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 . -*/ - -@import '../../_layout.css'; - -.container { - display: flex; - padding: 1.5em 0 1em; - - & > * { - vertical-align: middle; - min-height: $finishedHeight; - } -} - -.statusContainer { - box-sizing: border-box; - float: right; - padding: 0 1em; - flex: 0 0 $statusWidth; -} - -.transactionDetails { - width: 100%; - box-sizing: border-box; -} - -.isConfirmed { - color: green; -} - -.isRejected { - opacity: 0.5; - padding-top: 2em; -} - -.txHash { - display: block; - word-break: break-all; -} diff --git a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js b/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js deleted file mode 100644 index f24481e57..000000000 --- a/js/src/views/Signer/components/TransactionFinished/TransactionFinished.js +++ /dev/null @@ -1,120 +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 . - -import React, { Component, PropTypes } from 'react'; -import { observer } from 'mobx-react'; - -import { TxHash } from '../../../../ui'; - -import TransactionMainDetails from '../TransactionMainDetails'; -import TxHashLink from '../TxHashLink'; -import TransactionSecondaryDetails from '../TransactionSecondaryDetails'; - -import styles from './TransactionFinished.css'; - -import * as tUtil from '../util/transaction'; -import { capitalize } from '../util/util'; - -@observer -export default class TransactionFinished extends Component { - static propTypes = { - id: PropTypes.object.isRequired, - from: PropTypes.string.isRequired, - value: PropTypes.object.isRequired, // wei hex - gasPrice: PropTypes.object.isRequired, // wei hex - gas: PropTypes.object.isRequired, // hex - status: PropTypes.string.isRequired, // rejected, confirmed - date: PropTypes.instanceOf(Date).isRequired, - to: PropTypes.string, // undefined if it's a contract - txHash: PropTypes.string, // undefined if transacation is rejected - className: PropTypes.string, - data: PropTypes.string, - isTest: PropTypes.bool.isRequired, - store: PropTypes.object.isRequired - }; - - componentWillMount () { - const { from, to, gas, gasPrice, value, store } = this.props; - const fee = tUtil.getFee(gas, gasPrice); // BigNumber object - const totalValue = tUtil.getTotalValue(fee, value); - - this.setState({ totalValue }); - store.fetchBalances([from, to]); - } - - render () { - const { className, date, id, from, to, store } = this.props; - - const fromBalance = store.balances[from]; - const toBalance = store.balances[to]; - - return ( -
- - - -
- { this.renderStatus() } -
-
- ); - } - - renderStatus () { - const { status, txHash } = this.props; - - if (status !== 'confirmed') { - return ( -
- { capitalize(status) } -
- ); - } - - return ( - - ); - } - - renderTxHash () { - const { txHash, isTest } = this.props; - - if (!txHash) { - return; - } - - return ( -
- Transaction hash: - -
- ); - } -} diff --git a/js/src/views/Signer/components/TransactionFinished/index.js b/js/src/views/Signer/components/TransactionFinished/index.js deleted file mode 100644 index fe606e1d4..000000000 --- a/js/src/views/Signer/components/TransactionFinished/index.js +++ /dev/null @@ -1,17 +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 . - -export default from './TransactionFinished'; diff --git a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js index fb10392d3..ca2061791 100644 --- a/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js +++ b/js/src/views/Signer/components/TransactionMainDetails/TransactionMainDetails.js @@ -32,9 +32,7 @@ export default class TransactionMainDetails extends Component { value: PropTypes.object.isRequired, // wei hex totalValue: PropTypes.object.isRequired, // wei BigNumber isTest: PropTypes.bool.isRequired, - to: PropTypes.string, // undefined if it's a contract transaction: PropTypes.object.isRequired, - toBalance: PropTypes.object, // eth BigNumber - undefined if it's a contract or until it's fetched children: PropTypes.node }; diff --git a/js/src/views/Signer/components/TransactionPending/TransactionPending.js b/js/src/views/Signer/components/TransactionPending/TransactionPending.js index 98c83be3b..4fba02826 100644 --- a/js/src/views/Signer/components/TransactionPending/TransactionPending.js +++ b/js/src/views/Signer/components/TransactionPending/TransactionPending.js @@ -65,11 +65,10 @@ export default class TransactionPending extends Component { render () { const { className, id, transaction, store } = this.props; - const { from, to, value } = transaction; + const { from, value } = transaction; const { totalValue } = this.state; const fromBalance = store.balances[from]; - const toBalance = store.balances[to]; return (
@@ -78,8 +77,6 @@ export default class TransactionPending extends Component { value={ value } from={ from } fromBalance={ fromBalance } - to={ to } - toBalance={ toBalance } className={ styles.transactionDetails } transaction={ transaction } totalValue={ totalValue } /> diff --git a/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.css b/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.css deleted file mode 100644 index ae71b1004..000000000 --- a/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.css +++ /dev/null @@ -1,90 +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 . -*/ - -.container { - display: block; -} - -.iconsContainer { - display: block; - text-align: center; - font-size: .8em; - opacity: 0.5; - padding: 1em 0 0 0; -} - -.iconsContainer > * { - margin-right: 3px; - display: inline-block; -} - -.iconsContainer:after { - clear: both; -} - -.hasInfoIcon svg, -.miningTime svg, -.gasPrice svg, -.data svg, -.date svg { - width: 16px !important; - height: 16px !important; - position: relative; - bottom: -3px; - margin: 0 0.25rem 0 0.75rem; -} - -/* TODO [ToDr] composes was handling weird errors when linking from other app */ -.miningTime { - /* composes: hasInfoIcon; */ -} - -.gasPrice { - /* composes: hasInfoIcon; */ -} - -.data { - /* composes: hasInfoIcon; */ - cursor: pointer; -} - -.data.noData { - cursor: text; -} - - -.dataTooltip { - word-wrap: break-word; - max-width: 400px; -} - -.expandedData { - display: block; - padding: 15px; - background: gray; - word-wrap: break-word; - color: #fff; -} - -.expandedContainer { - padding: 10px; - border-radius: 4px; -} - -.expandedContainer:empty { - padding: 0; -} diff --git a/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.js b/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.js deleted file mode 100644 index 283712ed0..000000000 --- a/js/src/views/Signer/components/TransactionSecondaryDetails/TransactionSecondaryDetails.js +++ /dev/null @@ -1,178 +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 . - -import React, { Component, PropTypes } from 'react'; - -import ReactTooltip from 'react-tooltip'; -import DescriptionIcon from 'material-ui/svg-icons/action/description'; -import GasIcon from 'material-ui/svg-icons/maps/local-gas-station'; -import TimeIcon from 'material-ui/svg-icons/device/access-time'; -import moment from 'moment'; - -import styles from './TransactionSecondaryDetails.css'; - -import * as tUtil from '../util/transaction'; - -export default class TransactionSecondaryDetails extends Component { - static propTypes = { - id: PropTypes.object.isRequired, - date: PropTypes.instanceOf(Date), - data: PropTypes.string, // hex - gasPriceEthmDisplay: PropTypes.string, - gasToDisplay: PropTypes.string, - className: PropTypes.string - }; - - state = { - isDataExpanded: false - }; - - render () { - const className = this.props.className || ''; - - return ( -
-
- { this.renderGasPrice() } - { this.renderData() } - { this.renderDate() } -
-
- { this.renderDataExpanded() } -
-
- ); - } - - renderGasPrice () { - if (!this.props.gasPriceEthmDisplay && !this.props.gasToDisplay) { - return null; - } - - const { id } = this.props; - const { gasPriceEthmDisplay, gasToDisplay } = this.props; - - return ( -
- - - { gasPriceEthmDisplay } ETH/MGAS - - { /* dynamic id required in case there are multple transactions in page */ } - - Cost of 1,000,000 units of gas. This transaction will use up to { gasToDisplay } MGAS. - -
- ); - } - - renderData () { - if (!this.props.data) { - return null; - } - - const { data, id } = this.props; - let dataToDisplay = this.noData() ? 'no data' : tUtil.getShortData(data); - const noDataClass = this.noData() ? styles.noData : ''; - - return ( -
- - { dataToDisplay } - { /* dynamic id required in case there are multple transactions in page */ } - - Extra data for the transaction: -
- { dataToDisplay }. -
- { this.noData() ? '' : Click to expand. } -
-
- ); - } - - renderDate () { - const { date, id } = this.props; - - const dateToDisplay = moment(date).fromNow(); - const fullDate = moment(date).format('LL LTS'); - - return ( -
- - { dateToDisplay } - { /* dynamic id required in case there are multple transactions in page */ } - - Date of the request: -
- { fullDate } -
-
- ); - } - - renderDataExpanded () { - if (!this.props.data) return null; - - const { isDataExpanded } = this.state; - const { data } = this.props; - - if (!isDataExpanded) { - return; - } - - return ( -
-

Transaction's Data

- { data } -
- ); - } - - noData () { - return this.props.data === '0x'; - } - - toggleDataExpanded = () => { - if (this.noData()) { - return; - } - this.setState({ - isDataExpanded: !this.state.isDataExpanded - }); - } - -} diff --git a/js/src/views/Signer/components/TransactionSecondaryDetails/index.js b/js/src/views/Signer/components/TransactionSecondaryDetails/index.js deleted file mode 100644 index 4b352c41a..000000000 --- a/js/src/views/Signer/components/TransactionSecondaryDetails/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './TransactionSecondaryDetails'; diff --git a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js index ae2ba05fb..a15cb537d 100644 --- a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js +++ b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js @@ -24,7 +24,7 @@ import Store from '../../store'; import * as RequestsActions from '../../../../redux/providers/signerActions'; import { Container, Page, TxList } from '../../../../ui'; -import { RequestPending, RequestFinished } from '../../components'; +import { RequestPending } from '../../components'; import styles from './RequestsPage.css'; @@ -57,7 +57,6 @@ class RequestsPage extends Component {
{ this.renderPendingRequests() }
{ this.renderLocalQueue() }
-
{ this.renderFinishedRequests() }
); } @@ -106,24 +105,6 @@ class RequestsPage extends Component { ); } - renderFinishedRequests () { - const { finished } = this.props.signer; - - if (!finished.length) { - return; - } - - const items = finished.sort(this._sortRequests).map(this.renderFinished); - - return ( - -
- { items } -
-
- ); - } - renderPending = (data) => { const { actions, isTest } = this.props; const { payload, id, isSending, date } = data; @@ -143,27 +124,6 @@ class RequestsPage extends Component { /> ); } - - renderFinished = (data) => { - const { isTest } = this.props; - const { payload, id, result, msg, status, error, date } = data; - - return ( - - ); - } } function mapStateToProps (state) { From 7e2a072a2b8e183784482e62a7abfa02f36fae3b Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 29 Nov 2016 16:59:44 +0100 Subject: [PATCH 050/155] Remove finished rendering completely --- .../components/RequestFinished/index.js | 17 ---- .../RequestFinished/requestFinished.js | 87 ------------------- js/src/views/Signer/components/index.js | 18 ---- .../Signer/containers/Embedded/embedded.js | 2 +- .../containers/RequestsPage/RequestsPage.js | 2 +- 5 files changed, 2 insertions(+), 124 deletions(-) delete mode 100644 js/src/views/Signer/components/RequestFinished/index.js delete mode 100644 js/src/views/Signer/components/RequestFinished/requestFinished.js delete mode 100644 js/src/views/Signer/components/index.js diff --git a/js/src/views/Signer/components/RequestFinished/index.js b/js/src/views/Signer/components/RequestFinished/index.js deleted file mode 100644 index c5ed83b6b..000000000 --- a/js/src/views/Signer/components/RequestFinished/index.js +++ /dev/null @@ -1,17 +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 . - -export default from './requestFinished'; diff --git a/js/src/views/Signer/components/RequestFinished/requestFinished.js b/js/src/views/Signer/components/RequestFinished/requestFinished.js deleted file mode 100644 index bce9e4038..000000000 --- a/js/src/views/Signer/components/RequestFinished/requestFinished.js +++ /dev/null @@ -1,87 +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 . - -import React, { Component, PropTypes } from 'react'; - -import TransactionFinished from '../TransactionFinished'; -import SignRequest from '../SignRequest'; - -export default class RequestFinished extends Component { - static propTypes = { - id: PropTypes.object.isRequired, - result: PropTypes.any.isRequired, - date: PropTypes.instanceOf(Date).isRequired, - payload: PropTypes.oneOfType([ - PropTypes.shape({ transaction: PropTypes.object.isRequired }), - PropTypes.shape({ sign: PropTypes.object.isRequired }) - ]).isRequired, - msg: PropTypes.string, - status: PropTypes.string, - error: PropTypes.string, - className: PropTypes.string, - isTest: PropTypes.bool.isRequired, - store: PropTypes.object.isRequired - } - - render () { - const { payload, id, result, msg, status, error, date, className, isTest, store } = this.props; - - if (payload.sign) { - const { sign } = payload; - - return ( - - ); - } - - if (payload.transaction) { - const { transaction } = payload; - - return ( - - ); - } - - // Unknown payload - return null; - } -} diff --git a/js/src/views/Signer/components/index.js b/js/src/views/Signer/components/index.js deleted file mode 100644 index 7c891f621..000000000 --- a/js/src/views/Signer/components/index.js +++ /dev/null @@ -1,18 +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 . - -export RequestFinished from './RequestFinished'; -export RequestPending from './RequestPending'; diff --git a/js/src/views/Signer/containers/Embedded/embedded.js b/js/src/views/Signer/containers/Embedded/embedded.js index b62c1a6c0..af008609c 100644 --- a/js/src/views/Signer/containers/Embedded/embedded.js +++ b/js/src/views/Signer/containers/Embedded/embedded.js @@ -23,7 +23,7 @@ import Store from '../../store'; import * as RequestsActions from '../../../../redux/providers/signerActions'; import { Container } from '../../../../ui'; -import { RequestPending } from '../../components'; +import RequestPending from '../../components/RequestPending'; import styles from './embedded.css'; diff --git a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js index a15cb537d..1ee88fc09 100644 --- a/js/src/views/Signer/containers/RequestsPage/RequestsPage.js +++ b/js/src/views/Signer/containers/RequestsPage/RequestsPage.js @@ -24,7 +24,7 @@ import Store from '../../store'; import * as RequestsActions from '../../../../redux/providers/signerActions'; import { Container, Page, TxList } from '../../../../ui'; -import { RequestPending } from '../../components'; +import RequestPending from '../../components/RequestPending'; import styles from './RequestsPage.css'; From df3c07b0a9a3919ad8d2550f986a04a314741de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 29 Nov 2016 17:14:28 +0100 Subject: [PATCH 051/155] adding proof to a panicker --- dapps/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index b185eb57a..9bb3be4a7 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -266,7 +266,11 @@ impl Server { #[cfg(test)] /// Returns address that this server is bound to. pub fn addr(&self) -> &SocketAddr { - &self.server.as_ref().expect("server is always Some at the start; it's consumed only when object is dropped; qed").addrs()[0] + self.server.as_ref() + .expect("server is always Some at the start; it's consumed only when object is dropped; qed") + .addrs() + .first() + .expect("You cannot start the server without binding to at least one address; qed") } } From d58905ae13f781139380f3611bd6d137eeb64208 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 29 Nov 2016 17:59:17 +0100 Subject: [PATCH 052/155] 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 837ff1bc7dc17e5bbf4d84a6842be8786fb20aeb Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 30 Nov 2016 10:54:38 +0000 Subject: [PATCH 053/155] [ci skip] js-precompiled 20161130-104916 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58f130b35..caeb7a986 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1263,7 +1263,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#cb6836dddf8c9951e056283dcd9e105e97923d07" +source = "git+https://github.com/ethcore/js-precompiled.git#da35604af4259da3abd7a12a4c4925906c78f746" 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 a6f705ec6..408706016 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.78", + "version": "0.2.79", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From cce195a98bd6abe9c3ba9d02d817cb2ab36423a7 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 30 Nov 2016 17:28:55 +0100 Subject: [PATCH 054/155] fix status bar to bottom of the screen --- js/src/views/Application/Status/status.css | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/js/src/views/Application/Status/status.css b/js/src/views/Application/Status/status.css index de043a1ad..90b40b606 100644 --- a/js/src/views/Application/Status/status.css +++ b/js/src/views/Application/Status/status.css @@ -15,10 +15,15 @@ /* along with Parity. If not, see . */ .status { - padding: 0.5em; + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 1000; + padding: .4em .5em; font-size: x-small; color: #ccc; - background-color: rgba(0, 0, 0, 0.2) + background-color: rgba(0, 0, 0, 0.8); } .title { @@ -42,7 +47,7 @@ .netinfo { display: flex; align-items: center; - color: #ddd; + color: #ddd; } .netinfo > * { From f9f91837c29a1136efdd86665268a92b81344ed5 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 30 Nov 2016 17:41:23 +0100 Subject: [PATCH 055/155] rework status bar layout - floats -> Flexbox - align to content width --- js/src/views/Application/Status/status.css | 27 +++++----------------- js/src/views/Application/Status/status.js | 2 +- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/js/src/views/Application/Status/status.css b/js/src/views/Application/Status/status.css index 90b40b606..a761801e4 100644 --- a/js/src/views/Application/Status/status.css +++ b/js/src/views/Application/Status/status.css @@ -20,19 +20,16 @@ left: 0; right: 0; z-index: 1000; + display: flex; + align-items: center; padding: .4em .5em; font-size: x-small; color: #ccc; background-color: rgba(0, 0, 0, 0.8); } -.title { - margin: 0 0.5em 0 2em; -} - .enode { word-wrap: break-word; - float: right; } .enode > * { @@ -40,25 +37,24 @@ margin: 0.25em 0.5em; vertical-align: top; } - -.block { +.enode > :last-child { + margin-right: 0; } .netinfo { display: flex; + flex-grow: 1; align-items: center; color: #ddd; } .netinfo > * { - display: inline-block; margin-left: 1em; } .network { padding: 0.25em 0.5em; - display: inline-block; - border-radius: 4px; + border-radius: .4em; line-height: 1.2; text-transform: uppercase; } @@ -70,14 +66,3 @@ .networktest { background: rgb(136, 0, 0); } - -.peers { -} - -.version { - padding: 0.25em 0.5em; - float: left; -} - -.syncing { -} diff --git a/js/src/views/Application/Status/status.js b/js/src/views/Application/Status/status.js index 6417d5d28..0c9c5d6ec 100644 --- a/js/src/views/Application/Status/status.js +++ b/js/src/views/Application/Status/status.js @@ -46,7 +46,6 @@ class Status extends Component {
{ clientVersion }
- { this.renderEnode() }
@@ -56,6 +55,7 @@ class Status extends Component { { netPeers.active.toFormat() }/{ netPeers.connected.toFormat() }/{ netPeers.max.toFormat() } peers
+ { this.renderEnode() }
); } From 890f880a890c4c7f8c4f597bd80664b3cfe968cc Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 30 Nov 2016 17:45:14 +0100 Subject: [PATCH 056/155] status bar: beautify enode icon --- js/src/views/Application/Status/status.css | 4 ++-- js/src/views/Application/Status/status.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/views/Application/Status/status.css b/js/src/views/Application/Status/status.css index a761801e4..8721bc4c2 100644 --- a/js/src/views/Application/Status/status.css +++ b/js/src/views/Application/Status/status.css @@ -34,8 +34,8 @@ .enode > * { display: inline-block; - margin: 0.25em 0.5em; - vertical-align: top; + margin: 0 .25em; + vertical-align: middle; } .enode > :last-child { margin-right: 0; diff --git a/js/src/views/Application/Status/status.js b/js/src/views/Application/Status/status.js index 0c9c5d6ec..287e7a6ee 100644 --- a/js/src/views/Application/Status/status.js +++ b/js/src/views/Application/Status/status.js @@ -73,7 +73,7 @@ class Status extends Component { return (
- +
{ abbreviated }
); From 60a8aabe1961b4795d4d41bbbe13483234a4a2d9 Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 30 Nov 2016 17:58:18 +0100 Subject: [PATCH 057/155] differentiate Snackbar from background --- js/src/views/Application/Snackbar/snackbar.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/js/src/views/Application/Snackbar/snackbar.js b/js/src/views/Application/Snackbar/snackbar.js index ac6e6b950..fdeb48c57 100644 --- a/js/src/views/Application/Snackbar/snackbar.js +++ b/js/src/views/Application/Snackbar/snackbar.js @@ -19,10 +19,17 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { Snackbar as SnackbarMUI } from 'material-ui'; -import { darkBlack } from 'material-ui/styles/colors'; +import { darkBlack, grey800 } from 'material-ui/styles/colors'; import { closeSnackbar } from '../../../redux/providers/snackbarActions'; +const bodyStyle = { + backgroundColor: darkBlack, + borderStyle: 'solid', + borderColor: grey800, + borderWidth: '1px 1px 0 1px' +}; + class Snackbar extends Component { static propTypes = { closeSnackbar: PropTypes.func.isRequired, @@ -40,7 +47,7 @@ class Snackbar extends Component { open={ open } message={ message } autoHideDuration={ cooldown } - bodyStyle={ { backgroundColor: darkBlack } } + bodyStyle={ bodyStyle } onRequestClose={ this.handleClose } /> ); From eee03b542de3b01ca651d7d4bf06e1d1f7b6362c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 30 Nov 2016 19:48:10 +0100 Subject: [PATCH 058/155] add account recovery phrase page --- js/package.json | 1 + .../AccountDetails/recovery-page.ejs | 34 +++++++++++++++++++ js/webpack/app.js | 4 +++ 3 files changed, 39 insertions(+) create mode 100644 js/src/modals/CreateAccount/AccountDetails/recovery-page.ejs diff --git a/js/package.json b/js/package.json index 408706016..7f2a3fb9d 100644 --- a/js/package.json +++ b/js/package.json @@ -72,6 +72,7 @@ "core-js": "~2.4.1", "coveralls": "~2.11.11", "css-loader": "~0.26.0", + "ejs-loader": "~0.3.0", "enzyme": "2.3.0", "eslint": "~3.10.2", "eslint-config-semistandard": "~7.0.0", diff --git a/js/src/modals/CreateAccount/AccountDetails/recovery-page.ejs b/js/src/modals/CreateAccount/AccountDetails/recovery-page.ejs new file mode 100644 index 000000000..88966bbb8 --- /dev/null +++ b/js/src/modals/CreateAccount/AccountDetails/recovery-page.ejs @@ -0,0 +1,34 @@ + + + + + Recovery phrase for <%= name %> + + + + + +

Recovery phrase for your Parity account <%= name %>

+
<%= phrase %>
+ + diff --git a/js/webpack/app.js b/js/webpack/app.js index 320410b2e..aff9b8aac 100644 --- a/js/webpack/app.js +++ b/js/webpack/app.js @@ -64,6 +64,10 @@ module.exports = { test: /\.json$/, use: [ 'json-loader' ] }, + { + test: /\.ejs$/, + use: [ 'ejs-loader' ] + }, { test: /\.html$/, use: [ From 35fe4de6224cbc514d745e74d1fb917dde91534c Mon Sep 17 00:00:00 2001 From: Jannis R Date: Wed, 30 Nov 2016 19:58:52 +0100 Subject: [PATCH 059/155] add recovery page print button --- .../AccountDetails/accountDetails.js | 26 ++++++++ .../CreateAccount/AccountDetails/print.js | 61 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 js/src/modals/CreateAccount/AccountDetails/print.js diff --git a/js/src/modals/CreateAccount/AccountDetails/accountDetails.js b/js/src/modals/CreateAccount/AccountDetails/accountDetails.js index 14c858c06..6323350f8 100644 --- a/js/src/modals/CreateAccount/AccountDetails/accountDetails.js +++ b/js/src/modals/CreateAccount/AccountDetails/accountDetails.js @@ -15,8 +15,13 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; +import PrintIcon from 'material-ui/svg-icons/action/print'; import { Form, Input, InputAddress } from '../../../ui'; +import Button from '../../../ui/Button'; + +import print from './print'; +import recoveryPage from './recovery-page.ejs'; export default class AccountDetails extends Component { static propTypes = { @@ -42,6 +47,7 @@ export default class AccountDetails extends Component { label='address' value={ address } /> { this.renderPhrase() } + { this.renderPhraseCopyButton() } ); } @@ -62,4 +68,24 @@ export default class AccountDetails extends Component { value={ phrase } /> ); } + + renderPhraseCopyButton () { + const { phrase } = this.props; + if (!phrase) { + return null; + } + + return ( +
diff --git a/js/src/views/Account/account.js b/js/src/views/Account/account.js index e27333cbf..21d2f380c 100644 --- a/js/src/views/Account/account.js +++ b/js/src/views/Account/account.js @@ -64,12 +64,6 @@ class Account extends Component { } componentDidMount () { - const { api } = this.context; - const { address } = this.props.params; - const { isTestnet } = this.props; - - const verificationStore = new VerificationStore(api, address, isTestnet); - this.setState({ verificationStore }); this.setVisibleAccounts(); } @@ -80,6 +74,15 @@ class Account extends Component { if (prevAddress !== nextAddress) { this.setVisibleAccounts(nextProps); } + + const { isTestnet } = nextProps; + if (typeof isTestnet === 'boolean' && !this.state.verificationStore) { + const { api } = this.context; + const { address } = nextProps.params; + this.setState({ + verificationStore: new VerificationStore(api, address, isTestnet) + }); + } } componentWillUnmount () { @@ -115,7 +118,8 @@ class Account extends Component {
+ balance={ balance } + /> diff --git a/js/src/views/Contract/contract.js b/js/src/views/Contract/contract.js index 613bf70b9..54d06f228 100644 --- a/js/src/views/Contract/contract.js +++ b/js/src/views/Contract/contract.js @@ -132,7 +132,8 @@ class Contract extends Component {
+ balance={ balance } + /> @@ -447,7 +448,10 @@ function mapStateToProps (state) { } function mapDispatchToProps (dispatch) { - return bindActionCreators({ newError, setVisibleAccounts }, dispatch); + return bindActionCreators({ + newError, + setVisibleAccounts + }, dispatch); } export default connect( From 4f4bfb2239d5d0d62a370c792935fe9ed17796be Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 30 Nov 2016 20:46:47 +0000 Subject: [PATCH 063/155] [ci skip] js-precompiled 20161130-204501 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index caeb7a986..4d1bba344 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1263,7 +1263,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#da35604af4259da3abd7a12a4c4925906c78f746" +source = "git+https://github.com/ethcore/js-precompiled.git#b3f0e3ddedf9afee35ca8384a74158df572973c7" 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 408706016..c6fab4119 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.79", + "version": "0.2.80", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From b7dc60ace53922e4bcf5cd25830f5c623758b9b0 Mon Sep 17 00:00:00 2001 From: arkpar Date: Wed, 30 Nov 2016 23:33:17 +0100 Subject: [PATCH 064/155] Don't share the snapshot while downloading old blocks --- sync/src/chain.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index ff60d1748..bd312c9ee 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1426,7 +1426,10 @@ impl ChainSync { packet.append(&chain.best_block_hash); packet.append(&chain.genesis_hash); if warp_protocol { - let manifest = io.snapshot_service().manifest(); + let manifest = match self.old_blocks.is_some() { + true => None, + false => io.snapshot_service().manifest(), + }; let block_number = manifest.as_ref().map_or(0, |m| m.block_number); let manifest_hash = manifest.map_or(H256::new(), |m| m.into_rlp().sha3()); packet.append(&manifest_hash); From c4125a84b16bfecec33004587042306a1225f1ec Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Thu, 1 Dec 2016 07:07:52 +0700 Subject: [PATCH 065/155] Update gitlab-ci CARGOFLAGS -> -j $(nproc) --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6662caee6..df1085020 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ variables: SIMPLECOV: "true" RUST_BACKTRACE: "1" RUSTFLAGS: "" - CARGOFLAGS: "" + CARGOFLAGS: "-j $(nproc)" cache: key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME" untracked: true @@ -312,8 +312,8 @@ darwin: - stable - triggers script: - - cargo build --release -p ethstore $CARGOFLAGS - - cargo build --release $CARGOFLAGS + - cargo build -j 8 --release -p ethstore #$CARGOFLAGS + - cargo build -j 8 --release #$CARGOFLAGS - rm -rf parity.md5 - md5sum target/release/parity > parity.md5 - packagesbuild -v mac/Parity.pkgproj @@ -350,7 +350,7 @@ windows: - set RUST_BACKTRACE=1 - set RUSTFLAGS=%RUSTFLAGS% - rustup default stable-x86_64-pc-windows-msvc - - cargo build --release %CARGOFLAGS% + - cargo build -j 8 --release #%CARGOFLAGS% - curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll - curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe - signtool sign /f %keyfile% /p %certpass% target\release\parity.exe From 70c6aebca5fa25555cb6846718004fab081df5fa Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Thu, 1 Dec 2016 07:15:35 +0700 Subject: [PATCH 066/155] Update gitlab-ci -j $(nproc) in build --- .gitlab-ci.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index df1085020..94ac69d2c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ variables: SIMPLECOV: "true" RUST_BACKTRACE: "1" RUSTFLAGS: "" - CARGOFLAGS: "-j $(nproc)" + CARGOFLAGS: "" cache: key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME" untracked: true @@ -20,7 +20,7 @@ linux-stable: - stable - triggers script: - - cargo build --release $CARGOFLAGS + - cargo build -j $(nproc) --release $CARGOFLAGS - strip target/release/parity - md5sum target/release/parity > parity.md5 - sh scripts/deb-build.sh amd64 @@ -52,7 +52,7 @@ linux-beta: - stable - triggers script: - - cargo build --release $CARGOFLAGS + - cargo build -j $(nproc) --release $CARGOFLAGS - strip target/release/parity tags: - rust @@ -71,7 +71,7 @@ linux-nightly: - stable - triggers script: - - cargo build --release $CARGOFLAGS + - cargo build -j $(nproc) --release $CARGOFLAGS - strip target/release/parity tags: - rust @@ -92,7 +92,7 @@ linux-centos: script: - export CXX="g++" - export CC="gcc" - - cargo build --release $CARGOFLAGS + - cargo build -j $(nproc) --release $CARGOFLAGS - strip target/release/parity - md5sum target/release/parity > parity.md5 - aws configure set aws_access_key_id $s3_key @@ -119,7 +119,7 @@ linux-i686: script: - export HOST_CC=gcc - export HOST_CXX=g++ - - cargo build --target i686-unknown-linux-gnu --release $CARGOFLAGS + - cargo build -j $(nproc) --target i686-unknown-linux-gnu --release $CARGOFLAGS - strip target/i686-unknown-linux-gnu/release/parity - md5sum target/i686-unknown-linux-gnu/release/parity > parity.md5 - sh scripts/deb-build.sh i386 @@ -161,7 +161,7 @@ linux-armv7: - echo "[target.armv7-unknown-linux-gnueabihf]" >> .cargo/config - echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config - cat .cargo/config - - cargo build --target armv7-unknown-linux-gnueabihf --release $CARGOFLAGS + - cargo build -j $(nproc) --target armv7-unknown-linux-gnueabihf --release $CARGOFLAGS - arm-linux-gnueabihf-strip target/armv7-unknown-linux-gnueabihf/release/parity - md5sum target/armv7-unknown-linux-gnueabihf/release/parity > parity.md5 - sh scripts/deb-build.sh armhf @@ -203,7 +203,7 @@ linux-arm: - echo "[target.arm-unknown-linux-gnueabihf]" >> .cargo/config - echo "linker= \"arm-linux-gnueabihf-gcc\"" >> .cargo/config - cat .cargo/config - - cargo build --target arm-unknown-linux-gnueabihf --release $CARGOFLAGS + - cargo build -j $(nproc) --target arm-unknown-linux-gnueabihf --release $CARGOFLAGS - arm-linux-gnueabihf-strip target/arm-unknown-linux-gnueabihf/release/parity - md5sum target/arm-unknown-linux-gnueabihf/release/parity > parity.md5 - sh scripts/deb-build.sh armhf @@ -245,7 +245,7 @@ linux-armv6: - echo "[target.arm-unknown-linux-gnueabi]" >> .cargo/config - echo "linker= \"arm-linux-gnueabi-gcc\"" >> .cargo/config - cat .cargo/config - - cargo build --target arm-unknown-linux-gnueabi --release $CARGOFLAGS + - cargo build -j $(nproc) --target arm-unknown-linux-gnueabi --release $CARGOFLAGS - arm-linux-gnueabi-strip target/arm-unknown-linux-gnueabi/release/parity - md5sum target/arm-unknown-linux-gnueabi/release/parity > parity.md5 - aws configure set aws_access_key_id $s3_key @@ -280,7 +280,7 @@ linux-aarch64: - echo "[target.aarch64-unknown-linux-gnu]" >> .cargo/config - echo "linker= \"aarch64-linux-gnu-gcc\"" >> .cargo/config - cat .cargo/config - - cargo build --target aarch64-unknown-linux-gnu --release $CARGOFLAGS + - cargo build -j $(nproc) --target aarch64-unknown-linux-gnu --release $CARGOFLAGS - aarch64-linux-gnu-strip target/aarch64-unknown-linux-gnu/release/parity - md5sum target/aarch64-unknown-linux-gnu/release/parity > parity.md5 - sh scripts/deb-build.sh arm64 @@ -413,7 +413,7 @@ test-windows: - git submodule update --init --recursive script: - set RUST_BACKTRACE=1 - - cargo test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release + - cargo -j 8 test --features json-tests -p rlp -p ethash -p ethcore -p ethcore-bigint -p ethcore-dapps -p ethcore-rpc -p ethcore-signer -p ethcore-util -p ethcore-network -p ethcore-io -p ethkey -p ethstore -p ethsync -p ethcore-ipc -p ethcore-ipc-tests -p ethcore-ipc-nano -p parity %CARGOFLAGS% --verbose --release tags: - rust-windows allow_failure: true From 61eb2a910462d154a2e9f1572d942add2e6b7080 Mon Sep 17 00:00:00 2001 From: "Denis S. Soldatov aka General-Beck" Date: Thu, 1 Dec 2016 07:48:06 +0700 Subject: [PATCH 067/155] Update test.sh add -j 8 for test --- test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 44bffa7d9..3e9074478 100755 --- a/test.sh +++ b/test.sh @@ -19,5 +19,5 @@ case $1 in esac . ./scripts/targets.sh -cargo test $OPTIONS --features "$FEATURES" $TARGETS $1 \ +cargo test -j 8 $OPTIONS --features "$FEATURES" $TARGETS $1 \ From 3ef569329acd51ce1355fe6bd037c67b396eba4e Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 1 Dec 2016 01:21:59 +0000 Subject: [PATCH 068/155] [ci skip] js-precompiled 20161201-011624 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d1bba344..95a795bc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1263,7 +1263,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#b3f0e3ddedf9afee35ca8384a74158df572973c7" +source = "git+https://github.com/ethcore/js-precompiled.git#41719ab7b7cb9682a8a32fdfaad7749d5a1285b1" 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 67fd98976..bd4be6802 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.80", + "version": "0.2.81", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 0f987a2206bcfbfdcee7e2d8696c3fb44f618301 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 1 Dec 2016 04:48:29 +0000 Subject: [PATCH 069/155] [ci skip] js-precompiled 20161201-044643 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95a795bc3..035e5517c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1263,7 +1263,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#41719ab7b7cb9682a8a32fdfaad7749d5a1285b1" +source = "git+https://github.com/ethcore/js-precompiled.git#d99369b5010cfa73c3a62eb8c031bf873a7ca709" 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 bd4be6802..4957c7f19 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.81", + "version": "0.2.82", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 08a6be5d62325c5e358f4d737ebe6119bacb13ac Mon Sep 17 00:00:00 2001 From: Jannis R Date: Thu, 1 Dec 2016 12:11:03 +0100 Subject: [PATCH 070/155] recovery phrase: move print button to modal actions --- .../AccountDetails/accountDetails.js | 30 ------------------- js/src/modals/CreateAccount/createAccount.js | 23 ++++++++++++-- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/js/src/modals/CreateAccount/AccountDetails/accountDetails.js b/js/src/modals/CreateAccount/AccountDetails/accountDetails.js index 945bcc975..14c858c06 100644 --- a/js/src/modals/CreateAccount/AccountDetails/accountDetails.js +++ b/js/src/modals/CreateAccount/AccountDetails/accountDetails.js @@ -15,15 +15,8 @@ // along with Parity. If not, see . import React, { Component, PropTypes } from 'react'; -import PrintIcon from 'material-ui/svg-icons/action/print'; import { Form, Input, InputAddress } from '../../../ui'; -import Button from '../../../ui/Button'; - -import { createIdentityImg } from '../../../api/util/identity'; -import print from './print'; -import recoveryPage from './recovery-page.ejs'; -import ParityLogo from '../../../../assets/images/parity-logo-black-no-text.svg'; export default class AccountDetails extends Component { static propTypes = { @@ -49,7 +42,6 @@ export default class AccountDetails extends Component { label='address' value={ address } /> { this.renderPhrase() } - { this.renderPhraseCopyButton() } ); } @@ -70,26 +62,4 @@ export default class AccountDetails extends Component { value={ phrase } /> ); } - - renderPhraseCopyButton () { - const { phrase } = this.props; - if (!phrase) { - return null; - } - - return ( -