From b38d95328d739828310eb9ed0d7dbc80821792e1 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 7 Dec 2016 10:50:18 +0100 Subject: [PATCH 01/74] light: conditional compilation --- Cargo.lock | 1 - Cargo.toml | 1 + ethcore/Cargo.toml | 1 + ethcore/src/client/client.rs | 5 +++-- ethcore/src/client/mod.rs | 5 ++++- ethcore/src/client/traits.rs | 1 + ethcore/src/state/mod.rs | 3 +++ 7 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cca8b014d..7dc6f7bcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -298,7 +298,6 @@ 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/Cargo.toml b/Cargo.toml index 078d2916c..9d6848b6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,7 @@ ethstore-cli = ["ethcore/ethstore-cli"] evm-debug = ["ethcore/evm-debug"] evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] +light = ["ethcore/light"] [[bin]] path = "parity/main.rs" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index bd87c422f..2c3d946f4 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -58,5 +58,6 @@ dev = ["clippy"] default = [] benches = [] ipc = [] +light = [] ethkey-cli = ["ethkey/cli"] ethstore-cli = ["ethstore/cli"] diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 21c5a2366..ee72ea8b8 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -52,7 +52,7 @@ use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use client::{ BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, - ChainNotify, PruningInfo, ProvingBlockChainClient, + ChainNotify, PruningInfo, }; use client::Error as ClientError; use env_info::EnvInfo; @@ -1377,7 +1377,8 @@ impl MayPanic for Client { } } -impl ProvingBlockChainClient for Client { +#[cfg(feature = "light")] +impl ::client::ProvingBlockChainClient for Client { fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockID) -> Vec { self.state_at(id) .and_then(move |state| state.prove_storage(key1, key2, from_level).ok()) diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index af4daece6..beab88e8a 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -27,7 +27,10 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChain pub use self::error::Error; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::chain_notify::ChainNotify; -pub use self::traits::{BlockChainClient, MiningBlockChainClient, ProvingBlockChainClient}; +pub use self::traits::{BlockChainClient, MiningBlockChainClient}; + +#[cfg(feature = "light")] +pub use self::traits::ProvingBlockChainClient; pub use types::ids::*; pub use types::trace_filter::Filter as TraceFilter; diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 7bf17279c..3eed9aa5d 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -278,6 +278,7 @@ pub trait MiningBlockChainClient: BlockChainClient { } /// Extended client interface for providing proofs of the state. +#[cfg(feature = "light")] pub trait ProvingBlockChainClient: BlockChainClient { /// Prove account storage at a specific block id. /// diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index b3d63d0ae..d39481048 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -31,6 +31,8 @@ use transaction::SignedTransaction; use state_db::StateDB; use util::*; + +#[cfg(feature = "light")] use util::trie::recorder::{Recorder, BasicRecorder as TrieRecorder}; mod account; @@ -762,6 +764,7 @@ impl State { } // LES state proof implementations. +#[cfg(feature = "light")] 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` From cdc758368a74c00d7d281a0cc7cd5d2888004c47 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 7 Dec 2016 13:52:45 +0100 Subject: [PATCH 02/74] les: flesh out event handler --- Cargo.lock | 16 ++++ Cargo.toml | 4 +- ethcore/light/Cargo.toml | 2 +- ethcore/light/src/net/mod.rs | 145 +++++++++++++++++++++++++---------- sync/Cargo.toml | 2 + sync/src/lib.rs | 4 + 6 files changed, 129 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7dc6f7bcb..b00701f32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,6 +456,21 @@ dependencies = [ "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ethcore-light" +version = "1.5.0" +dependencies = [ + "ethcore 1.5.0", + "ethcore-io 1.5.0", + "ethcore-ipc 1.4.0", + "ethcore-ipc-codegen 1.4.0", + "ethcore-network 1.5.0", + "ethcore-util 1.5.0", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rlp 0.1.0", + "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ethcore-logger" version = "1.5.0" @@ -664,6 +679,7 @@ dependencies = [ "ethcore-ipc 1.4.0", "ethcore-ipc-codegen 1.4.0", "ethcore-ipc-nano 1.4.0", + "ethcore-light 1.5.0", "ethcore-network 1.5.0", "ethcore-util 1.5.0", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 9d6848b6a..4730cef0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ winapi = "0.2" daemonize = "0.2" [features] -default = ["ui-precompiled"] +default = ["ui-precompiled", "light"] ui = [ "dapps", @@ -80,7 +80,7 @@ ethstore-cli = ["ethcore/ethstore-cli"] evm-debug = ["ethcore/evm-debug"] evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] -light = ["ethcore/light"] +light = ["ethcore/light", "ethsync/light"] [[bin]] path = "parity/main.rs" diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 74400d7ab..9440bbf88 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -12,7 +12,7 @@ build = "build.rs" [dependencies] log = "0.3" -ethcore = { path = ".." } +ethcore = { path = "..", features = ["light"] } ethcore-util = { path = "../../util" } ethcore-network = { path = "../../util/network" } ethcore-io = { path = "../../util/io" } diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index a1b3b30b0..e729b4361 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -20,11 +20,13 @@ //! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) use ethcore::transaction::SignedTransaction; +use ethcore::receipt::Receipt; + use io::TimerToken; use network::{NetworkProtocolHandler, NetworkContext, NetworkError, PeerId}; use rlp::{RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; -use util::{Mutex, RwLock, U256}; +use util::{Bytes, Mutex, RwLock, U256}; use time::SteadyTime; use std::collections::{HashMap, HashSet}; @@ -134,16 +136,50 @@ impl Peer { } } +/// Context for a network event. +pub struct EventContext<'a> { + /// Protocol implementation. + pub proto: &'a LightProtocol, + /// Network context to enable immediate response to + /// events. + pub io: &'a NetworkContext<'a>, + /// Relevant peer for event. + pub peer: PeerId, +} + /// An LES event handler. +/// +/// Each handler function takes a context which describes the relevant peer +/// and gives references to the IO layer and protocol structure so new messages +/// can be dispatched immediately. +/// +/// Request responses are not guaranteed to be complete or valid, but passed IDs will be correct. +/// Response handlers are not given a copy of the original request; it is assumed +/// that relevant data will be stored by interested handlers. 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) { } + fn on_connect(&self, _ctx: EventContext, _status: &Status, _capabilities: &Capabilities) { } + /// Called when a peer disconnects, with a list of unfulfilled request IDs as + /// of yet. + fn on_disconnect(&self, _ctx: EventContext, _unfulfilled: &[ReqId]) { } /// Called when a peer makes an announcement. - fn on_announcement(&self, _id: PeerId, _announcement: &Announcement) { } + fn on_announcement(&self, _ctx: EventContext, _announcement: &Announcement) { } /// Called when a peer requests relay of some transactions. - fn on_transactions(&self, _id: PeerId, _relay: &[SignedTransaction]) { } + fn on_transactions(&self, _ctx: EventContext, _relay: &[SignedTransaction]) { } + /// Called when a peer responds with block bodies. + fn on_block_bodies(&self, _ctx: EventContext, _req_id: ReqId, _bodies: &[Bytes]) { } + /// Called when a peer responds with block headers. + fn on_block_headers(&self, _ctx: EventContext, _req_id: ReqId, _headers: &[Bytes]) { } + /// Called when a peer responds with block receipts. + fn on_receipts(&self, _ctx: EventContext, _req_id: ReqId, _receipts: &[Vec]) { } + /// Called when a peer responds with state proofs. Each proof is a series of trie + /// nodes in ascending order by distance from the root. + fn on_state_proofs(&self, _ctx: EventContext, _req_id: ReqId, _proofs: &[Vec]) { } + /// Called when a peer responds with contract code. + fn on_code(&self, _ctx: EventContext, _req_id: ReqId, _codes: &[Bytes]) { } + /// Called when a peer responds with header proofs. Each proof is a series of trie + /// nodes is ascending order by distance from the root. + fn on_header_proofs(&self, _ctx: EventContext, _req_id: ReqId, _proofs: &[Vec]) { } } // a request and the time it was made. @@ -290,7 +326,8 @@ 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 + /// These are intended to be added when the protocol structure + /// is initialized as a means of customizing its behavior. pub fn add_handler(&mut self, handler: Box) { self.handlers.push(handler); } @@ -313,12 +350,23 @@ impl LightProtocol { } // called when a peer disconnects. - fn on_disconnect(&self, peer: PeerId) { - // TODO: reassign all requests assigned to this peer. + fn on_disconnect(&self, peer: PeerId, io: &NetworkContext) { self.pending_peers.write().remove(&peer); - if self.peers.write().remove(&peer).is_some() { + if let Some(peer_info) = self.peers.write().remove(&peer) { + let unfulfilled: Vec<_> = peer_info.into_inner().current_asking.into_iter().map(ReqId).collect(); + { + let mut pending = self.pending_requests.write(); + for &ReqId(ref inner) in &unfulfilled { + pending.remove(inner); + } + } + for handler in &self.handlers { - handler.on_disconnect(peer) + handler.on_disconnect(EventContext { + peer: peer, + io: io, + proto: self, + }, &unfulfilled) } } } @@ -350,7 +398,7 @@ impl LightProtocol { } // Handle status message from peer. - fn status(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> { + fn status(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { let pending = match self.pending_peers.write().remove(peer) { Some(pending) => pending, None => { @@ -377,45 +425,56 @@ impl LightProtocol { })); for handler in &self.handlers { - handler.on_connect(*peer, &status, &capabilities) + handler.on_connect(EventContext { + peer: *peer, + io: io, + proto: self, + }, &status, &capabilities) } Ok(()) } // Handle an announcement. - fn announcement(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> { + fn announcement(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { if !self.peers.read().contains_key(peer) { debug!(target: "les", "Ignoring announcement from unknown peer"); return Ok(()) } let announcement = try!(status::parse_announcement(data)); - let peers = self.peers.read(); - let peer_info = match peers.get(peer) { - Some(info) => info, - None => return Ok(()), - }; - - let mut peer_info = peer_info.lock(); - - // update status. + // scope to ensure locks are dropped before moving into handler-space. { - // 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)); + let peers = self.peers.read(); + 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. + 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. + peer_info.capabilities.update_from(&announcement); } - // update capabilities. - peer_info.capabilities.update_from(&announcement); - for handler in &self.handlers { - handler.on_announcement(*peer, &announcement); + handler.on_announcement(EventContext { + peer: *peer, + io: io, + proto: self, + }, &announcement); } Ok(()) @@ -736,7 +795,7 @@ impl LightProtocol { } // Receive a set of transactions to relay. - fn relay_transactions(&self, peer: &PeerId, data: UntrustedRlp) -> Result<(), Error> { + fn relay_transactions(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_TRANSACTIONS: usize = 256; let txs: Vec<_> = try!(data.iter().take(MAX_TRANSACTIONS).map(|x| x.as_val::()).collect()); @@ -744,7 +803,11 @@ impl LightProtocol { debug!(target: "les", "Received {} transactions to relay from peer {}", txs.len(), peer); for handler in &self.handlers { - handler.on_transactions(*peer, &txs); + handler.on_transactions(EventContext { + peer: *peer, + io: io, + proto: self, + }, &txs); } Ok(()) @@ -761,8 +824,8 @@ impl NetworkProtocolHandler for LightProtocol { // handle the packet let res = match packet_id { - packet::STATUS => self.status(peer, rlp), - packet::ANNOUNCE => self.announcement(peer, rlp), + packet::STATUS => self.status(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), @@ -782,7 +845,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, rlp), + packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), other => { Err(Error::UnrecognizedPacket(other)) @@ -809,8 +872,8 @@ impl NetworkProtocolHandler for LightProtocol { self.on_connect(peer, io); } - fn disconnected(&self, _io: &NetworkContext, peer: &PeerId) { - self.on_disconnect(*peer); + fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { + self.on_disconnect(*peer, io); } fn timeout(&self, _io: &NetworkContext, timer: TimerToken) { diff --git a/sync/Cargo.toml b/sync/Cargo.toml index 738f5f55c..df0f4840d 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -15,6 +15,7 @@ ethcore-ipc-codegen = { path = "../ipc/codegen" } ethcore-util = { path = "../util" } ethcore-network = { path = "../util/network" } ethcore-io = { path = "../util/io" } +ethcore-light = { path = "../ethcore/light", optional = true } ethcore = { path = "../ethcore" } rlp = { path = "../util/rlp" } clippy = { version = "0.0.103", optional = true} @@ -31,4 +32,5 @@ parking_lot = "0.3" [features] default = ["ipc"] dev = ["clippy", "ethcore/dev", "ethcore-util/dev"] +light = ["ethcore-light"] ipc = [] diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 2061e4e3a..7a4f22fd7 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -37,6 +37,10 @@ extern crate semver; extern crate parking_lot; extern crate rlp; +#[cfg(feature = "light")] +extern crate ethcore_light as light; + + #[macro_use] extern crate log; #[macro_use] From 10d75b6de04599880a264c07af77fa5d63fa1556 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 7 Dec 2016 15:27:04 +0100 Subject: [PATCH 03/74] light: implement all response handlers --- ethcore/light/src/net/error.rs | 6 +- ethcore/light/src/net/mod.rs | 164 ++++++++++++++++++++++++++++----- ethcore/light/src/provider.rs | 5 +- 3 files changed, 151 insertions(+), 24 deletions(-) diff --git a/ethcore/light/src/net/error.rs b/ethcore/light/src/net/error.rs index 0855cdeb8..86dbd54ba 100644 --- a/ethcore/light/src/net/error.rs +++ b/ethcore/light/src/net/error.rs @@ -54,6 +54,8 @@ pub enum Error { WrongNetwork, /// Unknown peer. UnknownPeer, + /// Unsolicited response. + UnsolicitedResponse, } impl Error { @@ -67,6 +69,7 @@ impl Error { Error::UnexpectedHandshake => Punishment::Disconnect, Error::WrongNetwork => Punishment::Disable, Error::UnknownPeer => Punishment::Disconnect, + Error::UnsolicitedResponse => Punishment::Disable, } } } @@ -92,7 +95,8 @@ 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"), + Error::UnknownPeer => write!(f, "Unknown peer"), + Error::UnsolicitedResponse => write!(f, "Peer provided unsolicited data"), } } } \ No newline at end of file diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index e729b4361..9f08c4ca6 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -29,7 +29,7 @@ use util::hash::H256; use util::{Bytes, Mutex, RwLock, U256}; use time::SteadyTime; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::sync::atomic::{AtomicUsize, Ordering}; use provider::Provider; @@ -103,7 +103,6 @@ struct PendingPeer { struct Peer { 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, @@ -137,6 +136,7 @@ impl Peer { } /// Context for a network event. +#[derive(Clone)] pub struct EventContext<'a> { /// Protocol implementation. pub proto: &'a LightProtocol, @@ -177,15 +177,16 @@ pub trait Handler: Send + Sync { fn on_state_proofs(&self, _ctx: EventContext, _req_id: ReqId, _proofs: &[Vec]) { } /// Called when a peer responds with contract code. fn on_code(&self, _ctx: EventContext, _req_id: ReqId, _codes: &[Bytes]) { } - /// Called when a peer responds with header proofs. Each proof is a series of trie - /// nodes is ascending order by distance from the root. - fn on_header_proofs(&self, _ctx: EventContext, _req_id: ReqId, _proofs: &[Vec]) { } + /// Called when a peer responds with header proofs. Each proof is a block header coupled + /// with a series of trie nodes is ascending order by distance from the root. + fn on_header_proofs(&self, _ctx: EventContext, _req_id: ReqId, _proofs: &[(Bytes, Vec)]) { } } -// a request and the time it was made. +// a request, the peer who it was made to, and the time it was made. struct Requested { request: Request, timestamp: SteadyTime, + peer_id: PeerId, } /// Protocol parameters. @@ -280,10 +281,10 @@ impl LightProtocol { 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(), + peer_id: *peer_id, }); Ok(ReqId(req_id)) @@ -331,6 +332,38 @@ impl LightProtocol { pub fn add_handler(&mut self, handler: Box) { self.handlers.push(handler); } + + // Does the common pre-verification of responses before the response itself + // is actually decoded: + // - check whether peer exists + // - check whether request was made + // - check whether request kinds match + fn pre_verify_response(&self, peer: &PeerId, kind: request::Kind, raw: &UntrustedRlp) -> Result { + let req_id: usize = try!(raw.val_at(0)); + let cur_buffer: U256 = try!(raw.val_at(1)); + + trace!(target: "les", "pre-verifying response from peer {}, kind={:?}", peer, kind); + + match self.pending_requests.write().remove(&req_id) { + None => return Err(Error::UnsolicitedResponse), + Some(requested) => { + if requested.peer_id != *peer || requested.request.kind() != kind { + return Err(Error::UnsolicitedResponse) + } + } + } + + let peers = self.peers.read(); + match peers.get(peer) { + Some(peer_info) => { + let mut peer_info = peer_info.lock(); + let actual_buffer = ::std::cmp::min(cur_buffer, *peer_info.remote_flow.limit()); + peer_info.remote_buffer.update_to(actual_buffer); + Ok(ReqId(req_id)) + } + None => Err(Error::UnknownPeer), // probably only occurs in a race of some kind. + } + } } impl LightProtocol { @@ -352,8 +385,13 @@ impl LightProtocol { // called when a peer disconnects. fn on_disconnect(&self, peer: PeerId, io: &NetworkContext) { self.pending_peers.write().remove(&peer); - if let Some(peer_info) = self.peers.write().remove(&peer) { - let unfulfilled: Vec<_> = peer_info.into_inner().current_asking.into_iter().map(ReqId).collect(); + if self.peers.write().remove(&peer).is_some() { + let unfulfilled: Vec<_> = self.pending_requests.read() + .iter() + .filter(|&(_, r)| r.peer_id == peer) + .map(|(&id, _)| ReqId(id)) + .collect(); + { let mut pending = self.pending_requests.write(); for &ReqId(ref inner) in &unfulfilled { @@ -417,7 +455,6 @@ impl LightProtocol { 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, @@ -530,8 +567,19 @@ impl LightProtocol { } // Receive a response for block headers. - fn block_headers(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn block_headers(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + let req_id = try!(self.pre_verify_response(peer, request::Kind::Headers, &raw)); + let raw_headers: Vec<_> = raw.iter().skip(2).map(|x| x.as_raw().to_owned()).collect(); + + for handler in &self.handlers { + handler.on_block_headers(EventContext { + peer: *peer, + io: io, + proto: self, + }, req_id, &raw_headers); + } + + Ok(()) } // Handle a request for block bodies. @@ -576,8 +624,19 @@ impl LightProtocol { } // Receive a response for block bodies. - fn block_bodies(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn block_bodies(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + let req_id = try!(self.pre_verify_response(peer, request::Kind::Bodies, &raw)); + let raw_bodies: Vec = raw.iter().skip(2).map(|x| x.as_raw().to_owned()).collect(); + + for handler in &self.handlers { + handler.on_block_bodies(EventContext { + peer: *peer, + io: io, + proto: self, + }, req_id, &raw_bodies); + } + + Ok(()) } // Handle a request for receipts. @@ -622,8 +681,23 @@ impl LightProtocol { } // Receive a response for receipts. - fn receipts(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn receipts(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + let req_id = try!(self.pre_verify_response(peer, request::Kind::Receipts, &raw)); + let raw_receipts: Vec> = try!(raw + .iter() + .skip(2) + .map(|x| x.as_val()) + .collect()); + + for handler in &self.handlers { + handler.on_receipts(EventContext { + peer: *peer, + io: io, + proto: self, + }, req_id, &raw_receipts); + } + + Ok(()) } // Handle a request for proofs. @@ -679,8 +753,23 @@ impl LightProtocol { } // Receive a response for proofs. - fn proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn proofs(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + let req_id = try!(self.pre_verify_response(peer, request::Kind::StateProofs, &raw)); + + let raw_proofs: Vec> = raw.iter() + .skip(2) + .map(|x| x.iter().map(|node| node.as_raw().to_owned()).collect()) + .collect(); + + for handler in &self.handlers { + handler.on_state_proofs(EventContext { + peer: *peer, + io: io, + proto: self, + }, req_id, &raw_proofs); + } + + Ok(()) } // Handle a request for contract code. @@ -734,8 +823,20 @@ impl LightProtocol { } // Receive a response for contract code. - fn contract_code(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn contract_code(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + let req_id = try!(self.pre_verify_response(peer, request::Kind::Codes, &raw)); + + let raw_code: Vec = try!(raw.iter().skip(2).map(|x| x.as_val()).collect()); + + for handler in &self.handlers { + handler.on_code(EventContext { + peer: *peer, + io: io, + proto: self, + }, req_id, &raw_code); + } + + Ok(()) } // Handle a request for header proofs @@ -790,8 +891,27 @@ impl LightProtocol { } // Receive a response for header proofs - fn header_proofs(&self, _: &PeerId, _: &NetworkContext, _: UntrustedRlp) -> Result<(), Error> { - unimplemented!() + fn header_proofs(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + fn decode_res(raw: UntrustedRlp) -> Result<(Bytes, Vec), ::rlp::DecoderError> { + Ok(( + try!(raw.val_at(0)), + try!(raw.at(1)).iter().map(|x| x.as_raw().to_owned()).collect(), + )) + } + + + let req_id = try!(self.pre_verify_response(peer, request::Kind::HeaderProofs, &raw)); + let raw_proofs: Vec<_> = try!(raw.iter().skip(2).map(decode_res).collect()); + + for handler in &self.handlers { + handler.on_header_proofs(EventContext { + peer: *peer, + io: io, + proto: self, + }, req_id, &raw_proofs); + } + + Ok(()) } // Receive a set of transactions to relay. diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 264df0397..fe7156b58 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -71,7 +71,10 @@ pub trait Provider: Send + Sync { /// 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. + /// Provide header proofs from the Canonical Hash Tries as well as the headers + /// they correspond to -- each element in the returned vector is a 2-tuple. + /// The first element is a block header and the second a merkle proof of + /// the header in a requested CHT. fn header_proofs(&self, req: request::HeaderProofs) -> Vec; /// Provide pending transactions. From 2d1a3ff09154674fa918c48444d7dea7261b17b5 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 7 Dec 2016 17:52:10 +0100 Subject: [PATCH 04/74] les: generalize network and event contexts with traits --- ethcore/light/src/net/mod.rs | 223 ++++++++++++++++++----------------- 1 file changed, 114 insertions(+), 109 deletions(-) diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 9f08c4ca6..965e46db6 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -36,9 +36,11 @@ use provider::Provider; use request::{self, Request}; use self::buffer_flow::{Buffer, FlowParams}; +use self::context::{IoContext, EventContext, Ctx}; use self::error::{Error, Punishment}; mod buffer_flow; +mod context; mod error; mod status; @@ -135,18 +137,6 @@ impl Peer { } } -/// Context for a network event. -#[derive(Clone)] -pub struct EventContext<'a> { - /// Protocol implementation. - pub proto: &'a LightProtocol, - /// Network context to enable immediate response to - /// events. - pub io: &'a NetworkContext<'a>, - /// Relevant peer for event. - pub peer: PeerId, -} - /// An LES event handler. /// /// Each handler function takes a context which describes the relevant peer @@ -158,28 +148,28 @@ pub struct EventContext<'a> { /// that relevant data will be stored by interested handlers. pub trait Handler: Send + Sync { /// Called when a peer connects. - fn on_connect(&self, _ctx: EventContext, _status: &Status, _capabilities: &Capabilities) { } + fn on_connect(&self, _ctx: &EventContext, _status: &Status, _capabilities: &Capabilities) { } /// Called when a peer disconnects, with a list of unfulfilled request IDs as /// of yet. - fn on_disconnect(&self, _ctx: EventContext, _unfulfilled: &[ReqId]) { } + fn on_disconnect(&self, _ctx: &EventContext, _unfulfilled: &[ReqId]) { } /// Called when a peer makes an announcement. - fn on_announcement(&self, _ctx: EventContext, _announcement: &Announcement) { } + fn on_announcement(&self, _ctx: &EventContext, _announcement: &Announcement) { } /// Called when a peer requests relay of some transactions. - fn on_transactions(&self, _ctx: EventContext, _relay: &[SignedTransaction]) { } + fn on_transactions(&self, _ctx: &EventContext, _relay: &[SignedTransaction]) { } /// Called when a peer responds with block bodies. - fn on_block_bodies(&self, _ctx: EventContext, _req_id: ReqId, _bodies: &[Bytes]) { } + fn on_block_bodies(&self, _ctx: &EventContext, _req_id: ReqId, _bodies: &[Bytes]) { } /// Called when a peer responds with block headers. - fn on_block_headers(&self, _ctx: EventContext, _req_id: ReqId, _headers: &[Bytes]) { } + fn on_block_headers(&self, _ctx: &EventContext, _req_id: ReqId, _headers: &[Bytes]) { } /// Called when a peer responds with block receipts. - fn on_receipts(&self, _ctx: EventContext, _req_id: ReqId, _receipts: &[Vec]) { } + fn on_receipts(&self, _ctx: &EventContext, _req_id: ReqId, _receipts: &[Vec]) { } /// Called when a peer responds with state proofs. Each proof is a series of trie /// nodes in ascending order by distance from the root. - fn on_state_proofs(&self, _ctx: EventContext, _req_id: ReqId, _proofs: &[Vec]) { } + fn on_state_proofs(&self, _ctx: &EventContext, _req_id: ReqId, _proofs: &[Vec]) { } /// Called when a peer responds with contract code. - fn on_code(&self, _ctx: EventContext, _req_id: ReqId, _codes: &[Bytes]) { } + fn on_code(&self, _ctx: &EventContext, _req_id: ReqId, _codes: &[Bytes]) { } /// Called when a peer responds with header proofs. Each proof is a block header coupled /// with a series of trie nodes is ascending order by distance from the root. - fn on_header_proofs(&self, _ctx: EventContext, _req_id: ReqId, _proofs: &[(Bytes, Vec)]) { } + fn on_header_proofs(&self, _ctx: &EventContext, _req_id: ReqId, _proofs: &[(Bytes, Vec)]) { } } // a request, the peer who it was made to, and the time it was made. @@ -257,7 +247,7 @@ impl LightProtocol { /// 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 { + pub fn request_from(&self, io: &IoContext, peer_id: &PeerId, request: Request) -> Result { let peers = self.peers.read(); let peer = try!(peers.get(peer_id).ok_or_else(|| Error::UnknownPeer)); let mut peer = peer.lock(); @@ -279,7 +269,7 @@ impl LightProtocol { request::Kind::HeaderProofs => packet::GET_HEADER_PROOFS, }; - try!(io.send(*peer_id, packet_id, packet_data)); + io.send(*peer_id, packet_id, packet_data); self.pending_requests.write().insert(req_id, Requested { request: request, @@ -292,7 +282,7 @@ impl LightProtocol { /// Make an announcement of new chain head and capabilities to all peers. /// The announcement is expected to be valid. - pub fn make_announcement(&self, io: &NetworkContext, mut announcement: Announcement) { + pub fn make_announcement(&self, io: &IoContext, mut announcement: Announcement) { let mut reorgs_map = HashMap::new(); // update stored capabilities @@ -318,9 +308,7 @@ impl LightProtocol { 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); - } + io.send(*peer_id, packet::ANNOUNCE, status::write_announcement(&announcement)); } } @@ -364,11 +352,61 @@ impl LightProtocol { None => Err(Error::UnknownPeer), // probably only occurs in a race of some kind. } } + + // handle a packet using the given io context. + fn handle_packet(&self, io: &IoContext, 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::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.receipts(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::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 => { + Err(Error::UnrecognizedPacket(other)) + } + }; + + // if something went wrong, figure out how much to punish the peer. + 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) + } + } + } + } } impl LightProtocol { // called when a peer connects. - fn on_connect(&self, peer: &PeerId, io: &NetworkContext) { + fn on_connect(&self, peer: &PeerId, io: &IoContext) { let peer = *peer; match self.send_status(peer, io) { @@ -383,7 +421,7 @@ impl LightProtocol { } // called when a peer disconnects. - fn on_disconnect(&self, peer: PeerId, io: &NetworkContext) { + fn on_disconnect(&self, peer: PeerId, io: &IoContext) { self.pending_peers.write().remove(&peer); if self.peers.write().remove(&peer).is_some() { let unfulfilled: Vec<_> = self.pending_requests.read() @@ -400,7 +438,7 @@ impl LightProtocol { } for handler in &self.handlers { - handler.on_disconnect(EventContext { + handler.on_disconnect(&Ctx { peer: peer, io: io, proto: self, @@ -410,7 +448,7 @@ impl LightProtocol { } // send status to a peer. - fn send_status(&self, peer: PeerId, io: &NetworkContext) -> Result { + fn send_status(&self, peer: PeerId, io: &IoContext) -> Result { let chain_info = self.provider.chain_info(); // TODO: could update capabilities here. @@ -428,7 +466,7 @@ impl LightProtocol { 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)); + io.send(peer, packet::STATUS, status_packet); Ok(PendingPeer { sent_head: chain_info.best_block_hash, @@ -436,7 +474,7 @@ impl LightProtocol { } // Handle status message from peer. - fn status(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn status(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { let pending = match self.pending_peers.write().remove(peer) { Some(pending) => pending, None => { @@ -462,7 +500,7 @@ impl LightProtocol { })); for handler in &self.handlers { - handler.on_connect(EventContext { + handler.on_connect(&Ctx { peer: *peer, io: io, proto: self, @@ -473,7 +511,7 @@ impl LightProtocol { } // Handle an announcement. - fn announcement(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn announcement(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { if !self.peers.read().contains_key(peer) { debug!(target: "les", "Ignoring announcement from unknown peer"); return Ok(()) @@ -507,7 +545,7 @@ impl LightProtocol { } for handler in &self.handlers { - handler.on_announcement(EventContext { + handler.on_announcement(&Ctx { peer: *peer, io: io, proto: self, @@ -518,7 +556,7 @@ impl LightProtocol { } // Handle a request for block headers. - fn get_block_headers(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_block_headers(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_HEADERS: usize = 512; let peers = self.peers.read(); @@ -563,16 +601,18 @@ impl LightProtocol { } stream.out() - }).map_err(Into::into) + }); + + Ok(()) } // Receive a response for block headers. - fn block_headers(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + fn block_headers(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { let req_id = try!(self.pre_verify_response(peer, request::Kind::Headers, &raw)); let raw_headers: Vec<_> = raw.iter().skip(2).map(|x| x.as_raw().to_owned()).collect(); for handler in &self.handlers { - handler.on_block_headers(EventContext { + handler.on_block_headers(&Ctx { peer: *peer, io: io, proto: self, @@ -583,7 +623,7 @@ impl LightProtocol { } // Handle a request for block bodies. - fn get_block_bodies(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_block_bodies(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_BODIES: usize = 256; let peers = self.peers.read(); @@ -620,16 +660,18 @@ impl LightProtocol { } stream.out() - }).map_err(Into::into) + }); + + Ok(()) } // Receive a response for block bodies. - fn block_bodies(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + fn block_bodies(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { let req_id = try!(self.pre_verify_response(peer, request::Kind::Bodies, &raw)); let raw_bodies: Vec = raw.iter().skip(2).map(|x| x.as_raw().to_owned()).collect(); for handler in &self.handlers { - handler.on_block_bodies(EventContext { + handler.on_block_bodies(&Ctx { peer: *peer, io: io, proto: self, @@ -640,7 +682,7 @@ impl LightProtocol { } // Handle a request for receipts. - fn get_receipts(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_receipts(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_RECEIPTS: usize = 256; let peers = self.peers.read(); @@ -677,11 +719,13 @@ impl LightProtocol { } stream.out() - }).map_err(Into::into) + }); + + Ok(()) } // Receive a response for receipts. - fn receipts(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + fn receipts(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { let req_id = try!(self.pre_verify_response(peer, request::Kind::Receipts, &raw)); let raw_receipts: Vec> = try!(raw .iter() @@ -690,7 +734,7 @@ impl LightProtocol { .collect()); for handler in &self.handlers { - handler.on_receipts(EventContext { + handler.on_receipts(&Ctx { peer: *peer, io: io, proto: self, @@ -701,7 +745,7 @@ impl LightProtocol { } // Handle a request for proofs. - fn get_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_proofs(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_PROOFS: usize = 128; let peers = self.peers.read(); @@ -749,11 +793,13 @@ impl LightProtocol { } stream.out() - }).map_err(Into::into) + }); + + Ok(()) } // Receive a response for proofs. - fn proofs(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + fn proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { let req_id = try!(self.pre_verify_response(peer, request::Kind::StateProofs, &raw)); let raw_proofs: Vec> = raw.iter() @@ -762,7 +808,7 @@ impl LightProtocol { .collect(); for handler in &self.handlers { - handler.on_state_proofs(EventContext { + handler.on_state_proofs(&Ctx { peer: *peer, io: io, proto: self, @@ -773,7 +819,7 @@ impl LightProtocol { } // Handle a request for contract code. - fn get_contract_code(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_contract_code(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_CODES: usize = 256; let peers = self.peers.read(); @@ -819,17 +865,19 @@ impl LightProtocol { } stream.out() - }).map_err(Into::into) + }); + + Ok(()) } // Receive a response for contract code. - fn contract_code(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + fn contract_code(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { let req_id = try!(self.pre_verify_response(peer, request::Kind::Codes, &raw)); let raw_code: Vec = try!(raw.iter().skip(2).map(|x| x.as_val()).collect()); for handler in &self.handlers { - handler.on_code(EventContext { + handler.on_code(&Ctx { peer: *peer, io: io, proto: self, @@ -840,7 +888,7 @@ impl LightProtocol { } // Handle a request for header proofs - fn get_header_proofs(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn get_header_proofs(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_PROOFS: usize = 256; let peers = self.peers.read(); @@ -887,11 +935,13 @@ impl LightProtocol { } stream.out() - }).map_err(Into::into) + }); + + Ok(()) } // Receive a response for header proofs - fn header_proofs(&self, peer: &PeerId, io: &NetworkContext, raw: UntrustedRlp) -> Result<(), Error> { + fn header_proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { fn decode_res(raw: UntrustedRlp) -> Result<(Bytes, Vec), ::rlp::DecoderError> { Ok(( try!(raw.val_at(0)), @@ -904,7 +954,7 @@ impl LightProtocol { let raw_proofs: Vec<_> = try!(raw.iter().skip(2).map(decode_res).collect()); for handler in &self.handlers { - handler.on_header_proofs(EventContext { + handler.on_header_proofs(&Ctx { peer: *peer, io: io, proto: self, @@ -915,7 +965,7 @@ impl LightProtocol { } // Receive a set of transactions to relay. - fn relay_transactions(&self, peer: &PeerId, io: &NetworkContext, data: UntrustedRlp) -> Result<(), Error> { + fn relay_transactions(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { const MAX_TRANSACTIONS: usize = 256; let txs: Vec<_> = try!(data.iter().take(MAX_TRANSACTIONS).map(|x| x.as_val::()).collect()); @@ -923,7 +973,7 @@ impl LightProtocol { debug!(target: "les", "Received {} transactions to relay from peer {}", txs.len(), peer); for handler in &self.handlers { - handler.on_transactions(EventContext { + handler.on_transactions(&Ctx { peer: *peer, io: io, proto: self, @@ -940,52 +990,7 @@ 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::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.receipts(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::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 => { - Err(Error::UnrecognizedPacket(other)) - } - }; - - // if something went wrong, figure out how much to punish the peer. - 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) - } - } - } + self.handle_packet(io, peer, packet_id, data); } fn connected(&self, io: &NetworkContext, peer: &PeerId) { From 88e5ed9ea5eb51e2e64bf4ec90c7ac2a57117f79 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 8 Dec 2016 12:18:43 +0100 Subject: [PATCH 05/74] Allow mocha to use webpack aliasses via babel --- js/.babelrc | 9 +++++++++ js/package.json | 3 ++- js/webpack/test.js | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 js/webpack/test.js diff --git a/js/.babelrc b/js/.babelrc index 2298d98c0..33ac89e23 100644 --- a/js/.babelrc +++ b/js/.babelrc @@ -16,6 +16,15 @@ }, "development": { "plugins": ["react-hot-loader/babel"] + }, + "test": { + "plugins": [ + [ + "babel-plugin-webpack-alias", { + "config": "webpack/test.js" + } + ] + ] } } } diff --git a/js/package.json b/js/package.json index dd4ff1046..f46619a3a 100644 --- a/js/package.json +++ b/js/package.json @@ -40,7 +40,7 @@ "coveralls": "npm run testCoverage && coveralls < coverage/lcov.info", "lint": "eslint --ignore-path .gitignore ./src/", "lint:cached": "eslint --cache --ignore-path .gitignore ./src/", - "test": "mocha 'src/**/*.spec.js'", + "test": "NODE_ENV=test mocha 'src/**/*.spec.js'", "test:coverage": "istanbul cover _mocha -- 'src/**/*.spec.js'", "test:e2e": "mocha 'src/**/*.e2e.js'", "test:npm": "(cd .npmjs && npm i) && node test/npmLibrary && (rm -rf .npmjs/node_modules)", @@ -56,6 +56,7 @@ "babel-plugin-transform-decorators-legacy": "1.3.4", "babel-plugin-transform-react-remove-prop-types": "0.2.11", "babel-plugin-transform-runtime": "6.15.0", + "babel-plugin-webpack-alias": "2.1.2", "babel-polyfill": "6.16.0", "babel-preset-es2015": "6.18.0", "babel-preset-es2015-rollup": "1.2.0", diff --git a/js/webpack/test.js b/js/webpack/test.js new file mode 100644 index 000000000..a0340b46b --- /dev/null +++ b/js/webpack/test.js @@ -0,0 +1,26 @@ +// 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 . + +const path = require('path'); + +module.exports = { + context: path.join(__dirname, '../src'), + resolve: { + alias: { + '~': path.resolve(__dirname, '../src') + } + } +}; From ad8a191e95a88187d03c3ce5a5709358b2d6657e Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 8 Dec 2016 12:19:03 +0100 Subject: [PATCH 06/74] Adjust specs to show location --- js/src/dapps/localtx/Application/application.spec.js | 2 +- js/src/dapps/localtx/Transaction/transaction.spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/dapps/localtx/Application/application.spec.js b/js/src/dapps/localtx/Application/application.spec.js index 2044b4e14..3ffee343d 100644 --- a/js/src/dapps/localtx/Application/application.spec.js +++ b/js/src/dapps/localtx/Application/application.spec.js @@ -21,7 +21,7 @@ import '../../../environment/tests'; import Application from './application'; -describe('localtx/Application', () => { +describe('dapps/localtx/Application', () => { describe('rendering', () => { it('renders without crashing', () => { const rendered = shallow(); diff --git a/js/src/dapps/localtx/Transaction/transaction.spec.js b/js/src/dapps/localtx/Transaction/transaction.spec.js index 5e9c39147..04f2f8de8 100644 --- a/js/src/dapps/localtx/Transaction/transaction.spec.js +++ b/js/src/dapps/localtx/Transaction/transaction.spec.js @@ -29,7 +29,7 @@ Api.api = { import BigNumber from 'bignumber.js'; import { Transaction, LocalTransaction } from './transaction'; -describe('localtx/Transaction', () => { +describe('dapps/localtx/Transaction', () => { describe('rendering', () => { it('renders without crashing', () => { const transaction = { @@ -51,7 +51,7 @@ describe('localtx/Transaction', () => { }); }); -describe('localtx/LocalTransaction', () => { +describe('dapps/localtx/LocalTransaction', () => { describe('rendering', () => { it('renders without crashing', () => { const rendered = shallow( From 8d16f737952eb4998cd35092749d78c7d870744f Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Dec 2016 12:20:18 +0100 Subject: [PATCH 07/74] first few LES tests, fix get_header logic bug --- ethcore/light/src/net/context.rs | 113 +++++++++++ ethcore/light/src/net/mod.rs | 8 +- ethcore/light/src/net/tests/mod.rs | 291 +++++++++++++++++++++++++++++ ethcore/light/src/provider.rs | 4 +- ethcore/src/client/test_client.rs | 2 +- 5 files changed, 412 insertions(+), 6 deletions(-) create mode 100644 ethcore/light/src/net/context.rs create mode 100644 ethcore/light/src/net/tests/mod.rs diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs new file mode 100644 index 000000000..f2d5ab907 --- /dev/null +++ b/ethcore/light/src/net/context.rs @@ -0,0 +1,113 @@ +// 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 . + +//! I/O and event context generalizations. + +use network::{NetworkContext, PeerId}; + +use super::{Announcement, LightProtocol, ReqId}; +use super::error::Error; +use request::Request; + +/// An I/O context which allows sending and receiving packets as well as +/// disconnecting peers. This is used as a generalization of the portions +/// of a p2p network which the light protocol structure makes use of. +pub trait IoContext { + /// Send a packet to a specific peer. + fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec); + + /// Respond to a peer's message. Only works if this context is a byproduct + /// of a packet handler. + fn respond(&self, packet_id: u8, packet_body: Vec); + + /// Disconnect a peer. + fn disconnect_peer(&self, peer: PeerId); + + /// Disable a peer -- this is a disconnect + a time-out. + fn disable_peer(&self, peer: PeerId); +} + +impl<'a> IoContext for NetworkContext<'a> { + fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { + if let Err(e) = self.send(peer, packet_id, packet_body) { + debug!(target: "les", "Error sending packet to peer {}: {}", peer, e); + } + } + + fn respond(&self, packet_id: u8, packet_body: Vec) { + if let Err(e) = self.respond(packet_id, packet_body) { + debug!(target: "les", "Error responding to peer message: {}", e); + } + } + + fn disconnect_peer(&self, peer: PeerId) { + NetworkContext::disconnect_peer(self, peer); + } + + fn disable_peer(&self, peer: PeerId) { + NetworkContext::disable_peer(self, peer); + } +} + +/// Context for a protocol event. +pub trait EventContext { + /// Get the peer relevant to the event e.g. message sender, + /// disconnected/connected peer. + fn peer(&self) -> PeerId; + + /// Make a request from a peer. + fn request_from(&self, peer: PeerId, request: Request) -> Result; + + /// Make an announcement of new capabilities to the rest of the peers. + // TODO: maybe just put this on a timer in LightProtocol? + fn make_announcement(&self, announcement: Announcement); + + /// Disconnect a peer. + fn disconnect_peer(&self, peer: PeerId); + + /// Disable a peer. + fn disable_peer(&self, peer: PeerId); +} + +/// Concrete implementation of `EventContext` over the light protocol struct and +/// an io context. +pub struct Ctx<'a> { + /// Io context to enable immediate response to events. + pub io: &'a IoContext, + /// Protocol implementation. + pub proto: &'a LightProtocol, + /// Relevant peer for event. + pub peer: PeerId, +} + +impl<'a> EventContext for Ctx<'a> { + fn peer(&self) -> PeerId { self.peer } + fn request_from(&self, peer: PeerId, request: Request) -> Result { + self.proto.request_from(self.io, &peer, request) + } + + fn make_announcement(&self, announcement: Announcement) { + self.proto.make_announcement(self.io, announcement); + } + + fn disconnect_peer(&self, peer: PeerId) { + self.io.disconnect_peer(peer); + } + + fn disable_peer(&self, peer: PeerId) { + self.io.disable_peer(peer); + } +} \ No newline at end of file diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 965e46db6..57d091f75 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -44,6 +44,9 @@ mod context; mod error; mod status; +#[cfg(test)] +mod tests; + pub use self::status::{Status, Capabilities, Announcement, NetworkId}; const TIMEOUT: TimerToken = 0; @@ -181,8 +184,6 @@ struct Requested { /// Protocol parameters. pub struct Params { - /// Genesis hash. - pub genesis_hash: H256, /// Network id. pub network_id: NetworkId, /// Buffer flow parameters. @@ -217,9 +218,10 @@ pub struct LightProtocol { impl LightProtocol { /// Create a new instance of the protocol manager. pub fn new(provider: Box, params: Params) -> Self { + let genesis_hash = provider.chain_info().genesis_hash; LightProtocol { provider: provider, - genesis_hash: params.genesis_hash, + genesis_hash: genesis_hash, network_id: params.network_id, pending_peers: RwLock::new(HashMap::new()), peers: RwLock::new(HashMap::new()), diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs new file mode 100644 index 000000000..7db05542e --- /dev/null +++ b/ethcore/light/src/net/tests/mod.rs @@ -0,0 +1,291 @@ +// 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 . + +//! Tests for the `LightProtocol` implementation. +//! These don't test of the higher level logic on top of + +use ethcore::blockchain_info::BlockChainInfo; +use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; +use ethcore::ids::BlockID; +use ethcore::transaction::SignedTransaction; +use network::PeerId; + +use net::buffer_flow::FlowParams; +use net::context::IoContext; +use net::status::{Capabilities, Status, NetworkId, write_handshake}; +use net::{encode_request, LightProtocol, Params, packet}; +use provider::Provider; +use request::{self, Request, Headers}; + +use rlp::*; +use util::{Bytes, H256}; + +use std::sync::Arc; + +// expected result from a call. +#[derive(Debug, PartialEq, Eq)] +enum Expect { + /// Expect to have message sent to peer. + Send(PeerId, u8, Vec), + /// Expect this response. + Respond(u8, Vec), + /// Expect a punishment (disconnect/disable) + Punish(PeerId), + /// Expect nothing. + Nothing, +} + +impl IoContext for Expect { + fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { + assert_eq!(self, &Expect::Send(peer, packet_id, packet_body)); + } + + fn respond(&self, packet_id: u8, packet_body: Vec) { + assert_eq!(self, &Expect::Respond(packet_id, packet_body)); + } + + fn disconnect_peer(&self, peer: PeerId) { + assert_eq!(self, &Expect::Punish(peer)); + } + + fn disable_peer(&self, peer: PeerId) { + assert_eq!(self, &Expect::Punish(peer)); + } +} + +// can't implement directly for Arc due to cross-crate orphan rules. +struct TestProvider(Arc); + +struct TestProviderInner { + client: TestBlockChainClient, +} + +impl Provider for TestProvider { + fn chain_info(&self) -> BlockChainInfo { + self.0.client.chain_info() + } + + fn reorg_depth(&self, a: &H256, b: &H256) -> Option { + self.0.client.tree_route(a, b).map(|route| route.index as u64) + } + + fn earliest_state(&self) -> Option { + None + } + + fn block_headers(&self, req: request::Headers) -> Vec { + let best_num = self.0.client.chain_info().best_block_number; + let start_num = req.block_num; + + match self.0.client.block_hash(BlockID::Number(req.block_num)) { + Some(hash) if hash == req.block_hash => {} + _=> { + trace!(target: "les_provider", "unknown/non-canonical start block in header request: {:?}", (req.block_num, req.block_hash)); + return vec![] + } + } + + (0u64..req.max as u64) + .map(|x: u64| x.saturating_mul(req.skip + 1)) + .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.0.client.block_header(BlockID::Number(x))) + .take_while(|x| x.is_some()) + .flat_map(|x| x) + .collect() + } + + fn block_bodies(&self, req: request::Bodies) -> Vec { + req.block_hashes.into_iter() + .map(|hash| self.0.client.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.0.client.block_receipts(&hash)) + .map(|receipts| receipts.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) + .collect() + } + + fn proofs(&self, req: request::StateProofs) -> Vec { + req.requests.into_iter().map(|_| ::rlp::EMPTY_LIST_RLP.to_vec()).collect() + } + + fn contract_code(&self, req: request::ContractCodes) -> Vec { + req.code_requests.into_iter().map(|_| 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 { + self.0.client.pending_transactions() + } +} + +fn make_flow_params() -> FlowParams { + FlowParams::new(5_000_000.into(), Default::default(), 100_000.into()) +} + +fn capabilities() -> Capabilities { + Capabilities { + serve_headers: true, + serve_chain_since: Some(1), + serve_state_since: Some(1), + tx_relay: true, + } +} + +// helper for setting up the protocol handler and provider. +fn setup(flow_params: FlowParams, capabilities: Capabilities) -> (Arc, LightProtocol) { + let provider = Arc::new(TestProviderInner { + client: TestBlockChainClient::new(), + }); + + let proto = LightProtocol::new(Box::new(TestProvider(provider.clone())), Params { + network_id: NetworkId::Testnet, + flow_params: flow_params, + capabilities: capabilities, + }); + + (provider, proto) +} + +fn status(chain_info: BlockChainInfo) -> Status { + Status { + protocol_version: ::net::PROTOCOL_VERSION, + network_id: NetworkId::Testnet, + 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, + last_head: None, + } +} + +#[test] +fn handshake_expected() { + let flow_params = make_flow_params(); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let status = status(provider.client.chain_info()); + + let packet_body = write_handshake(&status, &capabilities, &flow_params); + + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); +} + +#[test] +#[should_panic] +fn genesis_mismatch() { + let flow_params = make_flow_params(); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let mut status = status(provider.client.chain_info()); + status.genesis_hash = H256::default(); + + let packet_body = write_handshake(&status, &capabilities, &flow_params); + + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); +} + +#[test] +fn buffer_overflow() { + let flow_params = make_flow_params(); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + } + + { + let my_status = write_handshake(&status, &capabilities, &flow_params); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } + + // 1000 requests is far too many for the default flow params. + let request = encode_request(&Request::Headers(Headers { + block_num: 1, + block_hash: provider.client.chain_info().genesis_hash, + max: 1000, + skip: 0, + reverse: false, + }), 111); + + proto.handle_packet(&Expect::Punish(1), &1, packet::GET_BLOCK_HEADERS, &request); +} + +#[test] +fn get_block_headers() { + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + + provider.client.add_blocks(100, EachBlockWith::Nothing); + + let cur_status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } + + let request = Headers { + block_num: 1, + block_hash: provider.client.block_hash(BlockID::Number(1)).unwrap(), + max: 10, + skip: 0, + reverse: false, + }; + let req_id = 111; + + let request_body = encode_request(&Request::Headers(request.clone()), req_id); + let response = { + let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockID::Number(i + 1)).unwrap()).collect(); + assert_eq!(headers.len(), 10); + + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Headers, 10); + + let mut response_stream = RlpStream::new_list(12); + + response_stream.append(&req_id).append(&new_buf); + for header in headers { + response_stream.append_raw(&header, 1); + } + + response_stream.out() + }; + + let expected = Expect::Respond(packet::BLOCK_HEADERS, response); + proto.handle_packet(&expected, &1, packet::GET_BLOCK_HEADERS, &request_body); +} \ No newline at end of file diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index fe7156b58..9446aa3f6 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -108,8 +108,8 @@ impl Provider for T { } (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: u64| x.saturating_mul(req.skip + 1)) + .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()) diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index dd00db7ec..37674e29e 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -92,8 +92,8 @@ pub struct TestBlockChainClient { pub first_block: RwLock>, } -#[derive(Clone)] /// Used for generating test client blocks. +#[derive(Clone)] pub enum EachBlockWith { /// Plain block. Nothing, From b3f7c00780cd4ff9c475afb880a621424d9d62d4 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 8 Dec 2016 12:22:40 +0100 Subject: [PATCH 08/74] ui/Container tests as per #2289 --- js/src/ui/Container/Title/title.js | 4 -- js/src/ui/Container/Title/title.spec.js | 52 +++++++++++++++++++++++++ js/src/ui/Container/container.spec.js | 38 ++++++++++++++++++ 3 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 js/src/ui/Container/Title/title.spec.js create mode 100644 js/src/ui/Container/container.spec.js diff --git a/js/src/ui/Container/Title/title.js b/js/src/ui/Container/Title/title.js index 485340d2a..1506ecaf6 100644 --- a/js/src/ui/Container/Title/title.js +++ b/js/src/ui/Container/Title/title.js @@ -27,10 +27,6 @@ export default class Title extends Component { byline: nodeOrStringProptype() } - state = { - name: 'Unnamed' - } - render () { const { className, title, byline } = this.props; diff --git a/js/src/ui/Container/Title/title.spec.js b/js/src/ui/Container/Title/title.spec.js new file mode 100644 index 000000000..7c6dc05b6 --- /dev/null +++ b/js/src/ui/Container/Title/title.spec.js @@ -0,0 +1,52 @@ +// 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 from 'react'; +import { mount, shallow } from 'enzyme'; + +import Title from './title'; + +function renderShallow (props) { + return shallow( + + ); +} + +function renderMount (props) { + return mount( + <Title { ...props } /> + ); +} + +describe('ui/Container/Title', () => { + describe('rendering', () => { + it('renders without crashing', () => { + expect(renderShallow()).to.be.defined; + }); + + it('renders with the specified className', () => { + expect(renderShallow({ className: 'testClass' })).to.have.className('testClass'); + }); + + it('renders the specified title', () => { + expect(renderMount({ title: 'titleText' })).to.contain.text('titleText'); + }); + + it('renders the specified byline', () => { + expect(renderMount({ byline: 'bylineText' })).to.contain.text('bylineText'); + }); + }); +}); diff --git a/js/src/ui/Container/container.spec.js b/js/src/ui/Container/container.spec.js new file mode 100644 index 000000000..865522d7e --- /dev/null +++ b/js/src/ui/Container/container.spec.js @@ -0,0 +1,38 @@ +// 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 <http://www.gnu.org/licenses/>. + +import React from 'react'; +import { shallow } from 'enzyme'; + +import Container from './title'; + +function renderShallow (props) { + return shallow( + <Container { ...props } /> + ); +} + +describe('ui/Container', () => { + describe('rendering', () => { + it('renders without crashing', () => { + expect(renderShallow()).to.be.defined; + }); + + it('renders with the specified className', () => { + expect(renderShallow({ className: 'testClass' })).to.have.className('testClass'); + }); + }); +}); From 543428fe5d3a8917cc08774111538328bdfa16e0 Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jaco@ethcore.io> Date: Thu, 8 Dec 2016 12:59:59 +0100 Subject: [PATCH 09/74] NODE_ENV=test for all tests --- js/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/package.json b/js/package.json index f46619a3a..a5fab74d5 100644 --- a/js/package.json +++ b/js/package.json @@ -41,8 +41,8 @@ "lint": "eslint --ignore-path .gitignore ./src/", "lint:cached": "eslint --cache --ignore-path .gitignore ./src/", "test": "NODE_ENV=test mocha 'src/**/*.spec.js'", - "test:coverage": "istanbul cover _mocha -- 'src/**/*.spec.js'", - "test:e2e": "mocha 'src/**/*.e2e.js'", + "test:coverage": "NODE_ENV=test istanbul cover _mocha -- 'src/**/*.spec.js'", + "test:e2e": "NODE_ENV=test mocha 'src/**/*.e2e.js'", "test:npm": "(cd .npmjs && npm i) && node test/npmLibrary && (rm -rf .npmjs/node_modules)", "prepush": "npm run lint:cached" }, From a5b5277a88388dd7fda717eca85553e7eec919cd Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jaco@ethcore.io> Date: Thu, 8 Dec 2016 13:00:29 +0100 Subject: [PATCH 10/74] Expanded basic tests for #2289 --- js/src/ui/Actionbar/actionbar.spec.js | 38 +++++++++++++++++++++++++ js/src/ui/Badge/badge.spec.js | 38 +++++++++++++++++++++++++ js/src/ui/Button/button.spec.js | 38 +++++++++++++++++++++++++ js/src/ui/Container/Title/title.spec.js | 4 +-- js/src/ui/Container/container.spec.js | 6 ++-- 5 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 js/src/ui/Actionbar/actionbar.spec.js create mode 100644 js/src/ui/Badge/badge.spec.js create mode 100644 js/src/ui/Button/button.spec.js diff --git a/js/src/ui/Actionbar/actionbar.spec.js b/js/src/ui/Actionbar/actionbar.spec.js new file mode 100644 index 000000000..818eb0f80 --- /dev/null +++ b/js/src/ui/Actionbar/actionbar.spec.js @@ -0,0 +1,38 @@ +// 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 <http://www.gnu.org/licenses/>. + +import React from 'react'; +import { shallow } from 'enzyme'; + +import Actionbar from './actionbar'; + +function renderShallow (props) { + return shallow( + <Actionbar { ...props } /> + ); +} + +describe('ui/Actionbar', () => { + describe('rendering', () => { + it('renders defaults', () => { + expect(renderShallow()).to.be.ok; + }); + + it('renders with the specified className', () => { + expect(renderShallow({ className: 'testClass' })).to.have.className('testClass'); + }); + }); +}); diff --git a/js/src/ui/Badge/badge.spec.js b/js/src/ui/Badge/badge.spec.js new file mode 100644 index 000000000..8074a8688 --- /dev/null +++ b/js/src/ui/Badge/badge.spec.js @@ -0,0 +1,38 @@ +// 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 <http://www.gnu.org/licenses/>. + +import React from 'react'; +import { shallow } from 'enzyme'; + +import Badge from './badge'; + +function renderShallow (props) { + return shallow( + <Badge { ...props } /> + ); +} + +describe('ui/Badge', () => { + describe('rendering', () => { + it('renders defaults', () => { + expect(renderShallow()).to.be.ok; + }); + + it('renders with the specified className', () => { + expect(renderShallow({ className: 'testClass' })).to.have.className('testClass'); + }); + }); +}); diff --git a/js/src/ui/Button/button.spec.js b/js/src/ui/Button/button.spec.js new file mode 100644 index 000000000..1d5f5beed --- /dev/null +++ b/js/src/ui/Button/button.spec.js @@ -0,0 +1,38 @@ +// 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 <http://www.gnu.org/licenses/>. + +import React from 'react'; +import { shallow } from 'enzyme'; + +import Button from './button'; + +function renderShallow (props) { + return shallow( + <Button { ...props } /> + ); +} + +describe.only('ui/Button', () => { + describe('rendering', () => { + it('renders defaults', () => { + expect(renderShallow()).to.be.ok; + }); + + it('renders with the specified className', () => { + expect(renderShallow({ className: 'testClass' })).to.have.className('testClass'); + }); + }); +}); diff --git a/js/src/ui/Container/Title/title.spec.js b/js/src/ui/Container/Title/title.spec.js index 7c6dc05b6..a65e221a6 100644 --- a/js/src/ui/Container/Title/title.spec.js +++ b/js/src/ui/Container/Title/title.spec.js @@ -33,8 +33,8 @@ function renderMount (props) { describe('ui/Container/Title', () => { describe('rendering', () => { - it('renders without crashing', () => { - expect(renderShallow()).to.be.defined; + it('renders defaults', () => { + expect(renderShallow()).to.be.ok; }); it('renders with the specified className', () => { diff --git a/js/src/ui/Container/container.spec.js b/js/src/ui/Container/container.spec.js index 865522d7e..9bbc9d20f 100644 --- a/js/src/ui/Container/container.spec.js +++ b/js/src/ui/Container/container.spec.js @@ -17,7 +17,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import Container from './title'; +import Container from './container'; function renderShallow (props) { return shallow( @@ -27,8 +27,8 @@ function renderShallow (props) { describe('ui/Container', () => { describe('rendering', () => { - it('renders without crashing', () => { - expect(renderShallow()).to.be.defined; + it('renders defaults', () => { + expect(renderShallow()).to.be.ok; }); it('renders with the specified className', () => { From 3e8ee9d423e860430fb70f3b45851f2a018c34e2 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Thu, 8 Dec 2016 13:44:17 +0100 Subject: [PATCH 11/74] light IPC feature and mock state proofs --- Cargo.lock | 2 - ethcore/light/Cargo.toml | 9 +- ethcore/light/build.rs | 5 + ethcore/light/src/lib.rs | 6 +- ethcore/light/src/net/tests/mod.rs | 164 ++++++++++++++++++++++++- ethcore/light/src/types/les_request.rs | 33 +++-- ethcore/light/src/types/mod.rs | 7 +- 7 files changed, 206 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b00701f32..4a5534dbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,8 +462,6 @@ version = "1.5.0" dependencies = [ "ethcore 1.5.0", "ethcore-io 1.5.0", - "ethcore-ipc 1.4.0", - "ethcore-ipc-codegen 1.4.0", "ethcore-network 1.5.0", "ethcore-util 1.5.0", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 9440bbf88..e80eb3e2f 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Ethcore <admin@ethcore.io>"] build = "build.rs" [build-dependencies] -"ethcore-ipc-codegen" = { path = "../../ipc/codegen" } +"ethcore-ipc-codegen" = { path = "../../ipc/codegen", optional = true } [dependencies] log = "0.3" @@ -16,6 +16,9 @@ ethcore = { path = "..", features = ["light"] } ethcore-util = { path = "../../util" } ethcore-network = { path = "../../util/network" } ethcore-io = { path = "../../util/io" } -ethcore-ipc = { path = "../../ipc/rpc" } +ethcore-ipc = { path = "../../ipc/rpc", optional = true } rlp = { path = "../../util/rlp" } -time = "0.1" \ No newline at end of file +time = "0.1" + +[features] +ipc = ["ethcore/ipc", "ethcore-ipc", "ethcore-ipc-codegen"] \ No newline at end of file diff --git a/ethcore/light/build.rs b/ethcore/light/build.rs index cff92a011..43915c1cf 100644 --- a/ethcore/light/build.rs +++ b/ethcore/light/build.rs @@ -14,8 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. +#[cfg(feature = "ipc")] extern crate ethcore_ipc_codegen; +#[cfg(feature = "ipc")] fn main() { ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap(); } + +#[cfg(not(feature = "ipc"))] +fn main() { } \ No newline at end of file diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index e150f4ee5..c00467c4c 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -47,6 +47,8 @@ extern crate ethcore; extern crate ethcore_util as util; extern crate ethcore_network as network; extern crate ethcore_io as io; -extern crate ethcore_ipc as ipc; extern crate rlp; -extern crate time; \ No newline at end of file +extern crate time; + +#[cfg(feature = "ipc")] +extern crate ethcore_ipc as ipc; \ No newline at end of file diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 7db05542e..555f6d19c 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -123,7 +123,19 @@ impl Provider for TestProvider { } fn proofs(&self, req: request::StateProofs) -> Vec<Bytes> { - req.requests.into_iter().map(|_| ::rlp::EMPTY_LIST_RLP.to_vec()).collect() + req.requests.into_iter() + .map(|req| { + match req.key2 { + Some(key2) => ::util::sha3::SHA3_NULL_RLP.to_owned(), + None => { + // sort of a leaf node + let mut stream = RlpStream::new_list(2); + stream.append(&req.key1).append_empty_data(); + stream.out() + } + } + }) + .collect() } fn contract_code(&self, req: request::ContractCodes) -> Vec<Bytes> { @@ -240,6 +252,9 @@ fn buffer_overflow() { proto.handle_packet(&Expect::Punish(1), &1, packet::GET_BLOCK_HEADERS, &request); } +// test the basic request types -- these just make sure that requests are parsed +// and sent to the provider correctly as well as testing response formatting. + #[test] fn get_block_headers() { let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); @@ -288,4 +303,151 @@ fn get_block_headers() { let expected = Expect::Respond(packet::BLOCK_HEADERS, response); proto.handle_packet(&expected, &1, packet::GET_BLOCK_HEADERS, &request_body); +} + +#[test] +fn get_block_bodies() { + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + + provider.client.add_blocks(100, EachBlockWith::Nothing); + + let cur_status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } + + let request = request::Bodies { + block_hashes: (0..10).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()).collect(), + }; + + let req_id = 111; + + let request_body = encode_request(&Request::Bodies(request.clone()), req_id); + let response = { + let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockID::Number(i + 1)).unwrap()).collect(); + assert_eq!(bodies.len(), 10); + + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10); + + let mut response_stream = RlpStream::new_list(12); + + response_stream.append(&req_id).append(&new_buf); + for body in bodies { + response_stream.append_raw(&body, 1); + } + + response_stream.out() + }; + + let expected = Expect::Respond(packet::BLOCK_BODIES, response); + proto.handle_packet(&expected, &1, packet::GET_BLOCK_BODIES, &request_body); +} + +#[test] +fn get_block_receipts() { + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + + provider.client.add_blocks(1000, EachBlockWith::Nothing); + + let cur_status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } + + // find the first 10 block hashes starting with `f` because receipts are only provided + // by the test client in that case. + let block_hashes: Vec<_> = (0..1000).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()) + .filter(|hash| format!("{}", hash).starts_with("f")).take(10).collect(); + + let request = request::Receipts { + block_hashes: block_hashes.clone(), + }; + + let req_id = 111; + + let request_body = encode_request(&Request::Receipts(request.clone()), req_id); + let response = { + let receipts: Vec<_> = block_hashes.iter() + .map(|hash| provider.client.block_receipts(hash).unwrap()) + .collect(); + + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Receipts, receipts.len()); + + let mut response_stream = RlpStream::new_list(2 + receipts.len()); + + response_stream.append(&req_id).append(&new_buf); + for block_receipts in receipts { + response_stream.append_raw(&block_receipts, 1); + } + + response_stream.out() + }; + + let expected = Expect::Respond(packet::RECEIPTS, response); + proto.handle_packet(&expected, &1, packet::GET_RECEIPTS, &request_body); +} + +#[test] +fn get_block_bodies() { + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + + provider.client.add_blocks(100, EachBlockWith::Nothing); + + let cur_status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } + + let request = request::Bodies { + block_hashes: (0..10).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()).collect(), + }; + + let req_id = 111; + + let request_body = encode_request(&Request::Bodies(request.clone()), req_id); + let response = { + let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockID::Number(i + 1)).unwrap()).collect(); + assert_eq!(bodies.len(), 10); + + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10); + + let mut response_stream = RlpStream::new_list(12); + + response_stream.append(&req_id).append(&new_buf); + for body in bodies { + response_stream.append_raw(&body, 1); + } + + response_stream.out() + }; + + let expected = Expect::Respond(packet::BLOCK_BODIES, response); + proto.handle_packet(&expected, &1, packet::GET_BLOCK_BODIES, &request_body); } \ No newline at end of file diff --git a/ethcore/light/src/types/les_request.rs b/ethcore/light/src/types/les_request.rs index d0de080ee..49bd2e9cc 100644 --- a/ethcore/light/src/types/les_request.rs +++ b/ethcore/light/src/types/les_request.rs @@ -19,7 +19,8 @@ use util::H256; /// A request for block headers. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct Headers { /// Starting block number pub block_num: u64, @@ -35,7 +36,8 @@ pub struct Headers { } /// A request for specific block bodies. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct Bodies { /// Hashes which bodies are being requested for. pub block_hashes: Vec<H256> @@ -45,14 +47,16 @@ pub struct Bodies { /// /// This request is answered with a list of transaction receipts for each block /// requested. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct Receipts { /// Block hashes to return receipts for. pub block_hashes: Vec<H256>, } /// A request for a state proof -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct StateProof { /// Block hash to query state from. pub block: H256, @@ -66,14 +70,16 @@ pub struct StateProof { } /// A request for state proofs. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct StateProofs { /// All the proof requests. pub requests: Vec<StateProof>, } /// A request for contract code. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct ContractCode { /// Block hash pub block_hash: H256, @@ -82,14 +88,16 @@ pub struct ContractCode { } /// A request for contract code. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct ContractCodes { /// Block hash and account key (== sha3(address)) pairs to fetch code for. pub code_requests: Vec<ContractCode>, } /// A request for a header proof from the Canonical Hash Trie. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct HeaderProof { /// Number of the CHT. pub cht_number: u64, @@ -100,14 +108,16 @@ pub struct HeaderProof { } /// A request for header proofs from the CHT. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct HeaderProofs { /// All the proof requests. pub requests: Vec<HeaderProof>, } /// Kinds of requests. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub enum Kind { /// Requesting headers. Headers, @@ -124,7 +134,8 @@ pub enum Kind { } /// Encompasses all possible types of requests in a single structure. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub enum Request { /// Requesting headers. Headers(Headers), diff --git a/ethcore/light/src/types/mod.rs b/ethcore/light/src/types/mod.rs index 2625358a3..d7f473553 100644 --- a/ethcore/light/src/types/mod.rs +++ b/ethcore/light/src/types/mod.rs @@ -15,6 +15,11 @@ // along with Parity. If not, see <http://www.gnu.org/licenses/>. //! Types used in the public (IPC) api which require custom code generation. +#![cfg_attr(feature = "ipc", allow(dead_code, unused_assignments, unused_variables))] // codegen issues -#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues + +#[cfg(feature = "ipc")] include!(concat!(env!("OUT_DIR"), "/mod.rs.in")); + +#[cfg(not(feature = "ipc"))] +include!("mod.rs.in"); \ No newline at end of file From 1ab4ee37817c61e8f039e8320c6a0f9e725b5b98 Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jaco@ethcore.io> Date: Thu, 8 Dec 2016 14:53:57 +0100 Subject: [PATCH 12/74] Update Button with correct proptypes --- js/src/ui/Button/button.js | 7 +++---- js/src/ui/Button/button.spec.js | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/js/src/ui/Button/button.js b/js/src/ui/Button/button.js index 48dbc981d..6ae113fd4 100644 --- a/js/src/ui/Button/button.js +++ b/js/src/ui/Button/button.js @@ -17,16 +17,15 @@ import React, { Component, PropTypes } from 'react'; import { FlatButton } from 'material-ui'; +import { nodeOrStringProptype } from '~/util/proptypes'; + export default class Button extends Component { static propTypes = { backgroundColor: PropTypes.string, className: PropTypes.string, disabled: PropTypes.bool, icon: PropTypes.node, - label: PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.object - ]), + label: nodeOrStringProptype(), onClick: PropTypes.func, primary: PropTypes.bool } diff --git a/js/src/ui/Button/button.spec.js b/js/src/ui/Button/button.spec.js index 1d5f5beed..9ae4ecdfc 100644 --- a/js/src/ui/Button/button.spec.js +++ b/js/src/ui/Button/button.spec.js @@ -25,7 +25,7 @@ function renderShallow (props) { ); } -describe.only('ui/Button', () => { +describe('ui/Button', () => { describe('rendering', () => { it('renders defaults', () => { expect(renderShallow()).to.be.ok; From 64ebcd0f24bda255647daa31bcda07e6fa4dd3e2 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Thu, 8 Dec 2016 16:12:00 +0100 Subject: [PATCH 13/74] fix contract code response encoding; add test --- ethcore/light/src/net/mod.rs | 2 +- ethcore/light/src/net/tests/mod.rs | 531 ++++++++++++++++------------- 2 files changed, 294 insertions(+), 239 deletions(-) diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 57d091f75..57dc77a86 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -863,7 +863,7 @@ impl LightProtocol { stream.append(&req_id).append(&cur_buffer); for code in response { - stream.append_raw(&code, 1); + stream.append(&code); } stream.out() diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 555f6d19c..e659d0681 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -31,62 +31,62 @@ use provider::Provider; use request::{self, Request, Headers}; use rlp::*; -use util::{Bytes, H256}; +use util::{Bytes, H256, U256}; use std::sync::Arc; // expected result from a call. #[derive(Debug, PartialEq, Eq)] enum Expect { - /// Expect to have message sent to peer. - Send(PeerId, u8, Vec<u8>), - /// Expect this response. - Respond(u8, Vec<u8>), - /// Expect a punishment (disconnect/disable) - Punish(PeerId), - /// Expect nothing. - Nothing, + /// Expect to have message sent to peer. + Send(PeerId, u8, Vec<u8>), + /// Expect this response. + Respond(u8, Vec<u8>), + /// Expect a punishment (disconnect/disable) + Punish(PeerId), + /// Expect nothing. + Nothing, } impl IoContext for Expect { - fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec<u8>) { - assert_eq!(self, &Expect::Send(peer, packet_id, packet_body)); - } + fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec<u8>) { + assert_eq!(self, &Expect::Send(peer, packet_id, packet_body)); + } - fn respond(&self, packet_id: u8, packet_body: Vec<u8>) { - assert_eq!(self, &Expect::Respond(packet_id, packet_body)); - } + fn respond(&self, packet_id: u8, packet_body: Vec<u8>) { + assert_eq!(self, &Expect::Respond(packet_id, packet_body)); + } - fn disconnect_peer(&self, peer: PeerId) { - assert_eq!(self, &Expect::Punish(peer)); - } + fn disconnect_peer(&self, peer: PeerId) { + assert_eq!(self, &Expect::Punish(peer)); + } - fn disable_peer(&self, peer: PeerId) { - assert_eq!(self, &Expect::Punish(peer)); - } + fn disable_peer(&self, peer: PeerId) { + assert_eq!(self, &Expect::Punish(peer)); + } } // can't implement directly for Arc due to cross-crate orphan rules. struct TestProvider(Arc<TestProviderInner>); struct TestProviderInner { - client: TestBlockChainClient, + client: TestBlockChainClient, } impl Provider for TestProvider { - fn chain_info(&self) -> BlockChainInfo { - self.0.client.chain_info() - } + fn chain_info(&self) -> BlockChainInfo { + self.0.client.chain_info() + } - fn reorg_depth(&self, a: &H256, b: &H256) -> Option<u64> { - self.0.client.tree_route(a, b).map(|route| route.index as u64) - } + fn reorg_depth(&self, a: &H256, b: &H256) -> Option<u64> { + self.0.client.tree_route(a, b).map(|route| route.index as u64) + } - fn earliest_state(&self) -> Option<u64> { - None - } + fn earliest_state(&self) -> Option<u64> { + None + } - fn block_headers(&self, req: request::Headers) -> Vec<Bytes> { + fn block_headers(&self, req: request::Headers) -> Vec<Bytes> { let best_num = self.0.client.chain_info().best_block_number; let start_num = req.block_num; @@ -124,22 +124,26 @@ impl Provider for TestProvider { fn proofs(&self, req: request::StateProofs) -> Vec<Bytes> { req.requests.into_iter() - .map(|req| { - match req.key2 { - Some(key2) => ::util::sha3::SHA3_NULL_RLP.to_owned(), - None => { - // sort of a leaf node - let mut stream = RlpStream::new_list(2); - stream.append(&req.key1).append_empty_data(); - stream.out() - } - } - }) - .collect() + .map(|req| { + match req.key2 { + Some(_) => ::util::sha3::SHA3_NULL_RLP.to_vec(), + None => { + // sort of a leaf node + let mut stream = RlpStream::new_list(2); + stream.append(&req.key1).append_empty_data(); + stream.out() + } + } + }) + .collect() } fn contract_code(&self, req: request::ContractCodes) -> Vec<Bytes> { - req.code_requests.into_iter().map(|_| Vec::new()).collect() + req.code_requests.into_iter() + .map(|req| { + req.account_key.iter().chain(req.account_key.iter()).cloned().collect() + }) + .collect() } fn header_proofs(&self, req: request::HeaderProofs) -> Vec<Bytes> { @@ -152,104 +156,104 @@ impl Provider for TestProvider { } fn make_flow_params() -> FlowParams { - FlowParams::new(5_000_000.into(), Default::default(), 100_000.into()) + FlowParams::new(5_000_000.into(), Default::default(), 100_000.into()) } fn capabilities() -> Capabilities { - Capabilities { - serve_headers: true, - serve_chain_since: Some(1), - serve_state_since: Some(1), - tx_relay: true, - } + Capabilities { + serve_headers: true, + serve_chain_since: Some(1), + serve_state_since: Some(1), + tx_relay: true, + } } // helper for setting up the protocol handler and provider. fn setup(flow_params: FlowParams, capabilities: Capabilities) -> (Arc<TestProviderInner>, LightProtocol) { - let provider = Arc::new(TestProviderInner { - client: TestBlockChainClient::new(), - }); + let provider = Arc::new(TestProviderInner { + client: TestBlockChainClient::new(), + }); - let proto = LightProtocol::new(Box::new(TestProvider(provider.clone())), Params { - network_id: NetworkId::Testnet, - flow_params: flow_params, - capabilities: capabilities, - }); + let proto = LightProtocol::new(Box::new(TestProvider(provider.clone())), Params { + network_id: NetworkId::Testnet, + flow_params: flow_params, + capabilities: capabilities, + }); - (provider, proto) + (provider, proto) } fn status(chain_info: BlockChainInfo) -> Status { - Status { - protocol_version: ::net::PROTOCOL_VERSION, - network_id: NetworkId::Testnet, - 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, - last_head: None, - } + Status { + protocol_version: ::net::PROTOCOL_VERSION, + network_id: NetworkId::Testnet, + 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, + last_head: None, + } } #[test] fn handshake_expected() { - let flow_params = make_flow_params(); - let capabilities = capabilities(); + let flow_params = make_flow_params(); + let capabilities = capabilities(); - let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); - let status = status(provider.client.chain_info()); + let status = status(provider.client.chain_info()); - let packet_body = write_handshake(&status, &capabilities, &flow_params); + let packet_body = write_handshake(&status, &capabilities, &flow_params); - proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); } #[test] #[should_panic] fn genesis_mismatch() { - let flow_params = make_flow_params(); - let capabilities = capabilities(); + let flow_params = make_flow_params(); + let capabilities = capabilities(); - let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); - let mut status = status(provider.client.chain_info()); - status.genesis_hash = H256::default(); + let mut status = status(provider.client.chain_info()); + status.genesis_hash = H256::default(); - let packet_body = write_handshake(&status, &capabilities, &flow_params); + let packet_body = write_handshake(&status, &capabilities, &flow_params); - proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); } #[test] fn buffer_overflow() { - let flow_params = make_flow_params(); - let capabilities = capabilities(); + let flow_params = make_flow_params(); + let capabilities = capabilities(); - let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); - let status = status(provider.client.chain_info()); + let status = status(provider.client.chain_info()); - { - let packet_body = write_handshake(&status, &capabilities, &flow_params); - proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); - } + { + let packet_body = write_handshake(&status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + } - { - let my_status = write_handshake(&status, &capabilities, &flow_params); - proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); - } + { + let my_status = write_handshake(&status, &capabilities, &flow_params); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } - // 1000 requests is far too many for the default flow params. - let request = encode_request(&Request::Headers(Headers { - block_num: 1, - block_hash: provider.client.chain_info().genesis_hash, - max: 1000, - skip: 0, - reverse: false, - }), 111); + // 1000 requests is far too many for the default flow params. + let request = encode_request(&Request::Headers(Headers { + block_num: 1, + block_hash: provider.client.chain_info().genesis_hash, + max: 1000, + skip: 0, + reverse: false, + }), 111); - proto.handle_packet(&Expect::Punish(1), &1, packet::GET_BLOCK_HEADERS, &request); + proto.handle_packet(&Expect::Punish(1), &1, packet::GET_BLOCK_HEADERS, &request); } // test the basic request types -- these just make sure that requests are parsed @@ -257,197 +261,248 @@ fn buffer_overflow() { #[test] fn get_block_headers() { - let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); - let capabilities = capabilities(); + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); - let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); - let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); - provider.client.add_blocks(100, EachBlockWith::Nothing); + provider.client.add_blocks(100, EachBlockWith::Nothing); - let cur_status = status(provider.client.chain_info()); + let cur_status = status(provider.client.chain_info()); - { - let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); - proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); - proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); - } + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } - let request = Headers { - block_num: 1, - block_hash: provider.client.block_hash(BlockID::Number(1)).unwrap(), - max: 10, - skip: 0, - reverse: false, - }; - let req_id = 111; + let request = Headers { + block_num: 1, + block_hash: provider.client.block_hash(BlockID::Number(1)).unwrap(), + max: 10, + skip: 0, + reverse: false, + }; + let req_id = 111; - let request_body = encode_request(&Request::Headers(request.clone()), req_id); - let response = { - let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockID::Number(i + 1)).unwrap()).collect(); - assert_eq!(headers.len(), 10); + let request_body = encode_request(&Request::Headers(request.clone()), req_id); + let response = { + let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockID::Number(i + 1)).unwrap()).collect(); + assert_eq!(headers.len(), 10); - let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Headers, 10); + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Headers, 10); - let mut response_stream = RlpStream::new_list(12); - - response_stream.append(&req_id).append(&new_buf); - for header in headers { - response_stream.append_raw(&header, 1); - } + let mut response_stream = RlpStream::new_list(12); + + response_stream.append(&req_id).append(&new_buf); + for header in headers { + response_stream.append_raw(&header, 1); + } - response_stream.out() - }; + response_stream.out() + }; - let expected = Expect::Respond(packet::BLOCK_HEADERS, response); - proto.handle_packet(&expected, &1, packet::GET_BLOCK_HEADERS, &request_body); + let expected = Expect::Respond(packet::BLOCK_HEADERS, response); + proto.handle_packet(&expected, &1, packet::GET_BLOCK_HEADERS, &request_body); } #[test] fn get_block_bodies() { - let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); - let capabilities = capabilities(); + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); - let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); - let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); - provider.client.add_blocks(100, EachBlockWith::Nothing); + provider.client.add_blocks(100, EachBlockWith::Nothing); - let cur_status = status(provider.client.chain_info()); + let cur_status = status(provider.client.chain_info()); - { - let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); - proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); - proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); - } + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } - let request = request::Bodies { - block_hashes: (0..10).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()).collect(), - }; + let request = request::Bodies { + block_hashes: (0..10).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()).collect(), + }; - let req_id = 111; + let req_id = 111; - let request_body = encode_request(&Request::Bodies(request.clone()), req_id); - let response = { - let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockID::Number(i + 1)).unwrap()).collect(); - assert_eq!(bodies.len(), 10); + let request_body = encode_request(&Request::Bodies(request.clone()), req_id); + let response = { + let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockID::Number(i + 1)).unwrap()).collect(); + assert_eq!(bodies.len(), 10); - let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10); + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10); - let mut response_stream = RlpStream::new_list(12); - - response_stream.append(&req_id).append(&new_buf); - for body in bodies { - response_stream.append_raw(&body, 1); - } + let mut response_stream = RlpStream::new_list(12); + + response_stream.append(&req_id).append(&new_buf); + for body in bodies { + response_stream.append_raw(&body, 1); + } - response_stream.out() - }; + response_stream.out() + }; - let expected = Expect::Respond(packet::BLOCK_BODIES, response); - proto.handle_packet(&expected, &1, packet::GET_BLOCK_BODIES, &request_body); + let expected = Expect::Respond(packet::BLOCK_BODIES, response); + proto.handle_packet(&expected, &1, packet::GET_BLOCK_BODIES, &request_body); } #[test] fn get_block_receipts() { - let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); - let capabilities = capabilities(); + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); - let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); - let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); - provider.client.add_blocks(1000, EachBlockWith::Nothing); + provider.client.add_blocks(1000, EachBlockWith::Nothing); - let cur_status = status(provider.client.chain_info()); + let cur_status = status(provider.client.chain_info()); - { - let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); - proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); - proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); - } + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } - // find the first 10 block hashes starting with `f` because receipts are only provided - // by the test client in that case. - let block_hashes: Vec<_> = (0..1000).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()) - .filter(|hash| format!("{}", hash).starts_with("f")).take(10).collect(); + // find the first 10 block hashes starting with `f` because receipts are only provided + // by the test client in that case. + let block_hashes: Vec<_> = (0..1000).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()) + .filter(|hash| format!("{}", hash).starts_with("f")).take(10).collect(); - let request = request::Receipts { - block_hashes: block_hashes.clone(), - }; + let request = request::Receipts { + block_hashes: block_hashes.clone(), + }; - let req_id = 111; + let req_id = 111; - let request_body = encode_request(&Request::Receipts(request.clone()), req_id); - let response = { - let receipts: Vec<_> = block_hashes.iter() - .map(|hash| provider.client.block_receipts(hash).unwrap()) - .collect(); + let request_body = encode_request(&Request::Receipts(request.clone()), req_id); + let response = { + let receipts: Vec<_> = block_hashes.iter() + .map(|hash| provider.client.block_receipts(hash).unwrap()) + .collect(); - let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Receipts, receipts.len()); + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Receipts, receipts.len()); - let mut response_stream = RlpStream::new_list(2 + receipts.len()); - - response_stream.append(&req_id).append(&new_buf); - for block_receipts in receipts { - response_stream.append_raw(&block_receipts, 1); - } + let mut response_stream = RlpStream::new_list(2 + receipts.len()); + + response_stream.append(&req_id).append(&new_buf); + for block_receipts in receipts { + response_stream.append_raw(&block_receipts, 1); + } - response_stream.out() - }; + response_stream.out() + }; - let expected = Expect::Respond(packet::RECEIPTS, response); - proto.handle_packet(&expected, &1, packet::GET_RECEIPTS, &request_body); + let expected = Expect::Respond(packet::RECEIPTS, response); + proto.handle_packet(&expected, &1, packet::GET_RECEIPTS, &request_body); } #[test] -fn get_block_bodies() { - let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); - let capabilities = capabilities(); +fn get_state_proofs() { + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); - let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); - let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + let cur_status = status(provider.client.chain_info()); - provider.client.add_blocks(100, EachBlockWith::Nothing); + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone())); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body); + } - let cur_status = status(provider.client.chain_info()); + let req_id = 112; + let key1 = U256::from(11223344).into(); + let key2 = U256::from(99988887).into(); - { - let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); - proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); - proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); - } + let request = Request::StateProofs (request::StateProofs { + requests: vec![ + request::StateProof { block: H256::default(), key1: key1, key2: None, from_level: 0 }, + request::StateProof { block: H256::default(), key1: key1, key2: Some(key2), from_level: 0}, + ] + }); - let request = request::Bodies { - block_hashes: (0..10).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()).collect(), - }; + let request_body = encode_request(&request, req_id); + let response = { + let proofs = vec![ + { let mut stream = RlpStream::new_list(2); stream.append(&key1).append_empty_data(); stream.out() }, + ::util::sha3::SHA3_NULL_RLP.to_vec(), + ]; - let req_id = 111; + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::StateProofs, 2); - let request_body = encode_request(&Request::Bodies(request.clone()), req_id); - let response = { - let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockID::Number(i + 1)).unwrap()).collect(); - assert_eq!(bodies.len(), 10); + let mut response_stream = RlpStream::new_list(4); + + response_stream.append(&req_id).append(&new_buf); + for proof in proofs { + response_stream.append_raw(&proof, 1); + } - let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10); + response_stream.out() + }; - let mut response_stream = RlpStream::new_list(12); - - response_stream.append(&req_id).append(&new_buf); - for body in bodies { - response_stream.append_raw(&body, 1); - } + let expected = Expect::Respond(packet::PROOFS, response); + proto.handle_packet(&expected, &1, packet::GET_PROOFS, &request_body); +} - response_stream.out() - }; +#[test] +fn get_contract_code() { + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); - let expected = Expect::Respond(packet::BLOCK_BODIES, response); - proto.handle_packet(&expected, &1, packet::GET_BLOCK_BODIES, &request_body); + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let cur_status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone())); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body); + } + + let req_id = 112; + let key1 = U256::from(11223344).into(); + let key2 = U256::from(99988887).into(); + + let request = Request::Codes (request::ContractCodes { + code_requests: vec![ + request::ContractCode { block_hash: H256::default(), account_key: key1 }, + request::ContractCode { block_hash: H256::default(), account_key: key2 }, + ], + }); + + let request_body = encode_request(&request, req_id); + let response = { + let codes: Vec<Vec<_>> = vec![ + key1.iter().chain(key1.iter()).cloned().collect(), + key2.iter().chain(key2.iter()).cloned().collect(), + ]; + + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Codes, 2); + + let mut response_stream = RlpStream::new_list(4); + + response_stream.append(&req_id).append(&new_buf); + for code in codes { + response_stream.append(&code); + } + + response_stream.out() + }; + + let expected = Expect::Respond(packet::CONTRACT_CODES, response); + proto.handle_packet(&expected, &1, packet::GET_CONTRACT_CODES, &request_body); } \ No newline at end of file From 2e87e31157260f650fdc9371e5525e1e676a7267 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Thu, 8 Dec 2016 16:46:58 +0100 Subject: [PATCH 14/74] light: no default features --- ethcore/light/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index e80eb3e2f..287044c98 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -21,4 +21,5 @@ rlp = { path = "../../util/rlp" } time = "0.1" [features] +default = [] ipc = ["ethcore/ipc", "ethcore-ipc", "ethcore-ipc-codegen"] \ No newline at end of file From 9e8fb616d37e7004f4600b631e8f67f08668dad9 Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jaco@ethcore.io> Date: Thu, 8 Dec 2016 16:50:19 +0100 Subject: [PATCH 15/74] Add store tests, fix issue with pending sort to top --- js/package.json | 1 + js/src/ui/TxList/store.js | 2 + js/src/ui/TxList/store.spec.js | 90 ++++++++++++++++++++++++++++++++++ js/test/mocha.config.js | 2 + 4 files changed, 95 insertions(+) create mode 100644 js/src/ui/TxList/store.spec.js diff --git a/js/package.json b/js/package.json index a5fab74d5..50f512823 100644 --- a/js/package.json +++ b/js/package.json @@ -67,6 +67,7 @@ "babel-register": "6.18.0", "babel-runtime": "6.18.0", "chai": "3.5.0", + "chai-as-promised": "6.0.0", "chai-enzyme": "0.6.1", "copy-webpack-plugin": "4.0.1", "core-js": "2.4.1", diff --git a/js/src/ui/TxList/store.js b/js/src/ui/TxList/store.js index 3faac193e..09cdd2226 100644 --- a/js/src/ui/TxList/store.js +++ b/js/src/ui/TxList/store.js @@ -45,6 +45,8 @@ export default class Store { if (bnB.eq(0)) { return bnB.eq(bnA) ? 0 : 1; + } else if (bnA.eq(0)) { + return -1; } return bnB.comparedTo(bnA); diff --git a/js/src/ui/TxList/store.spec.js b/js/src/ui/TxList/store.spec.js new file mode 100644 index 000000000..9fb3a709e --- /dev/null +++ b/js/src/ui/TxList/store.spec.js @@ -0,0 +1,90 @@ +// 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 <http://www.gnu.org/licenses/>. + +import BigNumber from 'bignumber.js'; +import sinon from 'sinon'; + +import Store from './store'; + +const SUBID = 123; +const BLOCKS = { + 1: { blockhash: '0x1' }, + 2: { blockhash: '0x2' } +}; +const TRANSACTIONS = { + '0x123': { blockNumber: new BigNumber(1) }, + '0x234': { blockNumber: new BigNumber(0) }, + '0x345': { blockNumber: new BigNumber(2) }, + '0x456': { blockNumber: new BigNumber(0) } +}; + +describe.only('ui/TxList/store', () => { + let api; + let store; + + beforeEach(() => { + api = { + subscribe: sinon.stub().resolves(SUBID), + eth: { + getBlockByNumber: (blockNumber) => { + return Promise.resolve(BLOCKS[blockNumber]); + } + } + }; + store = new Store(api); + }); + + describe('create', () => { + it('has empty storage', () => { + expect(store.blocks).to.deep.equal({}); + expect(store.sortedHashes.peek()).to.deep.equal([]); + expect(store.transactions).to.deep.equal({}); + }); + + it('subscribes to eth_blockNumber', () => { + expect(api.subscribe).to.have.been.calledWith('eth_blockNumber'); + expect(store._subscriptionId).to.equal(SUBID); + }); + }); + + describe('addBlocks', () => { + beforeEach(() => { + store.addBlocks(BLOCKS); + }); + + it('adds the blocks to the list', () => { + expect(store.blocks).to.deep.equal(BLOCKS); + }); + }); + + describe('addTransactions', () => { + beforeEach(() => { + store.addTransactions(TRANSACTIONS); + }); + + it('adds all transactions to the list', () => { + expect(store.transactions).to.deep.equal(TRANSACTIONS); + }); + + it('sorts transactions based on blockNumber', () => { + expect(store.sortedHashes.peek()).to.deep.equal(['0x234', '0x456', '0x345', '0x123']); + }); + + it('adds pending transactions to the pending queue', () => { + expect(store._pendingHashes).to.deep.equal(['0x234', '0x456']); + }); + }); +}); diff --git a/js/test/mocha.config.js b/js/test/mocha.config.js index adc43530e..36c91c76e 100644 --- a/js/test/mocha.config.js +++ b/js/test/mocha.config.js @@ -22,11 +22,13 @@ es6Promise.polyfill(); import 'mock-local-storage'; import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; import chaiEnzyme from 'chai-enzyme'; import sinonChai from 'sinon-chai'; import { WebSocket } from 'mock-socket'; import jsdom from 'jsdom'; +chai.use(chaiAsPromised); chai.use(chaiEnzyme()); chai.use(sinonChai); From ae49251c81c2a510c92ea80bb130b45dca56050b Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jacogr@gmail.com> Date: Thu, 8 Dec 2016 17:04:21 +0100 Subject: [PATCH 16/74] Remove .only --- js/src/ui/TxList/store.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/ui/TxList/store.spec.js b/js/src/ui/TxList/store.spec.js index 9fb3a709e..8e4eb848e 100644 --- a/js/src/ui/TxList/store.spec.js +++ b/js/src/ui/TxList/store.spec.js @@ -31,7 +31,7 @@ const TRANSACTIONS = { '0x456': { blockNumber: new BigNumber(0) } }; -describe.only('ui/TxList/store', () => { +describe('ui/TxList/store', () => { let api; let store; From 96d4569cf3192650ab32517ef5beceb0f95e0948 Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jaco@ethcore.io> Date: Thu, 8 Dec 2016 17:31:41 +0100 Subject: [PATCH 17/74] Update test describe paths --- js/src/views/Signer/components/util/transaction.spec.js | 2 +- .../views/Status/components/AutoComplete/AutoComplete.spec.js | 2 +- js/src/views/Status/components/Box/Box.spec.js | 2 +- js/src/views/Status/components/Call/Call.spec.js | 2 +- js/src/views/Status/components/Calls/Calls.spec.js | 2 +- .../views/Status/components/CallsToolbar/CallsToolbar.spec.js | 2 +- .../Status/components/MiningSettings/decodeExtraData.spec.js | 2 +- .../Status/components/MiningSettings/numberFromString.spec.js | 2 +- js/src/views/Status/components/Response/Response.spec.js | 2 +- js/src/views/Status/middleware/localstorage.spec.js | 2 +- js/src/views/Status/util/error.spec.js | 2 +- js/src/views/Status/util/index.spec.js | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/js/src/views/Signer/components/util/transaction.spec.js b/js/src/views/Signer/components/util/transaction.spec.js index af181a7f8..a2022fe2e 100644 --- a/js/src/views/Signer/components/util/transaction.spec.js +++ b/js/src/views/Signer/components/util/transaction.spec.js @@ -17,7 +17,7 @@ import BigNumber from 'bignumber.js'; import { getShortData, getFee, getTotalValue } from './transaction'; -describe('util/transaction', () => { +describe('views/Signer/components/util/transaction', () => { describe('getEstimatedMiningTime', () => { it('should return estimated mining time', () => { }); diff --git a/js/src/views/Status/components/AutoComplete/AutoComplete.spec.js b/js/src/views/Status/components/AutoComplete/AutoComplete.spec.js index 046fcfad6..6bc04574e 100644 --- a/js/src/views/Status/components/AutoComplete/AutoComplete.spec.js +++ b/js/src/views/Status/components/AutoComplete/AutoComplete.spec.js @@ -21,7 +21,7 @@ import getMuiTheme from 'material-ui/styles/getMuiTheme'; import WrappedAutoComplete from './AutoComplete'; -describe('components/AutoComplete', () => { +describe('views/Status/components/AutoComplete', () => { describe('rendering', () => { let rendered; diff --git a/js/src/views/Status/components/Box/Box.spec.js b/js/src/views/Status/components/Box/Box.spec.js index 0630dc99f..5b6301dc9 100644 --- a/js/src/views/Status/components/Box/Box.spec.js +++ b/js/src/views/Status/components/Box/Box.spec.js @@ -19,7 +19,7 @@ import { shallow } from 'enzyme'; import Box from './Box'; -describe('components/Box', () => { +describe('views/Status/components/Box', () => { describe('rendering', () => { const title = 'test title'; let rendered; diff --git a/js/src/views/Status/components/Call/Call.spec.js b/js/src/views/Status/components/Call/Call.spec.js index a4fcf7d01..70caca49f 100644 --- a/js/src/views/Status/components/Call/Call.spec.js +++ b/js/src/views/Status/components/Call/Call.spec.js @@ -22,7 +22,7 @@ import '../../../../environment/tests'; import Call from './Call'; -describe('components/Call', () => { +describe('views/Status/components/Call', () => { const call = { callIdx: 123, callNo: 456, name: 'eth_call', params: [{ name: '123' }], response: '' }; const element = 'dummyElement'; diff --git a/js/src/views/Status/components/Calls/Calls.spec.js b/js/src/views/Status/components/Calls/Calls.spec.js index 7a73fa528..e1e4ac8c9 100644 --- a/js/src/views/Status/components/Calls/Calls.spec.js +++ b/js/src/views/Status/components/Calls/Calls.spec.js @@ -21,7 +21,7 @@ import '../../../../environment/tests'; import Calls from './Calls'; -describe('components/Calls', () => { +describe('views/Status/components/Calls', () => { describe('rendering (no calls)', () => { let rendered; diff --git a/js/src/views/Status/components/CallsToolbar/CallsToolbar.spec.js b/js/src/views/Status/components/CallsToolbar/CallsToolbar.spec.js index 0ff9f568b..6f7a40dfe 100644 --- a/js/src/views/Status/components/CallsToolbar/CallsToolbar.spec.js +++ b/js/src/views/Status/components/CallsToolbar/CallsToolbar.spec.js @@ -22,7 +22,7 @@ import '../../../../environment/tests'; import CallsToolbar from './CallsToolbar'; -describe('components/CallsToolbar', () => { +describe('views/Status/components/CallsToolbar', () => { const callEl = { offsetTop: 0 }; const containerEl = { scrollTop: 0, clientHeight: 0, scrollHeight: 999 }; diff --git a/js/src/views/Status/components/MiningSettings/decodeExtraData.spec.js b/js/src/views/Status/components/MiningSettings/decodeExtraData.spec.js index 007e564b4..b2f93240c 100644 --- a/js/src/views/Status/components/MiningSettings/decodeExtraData.spec.js +++ b/js/src/views/Status/components/MiningSettings/decodeExtraData.spec.js @@ -16,7 +16,7 @@ import { decodeExtraData } from './decodeExtraData'; -describe('MINING SETTINGS', () => { +describe('views/Status/components/MiningSettings/decodeExtraData', () => { describe('EXTRA DATA', () => { const str = 'parity/1.0.0/1.0.0-beta2'; const encoded = '0xd783010000867061726974798b312e302e302d6265746132'; diff --git a/js/src/views/Status/components/MiningSettings/numberFromString.spec.js b/js/src/views/Status/components/MiningSettings/numberFromString.spec.js index c00273495..7efa514c3 100644 --- a/js/src/views/Status/components/MiningSettings/numberFromString.spec.js +++ b/js/src/views/Status/components/MiningSettings/numberFromString.spec.js @@ -16,7 +16,7 @@ import { numberFromString } from './numberFromString'; -describe('NUMBER FROM STRING', () => { +describe('views/Status/components/MiningSettings/numberFromString', () => { it('should convert string to number', () => { expect(numberFromString('12345'), 12345); }); diff --git a/js/src/views/Status/components/Response/Response.spec.js b/js/src/views/Status/components/Response/Response.spec.js index 1eb37a340..0617721c2 100644 --- a/js/src/views/Status/components/Response/Response.spec.js +++ b/js/src/views/Status/components/Response/Response.spec.js @@ -21,7 +21,7 @@ import '../../../../environment/tests'; import Response from './Response'; -describe('components/Response', () => { +describe('views/Status/components/Response', () => { describe('rendering', () => { it('renders non-arrays/non-objects exactly as received', () => { const TEST = '1234567890'; diff --git a/js/src/views/Status/middleware/localstorage.spec.js b/js/src/views/Status/middleware/localstorage.spec.js index c522196d9..e1d6ed7ee 100644 --- a/js/src/views/Status/middleware/localstorage.spec.js +++ b/js/src/views/Status/middleware/localstorage.spec.js @@ -21,7 +21,7 @@ import { syncRpcStateFromLocalStorage } from '../actions/localstorage'; import rpcData from '../data/rpc.json'; import LocalStorageMiddleware from './localstorage'; -describe('MIDDLEWARE: LOCAL STORAGE', () => { +describe('views/Status/middleware/localstorage', () => { let cut, state; beforeEach('mock cut', () => { diff --git a/js/src/views/Status/util/error.spec.js b/js/src/views/Status/util/error.spec.js index f3ba98cc8..7512a47bc 100644 --- a/js/src/views/Status/util/error.spec.js +++ b/js/src/views/Status/util/error.spec.js @@ -17,7 +17,7 @@ import sinon from 'sinon'; import * as ErrorUtil from './error'; -describe('util/error', () => { +describe('views/Status/util/error', () => { beforeEach('spy on isError', () => { sinon.spy(ErrorUtil, 'isError'); }); diff --git a/js/src/views/Status/util/index.spec.js b/js/src/views/Status/util/index.spec.js index e648646c1..39d17322a 100644 --- a/js/src/views/Status/util/index.spec.js +++ b/js/src/views/Status/util/index.spec.js @@ -16,7 +16,7 @@ import { toPromise, identity } from './'; -describe('util', () => { +describe('views/Status/util', () => { describe('toPromise', () => { it('rejects on error result', () => { const ERROR = new Error(); From efd66f566d072f5f4dd45814d459507b5c344668 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Thu, 8 Dec 2016 19:52:48 +0100 Subject: [PATCH 18/74] ethsync: optional ipc codegen --- Cargo.lock | 2 ++ Cargo.toml | 3 +-- ethcore/Cargo.toml | 1 - ethcore/light/Cargo.toml | 4 ++-- ethcore/src/client/client.rs | 1 - ethcore/src/client/mod.rs | 1 - ethcore/src/client/traits.rs | 1 - ethcore/src/state/mod.rs | 2 -- sync/Cargo.toml | 7 +++--- sync/src/api.rs | 42 ++++++++++++++++++------------------ sync/src/lib.rs | 8 ++++--- sync/src/tests/mod.rs | 2 ++ 12 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a5534dbe..b00701f32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,6 +462,8 @@ version = "1.5.0" dependencies = [ "ethcore 1.5.0", "ethcore-io 1.5.0", + "ethcore-ipc 1.4.0", + "ethcore-ipc-codegen 1.4.0", "ethcore-network 1.5.0", "ethcore-util 1.5.0", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 4730cef0d..078d2916c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ winapi = "0.2" daemonize = "0.2" [features] -default = ["ui-precompiled", "light"] +default = ["ui-precompiled"] ui = [ "dapps", @@ -80,7 +80,6 @@ ethstore-cli = ["ethcore/ethstore-cli"] evm-debug = ["ethcore/evm-debug"] evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] -light = ["ethcore/light", "ethsync/light"] [[bin]] path = "parity/main.rs" diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 2c3d946f4..bd87c422f 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -58,6 +58,5 @@ dev = ["clippy"] default = [] benches = [] ipc = [] -light = [] ethkey-cli = ["ethkey/cli"] ethstore-cli = ["ethstore/cli"] diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 287044c98..c89bbc74f 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -12,7 +12,7 @@ build = "build.rs" [dependencies] log = "0.3" -ethcore = { path = "..", features = ["light"] } +ethcore = { path = ".."} ethcore-util = { path = "../../util" } ethcore-network = { path = "../../util/network" } ethcore-io = { path = "../../util/io" } @@ -22,4 +22,4 @@ time = "0.1" [features] default = [] -ipc = ["ethcore/ipc", "ethcore-ipc", "ethcore-ipc-codegen"] \ No newline at end of file +ipc = ["ethcore-ipc", "ethcore-ipc-codegen"] \ No newline at end of file diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index ee72ea8b8..9cfad4b96 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1377,7 +1377,6 @@ impl MayPanic for Client { } } -#[cfg(feature = "light")] impl ::client::ProvingBlockChainClient for Client { fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockID) -> Vec<Bytes> { self.state_at(id) diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index beab88e8a..4e5554b01 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -29,7 +29,6 @@ pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::chain_notify::ChainNotify; pub use self::traits::{BlockChainClient, MiningBlockChainClient}; -#[cfg(feature = "light")] pub use self::traits::ProvingBlockChainClient; pub use types::ids::*; diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 3eed9aa5d..7bf17279c 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -278,7 +278,6 @@ pub trait MiningBlockChainClient: BlockChainClient { } /// Extended client interface for providing proofs of the state. -#[cfg(feature = "light")] pub trait ProvingBlockChainClient: BlockChainClient { /// Prove account storage at a specific block id. /// diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index d39481048..8a52b62ff 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -32,7 +32,6 @@ use state_db::StateDB; use util::*; -#[cfg(feature = "light")] use util::trie::recorder::{Recorder, BasicRecorder as TrieRecorder}; mod account; @@ -764,7 +763,6 @@ impl State { } // LES state proof implementations. -#[cfg(feature = "light")] 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` diff --git a/sync/Cargo.toml b/sync/Cargo.toml index df0f4840d..f35852558 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -15,7 +15,7 @@ ethcore-ipc-codegen = { path = "../ipc/codegen" } ethcore-util = { path = "../util" } ethcore-network = { path = "../util/network" } ethcore-io = { path = "../util/io" } -ethcore-light = { path = "../ethcore/light", optional = true } +ethcore-light = { path = "../ethcore/light"} ethcore = { path = "../ethcore" } rlp = { path = "../util/rlp" } clippy = { version = "0.0.103", optional = true} @@ -30,7 +30,6 @@ ethcore-ipc-nano = { path = "../ipc/nano" } parking_lot = "0.3" [features] -default = ["ipc"] +default = [] dev = ["clippy", "ethcore/dev", "ethcore-util/dev"] -light = ["ethcore-light"] -ipc = [] +ipc = ["ethcore-light/ipc"] diff --git a/sync/src/api.rs b/sync/src/api.rs index 5613b77ff..d8df149c8 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -85,14 +85,18 @@ pub trait SyncProvider: Send + Sync { } /// Transaction stats -#[derive(Debug, Binary)] +#[derive(Debug)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct TransactionStats { + /// Block number where this TX was first seen. pub first_seen: u64, + /// Peers it was propagated to. pub propagated_to: BTreeMap<H512, usize>, } /// Peer connection information -#[derive(Debug, Binary)] +#[derive(Debug)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct PeerInfo { /// Public node id pub id: Option<String>, @@ -120,8 +124,6 @@ pub struct EthSync { handler: Arc<SyncProtocolHandler>, /// The main subprotocol name subprotocol_name: [u8; 3], - /// Configuration - config: NetworkConfiguration, } impl EthSync { @@ -138,14 +140,13 @@ impl EthSync { overlay: RwLock::new(HashMap::new()), }), subprotocol_name: config.subprotocol_name, - config: network_config, }); Ok(sync) } } -#[ipc(client_ident="SyncClient")] +#[cfg_attr(feature = "ipc", ipc(client_ident="SyncClient"))] impl SyncProvider for EthSync { /// Get sync status fn status(&self) -> SyncStatus { @@ -279,7 +280,7 @@ pub trait ManageNetwork : Send + Sync { } -#[ipc(client_ident="NetworkManagerClient")] +#[cfg_attr(feature = "ipc", ipc(client_ident="NetworkManagerClient"))] impl ManageNetwork for EthSync { fn accept_unreserved_peers(&self) { self.network.set_non_reserved_mode(NonReservedPeerMode::Accept); @@ -315,7 +316,8 @@ impl ManageNetwork for EthSync { } /// IP fiter -#[derive(Binary, Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub enum AllowIP { /// Connect to any address All, @@ -337,7 +339,8 @@ impl AllowIP { } } -#[derive(Binary, Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] /// Network service configuration pub struct NetworkConfiguration { /// Directory path to store general network configuration. None means nothing will be saved @@ -375,26 +378,18 @@ pub struct NetworkConfiguration { } impl NetworkConfiguration { + /// Create a new default config. pub fn new() -> Self { From::from(BasicNetworkConfiguration::new()) } + /// Create a new local config. pub fn new_local() -> Self { From::from(BasicNetworkConfiguration::new_local()) } - fn validate(&self) -> Result<(), AddrParseError> { - if let Some(ref addr) = self.listen_address { - try!(SocketAddr::from_str(&addr)); - } - if let Some(ref addr) = self.public_address { - try!(SocketAddr::from_str(&addr)); - } - Ok(()) - } - + /// Attempt to convert this config into a BasicNetworkConfiguration. pub fn into_basic(self) -> Result<BasicNetworkConfiguration, AddrParseError> { - Ok(BasicNetworkConfiguration { config_path: self.config_path, net_config_path: self.net_config_path, @@ -447,9 +442,14 @@ impl From<BasicNetworkConfiguration> for NetworkConfiguration { } } -#[derive(Debug, Binary, Clone)] +/// Configuration for IPC service. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct ServiceConfiguration { + /// Sync config. pub sync: SyncConfig, + /// Network configuration. pub net: NetworkConfiguration, + /// IPC path. pub io_path: String, } diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 7a4f22fd7..801fcbbd5 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -37,10 +37,8 @@ extern crate semver; extern crate parking_lot; extern crate rlp; -#[cfg(feature = "light")] extern crate ethcore_light as light; - #[macro_use] extern crate log; #[macro_use] @@ -60,12 +58,16 @@ mod transactions_stats; #[cfg(test)] mod tests; +#[cfg(feature = "ipc")] mod api { #![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues include!(concat!(env!("OUT_DIR"), "/api.rs")); } -pub use api::{EthSync, SyncProvider, SyncClient, NetworkManagerClient, ManageNetwork, SyncConfig, +#[cfg(not(feature = "ipc"))] +mod api; + +pub use api::{EthSync, SyncProvider, ManageNetwork, SyncConfig, ServiceConfiguration, NetworkConfiguration, PeerInfo, AllowIP, TransactionStats}; pub use chain::{SyncStatus, SyncState}; pub use network::{is_valid_node_url, NonReservedPeerMode, NetworkError}; diff --git a/sync/src/tests/mod.rs b/sync/src/tests/mod.rs index bdb4ae4f9..62d110075 100644 --- a/sync/src/tests/mod.rs +++ b/sync/src/tests/mod.rs @@ -17,4 +17,6 @@ pub mod helpers; pub mod snapshot; mod chain; + +#[cfg(feature = "ipc")] mod rpc; From 6f5f1f5e26831575bee4d395989fb528f48a3272 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Thu, 8 Dec 2016 23:21:47 +0100 Subject: [PATCH 19/74] light: integrate with sync + serve_light CLI --- Cargo.lock | 1 + Cargo.toml | 1 + ethcore/light/build.rs | 1 + ethcore/light/src/lib.rs | 13 +++ ethcore/light/src/net/buffer_flow.rs | 13 +++ ethcore/light/src/net/mod.rs | 46 ++++++++--- ethcore/light/src/net/status.rs | 33 ++------ ethcore/light/src/net/tests/mod.rs | 10 +-- ethcore/light/src/provider.rs | 1 + parity/cli/config.full.toml | 1 + parity/cli/mod.rs | 5 ++ parity/cli/usage.txt | 1 + parity/configuration.rs | 2 + parity/main.rs | 1 + parity/modules.rs | 30 +++++-- parity/run.rs | 15 +++- parity/sync.rs | 10 ++- sync/build.rs | 6 +- sync/src/api.rs | 118 ++++++++++++++++++++++----- sync/src/lib.rs | 2 +- 20 files changed, 236 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b00701f32..9184d50f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,7 @@ dependencies = [ "ethcore-ipc-hypervisor 1.2.0", "ethcore-ipc-nano 1.4.0", "ethcore-ipc-tests 0.1.0", + "ethcore-light 1.5.0", "ethcore-logger 1.5.0", "ethcore-rpc 1.5.0", "ethcore-signer 1.5.0", diff --git a/Cargo.toml b/Cargo.toml index 078d2916c..7e989b173 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ rlp = { path = "util/rlp" } ethcore-stratum = { path = "stratum" } ethcore-dapps = { path = "dapps", optional = true } clippy = { version = "0.0.103", optional = true} +ethcore-light = { path = "ethcore/light" } [target.'cfg(windows)'.dependencies] winapi = "0.2" diff --git a/ethcore/light/build.rs b/ethcore/light/build.rs index 43915c1cf..7d4e0064c 100644 --- a/ethcore/light/build.rs +++ b/ethcore/light/build.rs @@ -20,6 +20,7 @@ extern crate ethcore_ipc_codegen; #[cfg(feature = "ipc")] fn main() { ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap(); + ethcore_ipc_codegen::derive_ipc_cond("src/provider.rs", true).unwrap(); } #[cfg(not(feature = "ipc"))] diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index c00467c4c..7fa2f5911 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -33,8 +33,21 @@ pub mod client; pub mod net; + +#[cfg(not(feature = "ipc"))] pub mod provider; +#[cfg(feature = "ipc")] +pub mod provider { + #![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues + include!(concat!(env!("OUT_DIR"), "/provider.rs")); +} + +#[cfg(feature = "ipc")] +pub mod remote { + pub use provider::LightProviderClient; +} + mod types; pub use self::provider::Provider; diff --git a/ethcore/light/src/net/buffer_flow.rs b/ethcore/light/src/net/buffer_flow.rs index 6730c71a7..2371c6ea4 100644 --- a/ethcore/light/src/net/buffer_flow.rs +++ b/ethcore/light/src/net/buffer_flow.rs @@ -22,6 +22,9 @@ //! //! This module provides an interface for configuration of buffer //! flow costs and recharge rates. +//! +//! Current default costs are picked completely arbitrarily, not based +//! on any empirical timings or mathematical models. use request; use super::packet; @@ -273,6 +276,16 @@ impl FlowParams { } } +impl Default for FlowParams { + fn default() -> Self { + FlowParams { + limit: 50_000_000.into(), + costs: CostTable::default(), + recharge: 100_000.into(), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 57dc77a86..fd64f4a4b 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -30,13 +30,14 @@ use util::{Bytes, Mutex, RwLock, U256}; use time::SteadyTime; use std::collections::HashMap; +use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use provider::Provider; use request::{self, Request}; use self::buffer_flow::{Buffer, FlowParams}; -use self::context::{IoContext, EventContext, Ctx}; +use self::context::Ctx; use self::error::{Error, Punishment}; mod buffer_flow; @@ -47,16 +48,20 @@ mod status; #[cfg(test)] mod tests; -pub use self::status::{Status, Capabilities, Announcement, NetworkId}; +pub use self::context::{EventContext, IoContext}; +pub use self::status::{Status, Capabilities, Announcement}; const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; -// LPV1 -const PROTOCOL_VERSION: u32 = 1; +// Supported protocol versions. +pub const PROTOCOL_VERSIONS: &'static [u8] = &[1]; -// TODO [rob] make configurable. -const PROTOCOL_ID: [u8; 3] = *b"les"; +// Max protocol version. +pub const MAX_PROTOCOL_VERSION: u8 = 1; + +// Packet count for LES. +pub const PACKET_COUNT: u8 = 15; // packet ID definitions. mod packet { @@ -173,6 +178,8 @@ pub trait Handler: Send + Sync { /// Called when a peer responds with header proofs. Each proof is a block header coupled /// with a series of trie nodes is ascending order by distance from the root. fn on_header_proofs(&self, _ctx: &EventContext, _req_id: ReqId, _proofs: &[(Bytes, Vec<Bytes>)]) { } + /// Called on abort. + fn on_abort(&self) { } } // a request, the peer who it was made to, and the time it was made. @@ -185,7 +192,7 @@ struct Requested { /// Protocol parameters. pub struct Params { /// Network id. - pub network_id: NetworkId, + pub network_id: u64, /// Buffer flow parameters. pub flow_params: FlowParams, /// Initial capabilities. @@ -203,9 +210,9 @@ pub struct Params { // 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<Provider>, + provider: Arc<Provider>, genesis_hash: H256, - network_id: NetworkId, + network_id: u64, pending_peers: RwLock<HashMap<PeerId, PendingPeer>>, peers: RwLock<HashMap<PeerId, Mutex<Peer>>>, pending_requests: RwLock<HashMap<usize, Requested>>, @@ -217,7 +224,7 @@ pub struct LightProtocol { impl LightProtocol { /// Create a new instance of the protocol manager. - pub fn new(provider: Box<Provider>, params: Params) -> Self { + pub fn new(provider: Arc<Provider>, params: Params) -> Self { let genesis_hash = provider.chain_info().genesis_hash; LightProtocol { provider: provider, @@ -323,6 +330,23 @@ impl LightProtocol { self.handlers.push(handler); } + /// Signal to handlers that network activity is being aborted + /// and clear peer data. + pub fn abort(&self) { + for handler in &self.handlers { + handler.on_abort(); + } + + // acquire in order and hold. + let mut pending_peers = self.pending_peers.write(); + let mut peers = self.peers.write(); + let mut pending_requests = self.pending_requests.write(); + + pending_peers.clear(); + peers.clear(); + pending_requests.clear(); + } + // Does the common pre-verification of responses before the response itself // is actually decoded: // - check whether peer exists @@ -460,7 +484,7 @@ impl LightProtocol { head_hash: chain_info.best_block_hash, head_num: chain_info.best_block_number, genesis_hash: chain_info.genesis_hash, - protocol_version: PROTOCOL_VERSION, + protocol_version: MAX_PROTOCOL_VERSION as u32, network_id: self.network_id, last_head: None, }; diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index 2c0c5f79a..6d0a76823 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -82,26 +82,6 @@ impl Key { } } -/// 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, -} - -impl NetworkId { - fn from_raw(raw: u32) -> Option<Self> { - 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, @@ -164,7 +144,7 @@ pub struct Status { /// Protocol version. pub protocol_version: u32, /// Network id of this peer. - pub network_id: NetworkId, + pub network_id: u64, /// Total difficulty of the head of the chain. pub head_td: U256, /// Hash of the best block. @@ -225,8 +205,7 @@ pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, FlowP 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")))), + network_id: try!(parser.expect(Key::NetworkId)), head_td: try!(parser.expect(Key::HeadTD)), head_hash: try!(parser.expect(Key::HeadHash)), head_num: try!(parser.expect(Key::HeadNum)), @@ -254,7 +233,7 @@ pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, FlowP pub fn write_handshake(status: &Status, capabilities: &Capabilities, flow_params: &FlowParams) -> Vec<u8> { 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::NetworkId, &(status.network_id as u64))); 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)); @@ -385,7 +364,7 @@ mod tests { fn full_handshake() { let status = Status { protocol_version: 1, - network_id: NetworkId::Mainnet, + network_id: 1, head_td: U256::default(), head_hash: H256::default(), head_num: 10, @@ -420,7 +399,7 @@ mod tests { fn partial_handshake() { let status = Status { protocol_version: 1, - network_id: NetworkId::Mainnet, + network_id: 1, head_td: U256::default(), head_hash: H256::default(), head_num: 10, @@ -455,7 +434,7 @@ mod tests { fn skip_unknown_keys() { let status = Status { protocol_version: 1, - network_id: NetworkId::Mainnet, + network_id: 1, head_td: U256::default(), head_hash: H256::default(), head_num: 10, diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index e659d0681..30ab2bab2 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -25,7 +25,7 @@ use network::PeerId; use net::buffer_flow::FlowParams; use net::context::IoContext; -use net::status::{Capabilities, Status, NetworkId, write_handshake}; +use net::status::{Capabilities, Status, write_handshake}; use net::{encode_request, LightProtocol, Params, packet}; use provider::Provider; use request::{self, Request, Headers}; @@ -174,8 +174,8 @@ fn setup(flow_params: FlowParams, capabilities: Capabilities) -> (Arc<TestProvid client: TestBlockChainClient::new(), }); - let proto = LightProtocol::new(Box::new(TestProvider(provider.clone())), Params { - network_id: NetworkId::Testnet, + let proto = LightProtocol::new(Arc::new(TestProvider(provider.clone())), Params { + network_id: 2, flow_params: flow_params, capabilities: capabilities, }); @@ -185,8 +185,8 @@ fn setup(flow_params: FlowParams, capabilities: Capabilities) -> (Arc<TestProvid fn status(chain_info: BlockChainInfo) -> Status { Status { - protocol_version: ::net::PROTOCOL_VERSION, - network_id: NetworkId::Testnet, + protocol_version: 1, + network_id: 2, head_td: chain_info.total_difficulty, head_hash: chain_info.best_block_hash, head_num: chain_info.best_block_number, diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 9446aa3f6..37a5cef4d 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -33,6 +33,7 @@ use request; /// or empty vector where appropriate. /// /// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) +#[cfg_attr(feature = "ipc", ipc(client_ident="LightProviderClient"))] pub trait Provider: Send + Sync { /// Provide current blockchain info. fn chain_info(&self) -> BlockChainInfo; diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index fcd9a9712..73b5e13be 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -32,6 +32,7 @@ warp = true allow_ips = "all" snapshot_peers = 0 max_pending_peers = 64 +serve_light = true reserved_only = false reserved_peers = "./path_to_file" diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 7fcdd2209..2335ccee8 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -135,6 +135,8 @@ usage! { flag_reserved_only: bool = false, or |c: &Config| otry!(c.network).reserved_only.clone(), flag_no_ancient_blocks: bool = false, or |_| None, + flag_serve_light: bool = false, + or |c: &Config| otry!(c.network).serve_light.clone(), // -- API and Console Options // RPC @@ -334,6 +336,7 @@ struct Network { node_key: Option<String>, reserved_peers: Option<String>, reserved_only: Option<bool>, + serve_light: Option<bool>, } #[derive(Default, Debug, PartialEq, RustcDecodable)] @@ -543,6 +546,7 @@ mod tests { flag_reserved_peers: Some("./path_to_file".into()), flag_reserved_only: false, flag_no_ancient_blocks: false, + flag_serve_light: true, // -- API and Console Options // RPC @@ -713,6 +717,7 @@ mod tests { node_key: None, reserved_peers: Some("./path/to/reserved_peers".into()), reserved_only: Some(true), + serve_light: None, }), rpc: Some(Rpc { disable: Some(true), diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index b67af6110..58b836aa4 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -97,6 +97,7 @@ Networking Options: --max-pending-peers NUM Allow up to NUM pending connections. (default: {flag_max_pending_peers}) --no-ancient-blocks Disable downloading old blocks after snapshot restoration or warp sync. (default: {flag_no_ancient_blocks}) + --serve-light Experimental: Serve light client peers. (default: {flag_serve_light}) API and Console Options: --no-jsonrpc Disable the JSON-RPC API server. (default: {flag_no_jsonrpc}) diff --git a/parity/configuration.rs b/parity/configuration.rs index 37c699521..94fa20e59 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -275,6 +275,7 @@ impl Configuration { no_periodic_snapshot: self.args.flag_no_periodic_snapshot, check_seal: !self.args.flag_no_seal_check, download_old_blocks: !self.args.flag_no_ancient_blocks, + serve_light: self.args.flag_serve_light, }; Cmd::Run(run_cmd) }; @@ -921,6 +922,7 @@ mod tests { no_periodic_snapshot: false, check_seal: true, download_old_blocks: true, + serve_light: false, })); } diff --git a/parity/main.rs b/parity/main.rs index c125e87f6..17f5ed74b 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -43,6 +43,7 @@ extern crate serde; extern crate serde_json; extern crate rlp; extern crate ethcore_hash_fetch as hash_fetch; +extern crate ethcore_light as light; extern crate ethcore_ipc_hypervisor as hypervisor; extern crate ethcore_rpc; diff --git a/parity/modules.rs b/parity/modules.rs index 39e05a293..5d1d66cd7 100644 --- a/parity/modules.rs +++ b/parity/modules.rs @@ -15,16 +15,22 @@ // along with Parity. If not, see <http://www.gnu.org/licenses/>. use std::sync::Arc; +use std::path::Path; + use ethcore::client::BlockChainClient; use hypervisor::Hypervisor; -use ethsync::{SyncConfig, NetworkConfiguration, NetworkError}; +use ethsync::{SyncConfig, NetworkConfiguration, NetworkError, Params}; use ethcore::snapshot::SnapshotService; +use light::Provider; + #[cfg(not(feature="ipc"))] use self::no_ipc_deps::*; + +#[cfg(not(feature="ipc"))] +use ethcore_logger::Config as LogConfig; + #[cfg(feature="ipc")] use self::ipc_deps::*; -use ethcore_logger::Config as LogConfig; -use std::path::Path; #[cfg(feature="ipc")] pub mod service_urls { @@ -36,6 +42,8 @@ pub mod service_urls { pub const SYNC_NOTIFY: &'static str = "parity-sync-notify.ipc"; pub const NETWORK_MANAGER: &'static str = "parity-manage-net.ipc"; pub const SYNC_CONTROL: &'static str = "parity-sync-control.ipc"; + pub const LIGHT_PROVIDER: &'static str = "parity-light-provider.ipc"; + #[cfg(feature="stratum")] pub const STRATUM: &'static str = "parity-stratum.ipc"; #[cfg(feature="stratum")] @@ -75,6 +83,7 @@ mod ipc_deps { pub use nanoipc::{GuardedSocket, NanoSocket, generic_client, fast_client}; pub use ipc::IpcSocket; pub use ipc::binary::serialize; + pub use light::remote::LightProviderClient; } #[cfg(feature="ipc")] @@ -124,6 +133,7 @@ pub fn sync net_cfg: NetworkConfiguration, _client: Arc<BlockChainClient>, _snapshot_service: Arc<SnapshotService>, + _provider: Arc<Provider>, log_settings: &LogConfig, ) -> Result<SyncModules, NetworkError> @@ -141,7 +151,9 @@ pub fn sync &service_urls::with_base(&hypervisor.io_path, service_urls::SYNC_NOTIFY)).unwrap(); let manage_client = generic_client::<NetworkManagerClient<_>>( &service_urls::with_base(&hypervisor.io_path, service_urls::NETWORK_MANAGER)).unwrap(); - + let provider_client = generic_client::<LightProviderClient<_>>( + &service_urls::with_base(&hypervisor.io_path, service_urls::LIGHT_PROVIDER)).unwrap(); + *hypervisor_ref = Some(hypervisor); Ok((sync_client, manage_client, notify_client)) } @@ -154,10 +166,18 @@ pub fn sync net_cfg: NetworkConfiguration, client: Arc<BlockChainClient>, snapshot_service: Arc<SnapshotService>, + provider: Arc<Provider>, _log_settings: &LogConfig, ) -> Result<SyncModules, NetworkError> { - let eth_sync = try!(EthSync::new(sync_cfg, client, snapshot_service, net_cfg)); + let eth_sync = try!(EthSync::new(Params { + config: sync_cfg, + chain: client, + provider: provider, + snapshot_service: snapshot_service, + network_config: net_cfg, + })); + Ok((eth_sync.clone() as Arc<SyncProvider>, eth_sync.clone() as Arc<ManageNetwork>, eth_sync.clone() as Arc<ChainNotify>)) } diff --git a/parity/run.rs b/parity/run.rs index 42a972000..5c02e4021 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -92,6 +92,7 @@ pub struct RunCmd { pub no_periodic_snapshot: bool, pub check_seal: bool, pub download_old_blocks: bool, + pub serve_light: bool, } pub fn open_ui(dapps_conf: &dapps::Configuration, signer_conf: &signer::Configuration) -> Result<(), String> { @@ -185,6 +186,11 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> { ); info!("Operating mode: {}", Colour::White.bold().paint(format!("{}", mode))); + if cmd.serve_light { + info!("Configured to serve light client peers. Please note this feature is {}.", + Colour::White.bold().paint("experimental".to_string())); + } + // display warning about using experimental journaldb alorithm if !algorithm.is_stable() { warn!("Your chosen strategy is {}! You can re-run with --pruning to change.", Colour::Red.bold().paint("unstable")); @@ -204,6 +210,7 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> { sync_config.fork_block = spec.fork_block(); sync_config.warp_sync = cmd.warp_sync; sync_config.download_old_blocks = cmd.download_old_blocks; + sync_config.serve_light = cmd.serve_light; // prepare account provider let account_provider = Arc::new(try!(prepare_account_provider(&cmd.dirs, cmd.acc_conf))); @@ -268,7 +275,13 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> { // create sync object let (sync_provider, manage_network, chain_notify) = try!(modules::sync( - &mut hypervisor, sync_config, net_conf.into(), client.clone(), snapshot_service.clone(), &cmd.logger_config, + &mut hypervisor, + sync_config, + net_conf.into(), + client.clone(), + snapshot_service.clone(), + client.clone(), + &cmd.logger_config, ).map_err(|e| format!("Sync error: {}", e))); service.add_notify(chain_notify.clone()); diff --git a/parity/sync.rs b/parity/sync.rs index 25f900b78..17f183c80 100644 --- a/parity/sync.rs +++ b/parity/sync.rs @@ -22,6 +22,7 @@ use hypervisor::{SYNC_MODULE_ID, HYPERVISOR_IPC_URL, ControlService}; use ethcore::client::ChainNotify; use ethcore::client::remote::RemoteClient; use ethcore::snapshot::remote::RemoteSnapshotService; +use light::remote::LightProviderClient; use ethsync::{SyncProvider, EthSync, ManageNetwork, ServiceConfiguration}; use modules::service_urls; use boot; @@ -48,8 +49,15 @@ pub fn main() { let remote_client = dependency!(RemoteClient, &service_urls::with_base(&service_config.io_path, service_urls::CLIENT)); let remote_snapshot = dependency!(RemoteSnapshotService, &service_urls::with_base(&service_config.io_path, service_urls::SNAPSHOT)); + let remote_provider = dependency!(LightProviderClient, &service_urls::with_base(&service_config.io_path, service_urls::LIGHT_PROVIDER)); - let sync = EthSync::new(service_config.sync, remote_client.service().clone(), remote_snapshot.service().clone(), service_config.net).unwrap(); + let sync = EthSync::new(Params { + config: service_config.sync, + chain: remote_client.service().clone(), + snapshot_service: remote_snapshot.service().clone(), + provider: remote_provider.service().clone(), + network_config: service_config.net + }).unwrap(); let _ = boot::main_thread(); let service_stop = Arc::new(AtomicBool::new(false)); diff --git a/sync/build.rs b/sync/build.rs index c465d5e34..1e08ae652 100644 --- a/sync/build.rs +++ b/sync/build.rs @@ -16,6 +16,10 @@ extern crate ethcore_ipc_codegen; +#[cfg(feature = "ipc")] fn main() { - ethcore_ipc_codegen::derive_ipc_cond("src/api.rs", cfg!(feature="ipc")).unwrap(); + ethcore_ipc_codegen::derive_ipc_cond("src/api.rs", true).unwrap(); } + +#[cfg(not(feature = "ipc"))] +fn main() {} diff --git a/sync/src/api.rs b/sync/src/api.rs index d8df149c8..acc593fb1 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -33,6 +33,7 @@ use ipc::{BinaryConvertable, BinaryConvertError, IpcConfig}; use std::str::FromStr; use parking_lot::RwLock; use chain::{ETH_PACKET_COUNT, SNAPSHOT_SYNC_PACKET_COUNT}; +use light::net::{LightProtocol, Params as LightParams, Capabilities, Handler as LightHandler, EventContext}; pub const WARP_SYNC_PROTOCOL_ID: ProtocolId = *b"par"; @@ -47,10 +48,14 @@ pub struct SyncConfig { pub network_id: u64, /// Main "eth" subprotocol name. pub subprotocol_name: [u8; 3], + /// Light "les" subprotocol name. + pub light_subprotocol_name: [u8; 3], /// Fork block to check pub fork_block: Option<(BlockNumber, H256)>, /// Enable snapshot sync pub warp_sync: bool, + /// Enable light client server. + pub serve_light: bool, } impl Default for SyncConfig { @@ -60,8 +65,10 @@ impl Default for SyncConfig { download_old_blocks: true, network_id: 1, subprotocol_name: *b"eth", + light_subprotocol_name: *b"les", fork_block: None, warp_sync: false, + serve_light: false, } } } @@ -116,30 +123,74 @@ pub struct PeerInfo { pub eth_difficulty: Option<U256>, } +/// EthSync initialization parameters. +#[cfg_attr(feature = "ipc", derive(Binary))] +pub struct Params { + /// Configuration. + pub config: SyncConfig, + /// Blockchain client. + pub chain: Arc<BlockChainClient>, + /// Snapshot service. + pub snapshot_service: Arc<SnapshotService>, + /// Light data provider. + pub provider: Arc<::light::Provider>, + /// Network layer configuration. + pub network_config: NetworkConfiguration, +} + /// Ethereum network protocol handler pub struct EthSync { /// Network service network: NetworkService, - /// Protocol handler - handler: Arc<SyncProtocolHandler>, + /// Main (eth/par) protocol handler + sync_handler: Arc<SyncProtocolHandler>, + /// Light (les) protocol handler + light_proto: Option<Arc<LightProtocol>>, /// The main subprotocol name subprotocol_name: [u8; 3], + /// Light subprotocol name. + light_subprotocol_name: [u8; 3], } impl EthSync { /// Creates and register protocol with the network service - pub fn new(config: SyncConfig, chain: Arc<BlockChainClient>, snapshot_service: Arc<SnapshotService>, network_config: NetworkConfiguration) -> Result<Arc<EthSync>, NetworkError> { - let chain_sync = ChainSync::new(config, &*chain); - let service = try!(NetworkService::new(try!(network_config.clone().into_basic()))); - let sync = Arc::new(EthSync{ + pub fn new(params: Params) -> Result<Arc<EthSync>, NetworkError> { + let pruning_info = params.chain.pruning_info(); + let light_proto = match params.config.serve_light { + false => None, + true => Some({ + let light_params = LightParams { + network_id: params.config.network_id, + flow_params: Default::default(), + capabilities: Capabilities { + serve_headers: true, + serve_chain_since: Some(pruning_info.earliest_chain), + serve_state_since: Some(pruning_info.earliest_state), + tx_relay: true, + }, + }; + + let mut light_proto = LightProtocol::new(params.provider, light_params); + light_proto.add_handler(Box::new(TxRelay(params.chain.clone()))); + + Arc::new(light_proto) + }) + }; + + let chain_sync = ChainSync::new(params.config, &*params.chain); + let service = try!(NetworkService::new(try!(params.network_config.clone().into_basic()))); + + let sync = Arc::new(EthSync { network: service, - handler: Arc::new(SyncProtocolHandler { + sync_handler: Arc::new(SyncProtocolHandler { sync: RwLock::new(chain_sync), - chain: chain, - snapshot_service: snapshot_service, + chain: params.chain, + snapshot_service: params.snapshot_service, overlay: RwLock::new(HashMap::new()), }), - subprotocol_name: config.subprotocol_name, + light_proto: light_proto, + subprotocol_name: params.config.subprotocol_name, + light_subprotocol_name: params.config.light_subprotocol_name, }); Ok(sync) @@ -150,14 +201,15 @@ impl EthSync { impl SyncProvider for EthSync { /// Get sync status fn status(&self) -> SyncStatus { - self.handler.sync.write().status() + self.sync_handler.sync.write().status() } /// Get sync peers fn peers(&self) -> Vec<PeerInfo> { + // TODO: [rob] LES peers/peer info self.network.with_context_eval(self.subprotocol_name, |context| { - let sync_io = NetSyncIo::new(context, &*self.handler.chain, &*self.handler.snapshot_service, &self.handler.overlay); - self.handler.sync.write().peers(&sync_io) + let sync_io = NetSyncIo::new(context, &*self.sync_handler.chain, &*self.sync_handler.snapshot_service, &self.sync_handler.overlay); + self.sync_handler.sync.write().peers(&sync_io) }).unwrap_or(Vec::new()) } @@ -166,7 +218,7 @@ impl SyncProvider for EthSync { } fn transactions_stats(&self) -> BTreeMap<H256, TransactionStats> { - let sync = self.handler.sync.read(); + let sync = self.sync_handler.sync.read(); sync.transactions_stats() .iter() .map(|(hash, stats)| (*hash, stats.into())) @@ -228,8 +280,8 @@ impl ChainNotify for EthSync { _duration: u64) { self.network.with_context(self.subprotocol_name, |context| { - let mut sync_io = NetSyncIo::new(context, &*self.handler.chain, &*self.handler.snapshot_service, &self.handler.overlay); - self.handler.sync.write().chain_new_blocks( + let mut sync_io = NetSyncIo::new(context, &*self.sync_handler.chain, &*self.sync_handler.snapshot_service, &self.sync_handler.overlay); + self.sync_handler.sync.write().chain_new_blocks( &mut sync_io, &imported, &invalid, @@ -245,19 +297,36 @@ impl ChainNotify for EthSync { Err(err) => warn!("Error starting network: {}", err), _ => {}, } - self.network.register_protocol(self.handler.clone(), self.subprotocol_name, ETH_PACKET_COUNT, &[62u8, 63u8]) + self.network.register_protocol(self.sync_handler.clone(), self.subprotocol_name, ETH_PACKET_COUNT, &[62u8, 63u8]) .unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e)); // register the warp sync subprotocol - self.network.register_protocol(self.handler.clone(), WARP_SYNC_PROTOCOL_ID, SNAPSHOT_SYNC_PACKET_COUNT, &[1u8]) + self.network.register_protocol(self.sync_handler.clone(), WARP_SYNC_PROTOCOL_ID, SNAPSHOT_SYNC_PACKET_COUNT, &[1u8]) .unwrap_or_else(|e| warn!("Error registering snapshot sync protocol: {:?}", e)); + + // register the light protocol. + if let Some(light_proto) = self.light_proto.as_ref().map(|x| x.clone()) { + self.network.register_protocol(light_proto, self.light_subprotocol_name, ::light::net::PACKET_COUNT, ::light::net::PROTOCOL_VERSIONS) + .unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e)); + } } fn stop(&self) { - self.handler.snapshot_service.abort_restore(); + self.sync_handler.snapshot_service.abort_restore(); self.network.stop().unwrap_or_else(|e| warn!("Error stopping network: {:?}", e)); } } +/// LES event handler. +/// Simply queues transactions from light client peers. +struct TxRelay(Arc<BlockChainClient>); + +impl LightHandler for TxRelay { + fn on_transactions(&self, ctx: &EventContext, relay: &[::ethcore::transaction::SignedTransaction]) { + trace!(target: "les", "Relaying {} transactions from peer {}", relay.len(), ctx.peer()); + self.0.queue_transactions(relay.iter().map(|tx| ::rlp::encode(tx).to_vec()).collect()) + } +} + impl IpcConfig for ManageNetwork { } impl IpcConfig for SyncProvider { } @@ -304,9 +373,14 @@ impl ManageNetwork for EthSync { fn stop_network(&self) { self.network.with_context(self.subprotocol_name, |context| { - let mut sync_io = NetSyncIo::new(context, &*self.handler.chain, &*self.handler.snapshot_service, &self.handler.overlay); - self.handler.sync.write().abort(&mut sync_io); + let mut sync_io = NetSyncIo::new(context, &*self.sync_handler.chain, &*self.sync_handler.snapshot_service, &self.sync_handler.overlay); + self.sync_handler.sync.write().abort(&mut sync_io); }); + + if let Some(light_proto) = self.light_proto.as_ref() { + light_proto.abort(); + } + self.stop(); } @@ -452,4 +526,4 @@ pub struct ServiceConfiguration { pub net: NetworkConfiguration, /// IPC path. pub io_path: String, -} +} \ No newline at end of file diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 801fcbbd5..09f79f16f 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -67,7 +67,7 @@ mod api { #[cfg(not(feature = "ipc"))] mod api; -pub use api::{EthSync, SyncProvider, ManageNetwork, SyncConfig, +pub use api::{EthSync, Params, SyncProvider, ManageNetwork, SyncConfig, ServiceConfiguration, NetworkConfiguration, PeerInfo, AllowIP, TransactionStats}; pub use chain::{SyncStatus, SyncState}; pub use network::{is_valid_node_url, NonReservedPeerMode, NetworkError}; From e7ce8c95588878f71ffeeb6509dbda0944e82ccd Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Thu, 8 Dec 2016 23:57:09 +0100 Subject: [PATCH 20/74] light: broadcast status updates to peers --- ethcore/light/src/net/mod.rs | 21 ++++++++++++++++++++- sync/src/api.rs | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index fd64f4a4b..7c186aa94 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -27,7 +27,7 @@ use network::{NetworkProtocolHandler, NetworkContext, NetworkError, PeerId}; use rlp::{RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; use util::{Bytes, Mutex, RwLock, U256}; -use time::SteadyTime; +use time::{Duration, SteadyTime}; use std::collections::HashMap; use std::sync::Arc; @@ -54,6 +54,9 @@ pub use self::status::{Status, Capabilities, Announcement}; const TIMEOUT: TimerToken = 0; const TIMEOUT_INTERVAL_MS: u64 = 1000; +// minimum interval between updates. +const UPDATE_INTERVAL_MS: i64 = 5000; + // Supported protocol versions. pub const PROTOCOL_VERSIONS: &'static [u8] = &[1]; @@ -107,6 +110,7 @@ pub struct ReqId(usize); // may not have received one for. struct PendingPeer { sent_head: H256, + last_update: SteadyTime, } // data about each peer. @@ -117,6 +121,7 @@ struct Peer { capabilities: Capabilities, remote_flow: FlowParams, sent_head: H256, // last head we've given them. + last_update: SteadyTime, } impl Peer { @@ -293,6 +298,7 @@ impl LightProtocol { /// The announcement is expected to be valid. pub fn make_announcement(&self, io: &IoContext, mut announcement: Announcement) { let mut reorgs_map = HashMap::new(); + let now = SteadyTime::now(); // update stored capabilities self.capabilities.write().update_from(&announcement); @@ -300,6 +306,17 @@ impl LightProtocol { // calculate reorg info and send packets for (peer_id, peer_info) in self.peers.read().iter() { let mut peer_info = peer_info.lock(); + + // TODO: "urgent" announcements like new blocks? + // the timer approach will skip 1 (possibly 2) in rare occasions. + if peer_info.sent_head == announcement.head_hash || + peer_info.status.head_num >= announcement.head_num || + now - peer_info.last_update < Duration::milliseconds(UPDATE_INTERVAL_MS) { + continue + } + + peer_info.last_update = now; + 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) { @@ -496,6 +513,7 @@ impl LightProtocol { Ok(PendingPeer { sent_head: chain_info.best_block_hash, + last_update: SteadyTime::now(), }) } @@ -523,6 +541,7 @@ impl LightProtocol { capabilities: capabilities.clone(), remote_flow: flow_params, sent_head: pending.sent_head, + last_update: pending.last_update, })); for handler in &self.handlers { diff --git a/sync/src/api.rs b/sync/src/api.rs index acc593fb1..7c531bf7c 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -279,6 +279,8 @@ impl ChainNotify for EthSync { sealed: Vec<H256>, _duration: u64) { + use light::net::Announcement; + self.network.with_context(self.subprotocol_name, |context| { let mut sync_io = NetSyncIo::new(context, &*self.sync_handler.chain, &*self.sync_handler.snapshot_service, &self.sync_handler.overlay); self.sync_handler.sync.write().chain_new_blocks( @@ -289,6 +291,25 @@ impl ChainNotify for EthSync { &retracted, &sealed); }); + + self.network.with_context(self.light_subprotocol_name, |context| { + let light_proto = match self.light_proto.as_ref() { + Some(lp) => lp, + None => return, + }; + + let chain_info = self.sync_handler.chain.chain_info(); + light_proto.make_announcement(context, Announcement { + head_hash: chain_info.best_block_hash, + head_num: chain_info.best_block_number, + head_td: chain_info.total_difficulty, + reorg_depth: 0, // recalculated on a per-peer basis. + serve_headers: false, // these fields consist of _changes_ in capability. + serve_state_since: None, + serve_chain_since: None, + tx_relay: false, + }) + }) } fn start(&self) { From d53c47aa69bc9717a55819391961b417fb079796 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Fri, 9 Dec 2016 00:35:34 +0100 Subject: [PATCH 21/74] more tracing --- ethcore/light/src/net/mod.rs | 9 +++++++++ ethcore/light/src/net/status.rs | 1 + 2 files changed, 10 insertions(+) diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 7c186aa94..0eda7b77c 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -230,6 +230,8 @@ pub struct LightProtocol { impl LightProtocol { /// Create a new instance of the protocol manager. pub fn new(provider: Arc<Provider>, params: Params) -> Self { + debug!(target: "les", "Initializing LES handler"); + let genesis_hash = provider.chain_info().genesis_hash; LightProtocol { provider: provider, @@ -400,6 +402,8 @@ impl LightProtocol { fn handle_packet(&self, io: &IoContext, peer: &PeerId, packet_id: u8, data: &[u8]) { let rlp = UntrustedRlp::new(data); + trace!(target: "les", "Incoming packet {} from peer {}", packet_id, peer); + // handle the packet let res = match packet_id { packet::STATUS => self.status(peer, io, rlp), @@ -452,6 +456,8 @@ impl LightProtocol { fn on_connect(&self, peer: &PeerId, io: &IoContext) { let peer = *peer; + trace!(target: "les", "Peer {} connecting", peer); + match self.send_status(peer, io) { Ok(pending_peer) => { self.pending_peers.write().insert(peer, pending_peer); @@ -465,6 +471,9 @@ impl LightProtocol { // called when a peer disconnects. fn on_disconnect(&self, peer: PeerId, io: &IoContext) { + trace!(target: "les", "Peer {} disconnecting", peer); + + self.pending_peers.write().remove(&peer); if self.peers.write().remove(&peer).is_some() { let unfulfilled: Vec<_> = self.pending_requests.read() diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index 6d0a76823..eb80fbe44 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -98,6 +98,7 @@ impl<'a> Parser<'a> { // 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<UntrustedRlp<'a>, DecoderError> { + trace!(target: "les", "Expecting key {}", key.as_str()); let pre_pos = self.pos; if let Some((k, val)) = try!(self.get_next()) { if k == key { return Ok(val) } From 07d7a3731958f60644bfbff599c87a7b1108d0a5 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Fri, 9 Dec 2016 01:06:51 +0100 Subject: [PATCH 22/74] les: make peer buffer flow params optional --- ethcore/light/src/net/error.rs | 4 ++ ethcore/light/src/net/mod.rs | 50 ++++++++++++---------- ethcore/light/src/net/status.rs | 66 ++++++++++++++++++++++-------- ethcore/light/src/net/tests/mod.rs | 24 +++++------ 4 files changed, 95 insertions(+), 49 deletions(-) diff --git a/ethcore/light/src/net/error.rs b/ethcore/light/src/net/error.rs index 86dbd54ba..c9ba85a42 100644 --- a/ethcore/light/src/net/error.rs +++ b/ethcore/light/src/net/error.rs @@ -56,6 +56,8 @@ pub enum Error { UnknownPeer, /// Unsolicited response. UnsolicitedResponse, + /// Not a server. + NotServer, } impl Error { @@ -70,6 +72,7 @@ impl Error { Error::WrongNetwork => Punishment::Disable, Error::UnknownPeer => Punishment::Disconnect, Error::UnsolicitedResponse => Punishment::Disable, + Error::NotServer => Punishment::Disable, } } } @@ -97,6 +100,7 @@ impl fmt::Display for Error { Error::WrongNetwork => write!(f, "Wrong network"), Error::UnknownPeer => write!(f, "Unknown peer"), Error::UnsolicitedResponse => write!(f, "Peer provided unsolicited data"), + Error::NotServer => write!(f, "Peer not a server."), } } } \ No newline at end of file diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 0eda7b77c..1eb89a92f 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -116,10 +116,9 @@ struct PendingPeer { // data about each peer. struct Peer { local_buffer: Buffer, // their buffer relative to us - remote_buffer: Buffer, // our buffer relative to them status: Status, capabilities: Capabilities, - remote_flow: FlowParams, + remote_flow: Option<(Buffer, FlowParams)>, sent_head: H256, // last head we've given them. last_update: SteadyTime, } @@ -142,12 +141,6 @@ impl Peer { 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); - } } /// An LES event handler. @@ -250,16 +243,21 @@ 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<usize> { - self.peers.read().get(&peer).map(|peer| { + self.peers.read().get(&peer).and_then(|peer| { let mut peer = peer.lock(); - peer.recharge_remote(); - peer.remote_flow.max_amount(&peer.remote_buffer, kind) + match peer.remote_flow.as_mut() { + Some(&mut (ref mut buf, ref flow)) => { + flow.recharge(buf); + Some(flow.max_amount(&*buf, kind)) + } + None => None, + } }) } /// Make a request to a peer. /// - /// Fails on: nonexistent peer, network error, + /// Fails on: nonexistent peer, network error, peer not server, /// insufficient buffer. Does not check capabilities before sending. /// On success, returns a request id which can later be coordinated /// with an event. @@ -268,10 +266,14 @@ impl LightProtocol { 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)); + match peer.remote_flow.as_mut() { + Some(&mut (ref mut buf, ref flow)) => { + flow.recharge(buf); + let max = flow.compute_cost(request.kind(), request.amount()); + try!(buf.deduct_cost(max)); + } + None => return Err(Error::NotServer), + } let req_id = self.req_id.fetch_add(1, Ordering::SeqCst); let packet_data = encode_request(&request, req_id); @@ -390,8 +392,13 @@ impl LightProtocol { match peers.get(peer) { Some(peer_info) => { let mut peer_info = peer_info.lock(); - let actual_buffer = ::std::cmp::min(cur_buffer, *peer_info.remote_flow.limit()); - peer_info.remote_buffer.update_to(actual_buffer); + match peer_info.remote_flow.as_mut() { + Some(&mut (ref mut buf, ref mut flow)) => { + let actual_buffer = ::std::cmp::min(cur_buffer, *flow.limit()); + buf.update_to(actual_buffer) + } + None => return Err(Error::NotServer), // this really should be impossible. + } Ok(ReqId(req_id)) } None => Err(Error::UnknownPeer), // probably only occurs in a race of some kind. @@ -516,7 +523,7 @@ impl LightProtocol { }; let capabilities = self.capabilities.read().clone(); - let status_packet = status::write_handshake(&status, &capabilities, &self.flow_params); + let status_packet = status::write_handshake(&status, &capabilities, Some(&self.flow_params)); io.send(peer, packet::STATUS, status_packet); @@ -543,12 +550,13 @@ impl LightProtocol { return Err(Error::WrongNetwork); } + let remote_flow = flow_params.map(|params| (params.create_buffer(), params)); + self.peers.write().insert(*peer, Mutex::new(Peer { local_buffer: self.flow_params.create_buffer(), - remote_buffer: flow_params.create_buffer(), status: status.clone(), capabilities: capabilities.clone(), - remote_flow: flow_params, + remote_flow: remote_flow, sent_head: pending.sent_head, last_update: pending.last_update, })); diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index eb80fbe44..59981b88d 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -198,7 +198,7 @@ impl Capabilities { /// - chain status /// - serving capabilities /// - buffer flow parameters -pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, FlowParams), DecoderError> { +pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, Option<FlowParams>), DecoderError> { let mut parser = Parser { pos: 0, rlp: rlp, @@ -221,17 +221,20 @@ pub fn parse_handshake(rlp: UntrustedRlp) -> Result<(Status, Capabilities, FlowP tx_relay: parser.expect_raw(Key::TxRelay).is_ok(), }; - let flow_params = FlowParams::new( - try!(parser.expect(Key::BufferLimit)), - try!(parser.expect(Key::BufferCostTable)), - try!(parser.expect(Key::BufferRechargeRate)), - ); + let flow_params = match ( + parser.expect(Key::BufferLimit), + parser.expect(Key::BufferCostTable), + parser.expect(Key::BufferRechargeRate) + ) { + (Ok(bl), Ok(bct), Ok(brr)) => Some(FlowParams::new(bl, bct, brr)), + _ => None, + }; 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<u8> { +pub fn write_handshake(status: &Status, capabilities: &Capabilities, flow_params: Option<&FlowParams>) -> Vec<u8> { let mut pairs = Vec::new(); pairs.push(encode_pair(Key::ProtocolVersion, &status.protocol_version)); pairs.push(encode_pair(Key::NetworkId, &(status.network_id as u64))); @@ -253,9 +256,11 @@ pub fn write_handshake(status: &Status, capabilities: &Capabilities, flow_params 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())); + if let Some(flow_params) = flow_params { + 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()); @@ -386,14 +391,14 @@ mod tests { 1000.into(), ); - let handshake = write_handshake(&status, &capabilities, &flow_params); + let handshake = write_handshake(&status, &capabilities, Some(&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); + assert_eq!(read_flow.unwrap(), flow_params); } #[test] @@ -421,14 +426,14 @@ mod tests { 1000.into(), ); - let handshake = write_handshake(&status, &capabilities, &flow_params); + let handshake = write_handshake(&status, &capabilities, Some(&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); + assert_eq!(read_flow.unwrap(), flow_params); } #[test] @@ -456,7 +461,7 @@ mod tests { 1000.into(), ); - let handshake = write_handshake(&status, &capabilities, &flow_params); + let handshake = write_handshake(&status, &capabilities, Some(&flow_params)); let interleaved = { let handshake = UntrustedRlp::new(&handshake); let mut stream = RlpStream::new_list(handshake.item_count() * 3); @@ -478,7 +483,7 @@ mod tests { assert_eq!(read_status, status); assert_eq!(read_capabilities, capabilities); - assert_eq!(read_flow, flow_params); + assert_eq!(read_flow.unwrap(), flow_params); } #[test] @@ -528,4 +533,33 @@ mod tests { let out = stream.drain(); assert!(parse_announcement(UntrustedRlp::new(&out)).is_ok()); } + + #[test] + fn optional_flow() { + let status = Status { + protocol_version: 1, + network_id: 1, + head_td: U256::default(), + head_hash: H256::default(), + head_num: 10, + genesis_hash: 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 handshake = write_handshake(&status, &capabilities, None); + + let (read_status, read_capabilities, read_flow) + = parse_handshake(UntrustedRlp::new(&handshake)).unwrap(); + + assert_eq!(read_status, status); + assert_eq!(read_capabilities, capabilities); + assert!(read_flow.is_none()); + } } \ No newline at end of file diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 30ab2bab2..7a159b6a4 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -204,7 +204,7 @@ fn handshake_expected() { let status = status(provider.client.chain_info()); - let packet_body = write_handshake(&status, &capabilities, &flow_params); + let packet_body = write_handshake(&status, &capabilities, Some(&flow_params)); proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); } @@ -220,7 +220,7 @@ fn genesis_mismatch() { let mut status = status(provider.client.chain_info()); status.genesis_hash = H256::default(); - let packet_body = write_handshake(&status, &capabilities, &flow_params); + let packet_body = write_handshake(&status, &capabilities, Some(&flow_params)); proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); } @@ -235,12 +235,12 @@ fn buffer_overflow() { let status = status(provider.client.chain_info()); { - let packet_body = write_handshake(&status, &capabilities, &flow_params); + let packet_body = write_handshake(&status, &capabilities, Some(&flow_params)); proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); } { - let my_status = write_handshake(&status, &capabilities, &flow_params); + let my_status = write_handshake(&status, &capabilities, Some(&flow_params)); proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); } @@ -267,14 +267,14 @@ fn get_block_headers() { let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + let my_status = write_handshake(&cur_status, &capabilities, Some(&flow_params)); provider.client.add_blocks(100, EachBlockWith::Nothing); let cur_status = status(provider.client.chain_info()); { - let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params)); proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); } @@ -317,14 +317,14 @@ fn get_block_bodies() { let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + let my_status = write_handshake(&cur_status, &capabilities, Some(&flow_params)); provider.client.add_blocks(100, EachBlockWith::Nothing); let cur_status = status(provider.client.chain_info()); { - let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params)); proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); } @@ -364,14 +364,14 @@ fn get_block_receipts() { let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); let cur_status = status(provider.client.chain_info()); - let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + let my_status = write_handshake(&cur_status, &capabilities, Some(&flow_params)); provider.client.add_blocks(1000, EachBlockWith::Nothing); let cur_status = status(provider.client.chain_info()); { - let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params)); proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); } @@ -419,7 +419,7 @@ fn get_state_proofs() { let cur_status = status(provider.client.chain_info()); { - let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params)); proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone())); proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body); } @@ -468,7 +468,7 @@ fn get_contract_code() { let cur_status = status(provider.client.chain_info()); { - let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params)); proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone())); proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body); } From 296301e284d1a827fbee54750803d58480245e74 Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jaco@ethcore.io> Date: Fri, 9 Dec 2016 10:55:46 +0100 Subject: [PATCH 23/74] Extend tests for TxList --- js/src/ui/TxList/TxRow/index.js | 17 ++++ js/src/ui/TxList/TxRow/txRow.js | 133 +++++++++++++++++++++++++++ js/src/ui/TxList/TxRow/txRow.spec.js | 51 ++++++++++ js/src/ui/TxList/txList.js | 114 +---------------------- js/src/ui/TxList/txList.spec.js | 52 +++++++++++ 5 files changed, 254 insertions(+), 113 deletions(-) create mode 100644 js/src/ui/TxList/TxRow/index.js create mode 100644 js/src/ui/TxList/TxRow/txRow.js create mode 100644 js/src/ui/TxList/TxRow/txRow.spec.js create mode 100644 js/src/ui/TxList/txList.spec.js diff --git a/js/src/ui/TxList/TxRow/index.js b/js/src/ui/TxList/TxRow/index.js new file mode 100644 index 000000000..90243cd5f --- /dev/null +++ b/js/src/ui/TxList/TxRow/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see <http://www.gnu.org/licenses/>. + +export default from './txRow'; diff --git a/js/src/ui/TxList/TxRow/txRow.js b/js/src/ui/TxList/TxRow/txRow.js new file mode 100644 index 000000000..98e948a38 --- /dev/null +++ b/js/src/ui/TxList/TxRow/txRow.js @@ -0,0 +1,133 @@ +// 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 <http://www.gnu.org/licenses/>. + +import moment from 'moment'; +import React, { Component, PropTypes } from 'react'; + +import { txLink, addressLink } from '~/3rdparty/etherscan/links'; + +import IdentityIcon from '../../IdentityIcon'; +import IdentityName from '../../IdentityName'; +import MethodDecoding from '../../MethodDecoding'; + +import styles from '../txList.css'; + +export default class TxRow extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + }; + + static propTypes = { + tx: PropTypes.object.isRequired, + address: PropTypes.string.isRequired, + isTest: PropTypes.bool.isRequired, + + block: PropTypes.object, + historic: PropTypes.bool, + className: PropTypes.string + }; + + static defaultProps = { + historic: true + }; + + render () { + const { tx, address, isTest, historic, className } = this.props; + + return ( + <tr className={ className || '' }> + { this.renderBlockNumber(tx.blockNumber) } + { this.renderAddress(tx.from) } + <td className={ styles.transaction }> + { this.renderEtherValue(tx.value) } + <div>⇒</div> + <div> + <a + className={ styles.link } + href={ txLink(tx.hash, isTest) } + target='_blank'> + { `${tx.hash.substr(2, 6)}...${tx.hash.slice(-6)}` } + </a> + </div> + </td> + { this.renderAddress(tx.to) } + <td className={ styles.method }> + <MethodDecoding + historic={ historic } + address={ address } + transaction={ tx } /> + </td> + </tr> + ); + } + + renderAddress (address) { + const { isTest } = this.props; + + let esLink = null; + if (address) { + esLink = ( + <a + href={ addressLink(address, isTest) } + target='_blank' + className={ styles.link }> + <IdentityName address={ address } shorten /> + </a> + ); + } + + return ( + <td className={ styles.address }> + <div className={ styles.center }> + <IdentityIcon + center + className={ styles.icon } + address={ address } /> + </div> + <div className={ styles.center }> + { esLink || 'DEPLOY' } + </div> + </td> + ); + } + + renderEtherValue (_value) { + const { api } = this.context; + const value = api.util.fromWei(_value); + + if (value.eq(0)) { + return <div className={ styles.value }>{ ' ' }</div>; + } + + return ( + <div className={ styles.value }> + { value.toFormat(5) }<small>ETH</small> + </div> + ); + } + + renderBlockNumber (_blockNumber) { + const { block } = this.props; + const blockNumber = _blockNumber.toNumber(); + + return ( + <td className={ styles.timestamp }> + <div>{ blockNumber && block ? moment(block.timestamp).fromNow() : null }</div> + <div>{ blockNumber ? _blockNumber.toFormat() : 'Pending' }</div> + </td> + ); + } +} diff --git a/js/src/ui/TxList/TxRow/txRow.spec.js b/js/src/ui/TxList/TxRow/txRow.spec.js new file mode 100644 index 000000000..64baa2fe7 --- /dev/null +++ b/js/src/ui/TxList/TxRow/txRow.spec.js @@ -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 <http://www.gnu.org/licenses/>. + +import BigNumber from 'bignumber.js'; +import React from 'react'; +import { shallow } from 'enzyme'; +import sinon from 'sinon'; + +import Api from '~/api'; + +import TxRow from './txRow'; + +const api = new Api({ execute: sinon.stub() }); + +function renderShallow (props) { + return shallow( + <TxRow + { ...props } />, + { context: { api } } + ); +} + +describe('ui/TxRow', () => { + describe('rendering', () => { + it('renders defaults', () => { + const block = { + timestamp: new Date() + }; + const tx = { + blockNumber: new BigNumber(123), + hash: '0x123456789abcdef0123456789abcdef0123456789abcdef', + value: new BigNumber(1) + }; + + expect(renderShallow({ block, tx })).to.be.ok; + }); + }); +}); diff --git a/js/src/ui/TxList/txList.js b/js/src/ui/TxList/txList.js index b8c53c1d9..45face696 100644 --- a/js/src/ui/TxList/txList.js +++ b/js/src/ui/TxList/txList.js @@ -14,128 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -import moment from 'moment'; import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { observer } from 'mobx-react'; -import { txLink, addressLink } from '../../3rdparty/etherscan/links'; - -import IdentityIcon from '../IdentityIcon'; -import IdentityName from '../IdentityName'; -import MethodDecoding from '../MethodDecoding'; import Store from './store'; +import TxRow from './TxRow'; import styles from './txList.css'; -export class TxRow extends Component { - static contextTypes = { - api: PropTypes.object.isRequired - }; - - static propTypes = { - tx: PropTypes.object.isRequired, - address: PropTypes.string.isRequired, - isTest: PropTypes.bool.isRequired, - - block: PropTypes.object, - historic: PropTypes.bool, - className: PropTypes.string - }; - - static defaultProps = { - historic: true - }; - - render () { - const { tx, address, isTest, historic, className } = this.props; - - return ( - <tr className={ className || '' }> - { this.renderBlockNumber(tx.blockNumber) } - { this.renderAddress(tx.from) } - <td className={ styles.transaction }> - { this.renderEtherValue(tx.value) } - <div>⇒</div> - <div> - <a - className={ styles.link } - href={ txLink(tx.hash, isTest) } - target='_blank'> - { `${tx.hash.substr(2, 6)}...${tx.hash.slice(-6)}` } - </a> - </div> - </td> - { this.renderAddress(tx.to) } - <td className={ styles.method }> - <MethodDecoding - historic={ historic } - address={ address } - transaction={ tx } /> - </td> - </tr> - ); - } - - renderAddress (address) { - const { isTest } = this.props; - - let esLink = null; - if (address) { - esLink = ( - <a - href={ addressLink(address, isTest) } - target='_blank' - className={ styles.link }> - <IdentityName address={ address } shorten /> - </a> - ); - } - - return ( - <td className={ styles.address }> - <div className={ styles.center }> - <IdentityIcon - center - className={ styles.icon } - address={ address } /> - </div> - <div className={ styles.center }> - { esLink || 'DEPLOY' } - </div> - </td> - ); - } - - renderEtherValue (_value) { - const { api } = this.context; - const value = api.util.fromWei(_value); - - if (value.eq(0)) { - return <div className={ styles.value }>{ ' ' }</div>; - } - - return ( - <div className={ styles.value }> - { value.toFormat(5) }<small>ETH</small> - </div> - ); - } - - renderBlockNumber (_blockNumber) { - const { block } = this.props; - const blockNumber = _blockNumber.toNumber(); - - return ( - <td className={ styles.timestamp }> - <div>{ blockNumber && block ? moment(block.timestamp).fromNow() : null }</div> - <div>{ blockNumber ? _blockNumber.toFormat() : 'Pending' }</div> - </td> - ); - } -} - @observer class TxList extends Component { static contextTypes = { diff --git a/js/src/ui/TxList/txList.spec.js b/js/src/ui/TxList/txList.spec.js new file mode 100644 index 000000000..dcaf5fb78 --- /dev/null +++ b/js/src/ui/TxList/txList.spec.js @@ -0,0 +1,52 @@ +// 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 <http://www.gnu.org/licenses/>. + +import React from 'react'; +import { shallow } from 'enzyme'; +import sinon from 'sinon'; + +import Api from '~/api'; + +import TxList from './txList'; + +const api = new Api({ execute: sinon.stub() }); + +const STORE = { + getState: () => { + return { + nodeStatus: { + isTest: true + } + }; + } +}; + +function renderShallow (props) { + return shallow( + <TxList + store={ STORE } + { ...props } />, + { context: { api } } + ); +} + +describe('ui/TxList', () => { + describe('rendering', () => { + it('renders defaults', () => { + expect(renderShallow()).to.be.ok; + }); + }); +}); From 1f4c84cbf1a58cef3daf62151a54e580bf7a3361 Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jaco@ethcore.io> Date: Fri, 9 Dec 2016 13:03:46 +0100 Subject: [PATCH 24/74] Theme tests --- js/src/ui/Theme/theme.spec.js | 55 +++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 js/src/ui/Theme/theme.spec.js diff --git a/js/src/ui/Theme/theme.spec.js b/js/src/ui/Theme/theme.spec.js new file mode 100644 index 000000000..9d2aa1bd8 --- /dev/null +++ b/js/src/ui/Theme/theme.spec.js @@ -0,0 +1,55 @@ +// 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 <http://www.gnu.org/licenses/>. + +import getMuiTheme from 'material-ui/styles/getMuiTheme'; +import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme'; + +const muiTheme = getMuiTheme(lightBaseTheme); + +import theme from './theme'; + +describe('ui/Theme', () => { + it('is MUI-based', () => { + expect(Object.keys(theme)).to.deep.equal(Object.keys(muiTheme).concat('parity')); + }); + + it('allows setting of Parity backgrounds', () => { + expect(typeof theme.parity.setBackgroundSeed === 'function').to.be.true; + expect(typeof theme.parity.getBackgroundStyle === 'function').to.be.true; + }); + + describe('parity', () => { + describe('setBackgroundSeed', () => { + const SEED = 'testseed'; + + beforeEach(() => { + theme.parity.setBackgroundSeed(SEED); + }); + + it('sets the correct theme values', () => { + expect(theme.parity.backgroundSeed).to.equal(SEED); + }); + }); + + describe('getBackgroundStyle', () => { + it('generates a style containing background', () => { + const style = theme.parity.getBackgroundStyle(); + + expect(style).to.have.property('background'); + }); + }); + }); +}); From bb209424ac5f00b30ed6202f2090c3ced55250fa Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jaco@ethcore.io> Date: Fri, 9 Dec 2016 13:36:46 +0100 Subject: [PATCH 25/74] IdentityName flags testing --- js/src/ui/IdentityName/identityName.spec.js | 76 +++++++++++++++++++++ js/src/ui/TxList/txList.spec.js | 2 + 2 files changed, 78 insertions(+) create mode 100644 js/src/ui/IdentityName/identityName.spec.js diff --git a/js/src/ui/IdentityName/identityName.spec.js b/js/src/ui/IdentityName/identityName.spec.js new file mode 100644 index 000000000..554a5be0a --- /dev/null +++ b/js/src/ui/IdentityName/identityName.spec.js @@ -0,0 +1,76 @@ +// 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 <http://www.gnu.org/licenses/>. + +import React from 'react'; +import { mount } from 'enzyme'; +import sinon from 'sinon'; + +import IdentityName from './identityName'; + +const ADDR_A = '0x123456789abcdef0123456789A'; +const ADDR_B = '0x123456789abcdef0123456789B'; +const ADDR_C = '0x123456789abcdef0123456789C'; +const STORE = { + dispatch: sinon.stub(), + subscribe: sinon.stub(), + getState: () => { + return { + balances: { + tokens: {} + }, + personal: { + accountsInfo: { + [ADDR_A]: { name: 'testing' }, + [ADDR_B]: {} + } + } + }; + } +}; + +function render (props) { + return mount( + <IdentityName + store={ STORE } + { ...props } /> + ); +} + +describe('ui/IdentityName', () => { + describe('rendering', () => { + it('renders defaults', () => { + expect(render()).to.be.ok; + }); + + describe('account not found', () => { + it('renders null with empty', () => { + expect(render({ address: ADDR_C, empty: true }).html()).to.be.null; + }); + + it('renders address without empty', () => { + expect(render({ address: ADDR_C }).text()).to.equal(ADDR_C); + }); + + it('renders short address with shorten', () => { + expect(render({ address: ADDR_C, shorten: true }).text()).to.equal('123456…56789c'); + }); + + it('renders unknown with flag', () => { + expect(render({ address: ADDR_C, unknown: true }).text()).to.equal('UNNAMED'); + }); + }); + }); +}); diff --git a/js/src/ui/TxList/txList.spec.js b/js/src/ui/TxList/txList.spec.js index dcaf5fb78..3f773980f 100644 --- a/js/src/ui/TxList/txList.spec.js +++ b/js/src/ui/TxList/txList.spec.js @@ -25,6 +25,8 @@ import TxList from './txList'; const api = new Api({ execute: sinon.stub() }); const STORE = { + dispatch: sinon.stub(), + subscribe: sinon.stub(), getState: () => { return { nodeStatus: { From 5f37c936595426b367a6f04dbb38c1f3593b8244 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Fri, 9 Dec 2016 15:04:54 +0100 Subject: [PATCH 26/74] les: use negotiated protocol version --- ethcore/light/src/net/context.rs | 7 +++++++ ethcore/light/src/net/error.rs | 8 ++++++++ ethcore/light/src/net/mod.rs | 22 +++++++++++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs index f2d5ab907..c05e69b0f 100644 --- a/ethcore/light/src/net/context.rs +++ b/ethcore/light/src/net/context.rs @@ -38,6 +38,9 @@ pub trait IoContext { /// Disable a peer -- this is a disconnect + a time-out. fn disable_peer(&self, peer: PeerId); + + /// Get a peer's protocol version. + fn protocol_version(&self, peer: PeerId) -> Option<u8>; } impl<'a> IoContext for NetworkContext<'a> { @@ -60,6 +63,10 @@ impl<'a> IoContext for NetworkContext<'a> { fn disable_peer(&self, peer: PeerId) { NetworkContext::disable_peer(self, peer); } + + fn protocol_version(&self, peer: PeerId) -> Option<u8> { + self.protocol_version(self.subprotocol_name(), peer) + } } /// Context for a protocol event. diff --git a/ethcore/light/src/net/error.rs b/ethcore/light/src/net/error.rs index c9ba85a42..01a15928b 100644 --- a/ethcore/light/src/net/error.rs +++ b/ethcore/light/src/net/error.rs @@ -58,6 +58,10 @@ pub enum Error { UnsolicitedResponse, /// Not a server. NotServer, + /// Unsupported protocol version. + UnsupportedProtocolVersion(u8), + /// Bad protocol version. + BadProtocolVersion, } impl Error { @@ -73,6 +77,8 @@ impl Error { Error::UnknownPeer => Punishment::Disconnect, Error::UnsolicitedResponse => Punishment::Disable, Error::NotServer => Punishment::Disable, + Error::UnsupportedProtocolVersion(_) => Punishment::Disable, + Error::BadProtocolVersion => Punishment::Disable, } } } @@ -101,6 +107,8 @@ impl fmt::Display for Error { Error::UnknownPeer => write!(f, "Unknown peer"), Error::UnsolicitedResponse => write!(f, "Peer provided unsolicited data"), Error::NotServer => write!(f, "Peer not a server."), + Error::UnsupportedProtocolVersion(pv) => write!(f, "Unsupported protocol version: {}", pv), + Error::BadProtocolVersion => write!(f, "Bad protocol version in handshake"), } } } \ No newline at end of file diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 1eb89a92f..481740a48 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -23,7 +23,7 @@ use ethcore::transaction::SignedTransaction; use ethcore::receipt::Receipt; use io::TimerToken; -use network::{NetworkProtocolHandler, NetworkContext, NetworkError, PeerId}; +use network::{NetworkProtocolHandler, NetworkContext, PeerId}; use rlp::{RlpStream, Stream, UntrustedRlp, View}; use util::hash::H256; use util::{Bytes, Mutex, RwLock, U256}; @@ -111,6 +111,7 @@ pub struct ReqId(usize); struct PendingPeer { sent_head: H256, last_update: SteadyTime, + proto_version: u8, } // data about each peer. @@ -121,6 +122,7 @@ struct Peer { remote_flow: Option<(Buffer, FlowParams)>, sent_head: H256, // last head we've given them. last_update: SteadyTime, + proto_version: u8, } impl Peer { @@ -507,17 +509,21 @@ impl LightProtocol { } // send status to a peer. - fn send_status(&self, peer: PeerId, io: &IoContext) -> Result<PendingPeer, NetworkError> { - let chain_info = self.provider.chain_info(); + fn send_status(&self, peer: PeerId, io: &IoContext) -> Result<PendingPeer, Error> { + let proto_version = try!(io.protocol_version(peer).ok_or(Error::WrongNetwork)); - // TODO: could update capabilities here. + if PROTOCOL_VERSIONS.iter().find(|x| **x == proto_version).is_none() { + return Err(Error::UnsupportedProtocolVersion(proto_version)); + } + + let chain_info = self.provider.chain_info(); 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: MAX_PROTOCOL_VERSION as u32, + protocol_version: proto_version as u32, // match peer proto version network_id: self.network_id, last_head: None, }; @@ -530,6 +536,7 @@ impl LightProtocol { Ok(PendingPeer { sent_head: chain_info.best_block_hash, last_update: SteadyTime::now(), + proto_version: proto_version, }) } @@ -550,6 +557,10 @@ impl LightProtocol { return Err(Error::WrongNetwork); } + if Some(status.protocol_version as u8) != io.protocol_version(*peer) { + return Err(Error::BadProtocolVersion); + } + let remote_flow = flow_params.map(|params| (params.create_buffer(), params)); self.peers.write().insert(*peer, Mutex::new(Peer { @@ -559,6 +570,7 @@ impl LightProtocol { remote_flow: remote_flow, sent_head: pending.sent_head, last_update: pending.last_update, + proto_version: pending.proto_version, })); for handler in &self.handlers { From 5d054f08c33df9ec6d17664f3529cc06758ab625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= <tomasz@ethcore.io> Date: Fri, 9 Dec 2016 15:05:03 +0100 Subject: [PATCH 27/74] Clearing old transactions --- ethcore/src/miner/transaction_queue.rs | 41 +++++++++++++++++++++++++- util/table/src/lib.rs | 6 ++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index cd2d3ba47..84981b893 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -81,6 +81,8 @@ //! 3. `remove_all` is used to inform the queue about client (state) nonce changes. //! - It removes all transactions (either from `current` or `future`) with nonce < client nonce //! - It moves matching `future` transactions to `current` +//! 4. `remove_old` is used periodically to clear the transactions if node goes out of sync +//! (in such case `remove_all` is not invoked because we are syncing the chain) use std::ops::Deref; use std::cmp::Ordering; @@ -765,6 +767,20 @@ impl TransactionQueue { assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); } + /// Checks the current nonce for all transactions' senders in the queue and removes the old transactions. + pub fn remove_old<F>(&mut self, fetch_account: &F) where + F: Fn(&Address) -> AccountDetails, + { + let senders = self.current.by_address + .keys() + .map(|key| (*key, fetch_account(key).nonce)) + .collect::<Vec<_>>(); + + for (sender, nonce) in senders { + self.remove_all(sender, nonce); + } + } + /// Penalize transactions from sender of transaction with given hash. /// I.e. it should change the priority of the transaction in the queue. /// @@ -2438,7 +2454,7 @@ mod test { } #[test] - fn should_reject_transactions_below_bas_gas() { + fn should_reject_transactions_below_base_gas() { // given let mut txq = TransactionQueue::default(); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); @@ -2457,4 +2473,27 @@ mod test { } + #[test] + fn should_clear_all_old_transactions() { + // given + let mut txq = TransactionQueue::default(); + let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); + let (tx3, tx4) = new_tx_pair_default(1.into(), 0.into()); + let nonce1 = tx1.nonce; + let new_details = |_a: &Address| AccountDetails { nonce: nonce1 + U256::one(), balance: !U256::zero() }; + + // Insert all transactions + txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx2, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx3, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + txq.add(tx4, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); + assert_eq!(txq.top_transactions().len(), 4); + + // when + txq.remove_old(&new_details); + + // then + assert_eq!(txq.top_transactions().len(), 2); + } + } diff --git a/util/table/src/lib.rs b/util/table/src/lib.rs index a13beab11..f65c1e171 100644 --- a/util/table/src/lib.rs +++ b/util/table/src/lib.rs @@ -18,6 +18,7 @@ use std::hash::Hash; use std::collections::HashMap; +use std::collections::hash_map::Keys; /// Structure to hold double-indexed values /// @@ -41,6 +42,11 @@ impl<Row, Col, Val> Table<Row, Col, Val> } } + /// Returns keys iterator for this Table. + pub fn keys(&self) -> Keys<Row, HashMap<Col, Val>> { + self.map.keys() + } + /// Removes all elements from this Table pub fn clear(&mut self) { self.map.clear(); From 415fccfffbe391a00ee5561623a47ace745dc553 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Fri, 9 Dec 2016 15:20:45 +0100 Subject: [PATCH 28/74] add ethcore-light cov target --- ethcore/light/src/net/tests/mod.rs | 4 ++++ scripts/targets.sh | 1 + 2 files changed, 5 insertions(+) diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 7a159b6a4..6e2bc9f33 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -64,6 +64,10 @@ impl IoContext for Expect { fn disable_peer(&self, peer: PeerId) { assert_eq!(self, &Expect::Punish(peer)); } + + fn protocol_version(&self, _peer: PeerId) -> Option<u8> { + Some(super::MAX_PROTOCOL_VERSION) + } } // can't implement directly for Arc due to cross-crate orphan rules. diff --git a/scripts/targets.sh b/scripts/targets.sh index 529937c23..505875336 100644 --- a/scripts/targets.sh +++ b/scripts/targets.sh @@ -17,4 +17,5 @@ export TARGETS=" -p ethcore-ipc \ -p ethcore-ipc-tests \ -p ethcore-ipc-nano \ + -p ethcore-light \ -p parity" From cee07fef747cb5146a170e066aeb2ca2b5cc129e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= <tomasz@ethcore.io> Date: Fri, 9 Dec 2016 15:54:13 +0100 Subject: [PATCH 29/74] Trigger remove_old on new block --- ethcore/src/miner/miner.rs | 60 +++++++++----------------- ethcore/src/miner/transaction_queue.rs | 42 ++++++++++++------ 2 files changed, 48 insertions(+), 54 deletions(-) diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 8d1f55567..cebd35b09 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1083,20 +1083,6 @@ impl MinerService for Miner { fn chain_new_blocks(&self, chain: &MiningBlockChainClient, _imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256]) { trace!(target: "miner", "chain_new_blocks"); - fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> { - let block = chain - .block(BlockID::Hash(*hash)) - // Client should send message after commit to db and inserting to chain. - .expect("Expected in-chain blocks."); - let block = BlockView::new(&block); - let txs = block.transactions(); - // populate sender - for tx in &txs { - let _sender = tx.sender(); - } - txs - } - // 1. We ignore blocks that were `imported` (because it means that they are not in canon-chain, and transactions // should be still available in the queue. // 2. We ignore blocks that are `invalid` because it doesn't have any meaning in terms of the transactions that @@ -1107,35 +1093,29 @@ impl MinerService for Miner { // Then import all transactions... { - let out_of_chain = retracted - .par_iter() - .map(|h| fetch_transactions(chain, h)); - out_of_chain.for_each(|txs| { - let mut transaction_queue = self.transaction_queue.lock(); - let _ = self.add_transactions_to_queue( - chain, txs, TransactionOrigin::RetractedBlock, &mut transaction_queue - ); - }); + retracted.par_iter() + .map(|hash| { + let block = chain.block(BlockID::Hash(*hash)) + .expect("Client is sending message after commit to db and inserting to chain; the block is available; qed"); + let block = BlockView::new(&block); + let txs = block.transactions(); + // populate sender + for tx in &txs { + let _sender = tx.sender(); + } + txs + }).for_each(|txs| { + let mut transaction_queue = self.transaction_queue.lock(); + let _ = self.add_transactions_to_queue( + chain, txs, TransactionOrigin::RetractedBlock, &mut transaction_queue + ); + }); } - // ...and at the end remove old ones + // ...and at the end remove the old ones { - let in_chain = enacted - .par_iter() - .map(|h: &H256| fetch_transactions(chain, h)); - - in_chain.for_each(|mut txs| { - let mut transaction_queue = self.transaction_queue.lock(); - - let to_remove = txs.drain(..) - .map(|tx| { - tx.sender().expect("Transaction is in block, so sender has to be defined.") - }) - .collect::<HashSet<Address>>(); - for sender in to_remove { - transaction_queue.remove_all(sender, chain.latest_nonce(&sender)); - } - }); + let mut transaction_queue = self.transaction_queue.lock(); + transaction_queue.remove_old(|sender| chain.latest_nonce(sender)); } if enacted.len() > 0 { diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 84981b893..03a9da8e3 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -79,10 +79,10 @@ //! we check if the transactions should go to `current` (comparing state nonce) //! - When it's removed from `current` - all transactions from this sender (`current` & `future`) are recalculated. //! 3. `remove_all` is used to inform the queue about client (state) nonce changes. -//! - It removes all transactions (either from `current` or `future`) with nonce < client nonce -//! - It moves matching `future` transactions to `current` -//! 4. `remove_old` is used periodically to clear the transactions if node goes out of sync -//! (in such case `remove_all` is not invoked because we are syncing the chain) +//! - It removes all transactions (either from `current` or `future`) with nonce < client nonce +//! - It moves matching `future` transactions to `current` +//! 4. `remove_old` is used as convenient method to update the state nonce for all senders in the queue. +//! - Invokes `remove_all` with latest state nonce for all senders. use std::ops::Deref; use std::cmp::Ordering; @@ -754,6 +754,21 @@ impl TransactionQueue { /// Removes all transactions from particular sender up to (excluding) given client (state) nonce. /// Client (State) Nonce = next valid nonce for this sender. pub fn remove_all(&mut self, sender: Address, client_nonce: U256) { + // Check if there is anything in current... + let should_check_in_current = self.current.by_address.row(&sender) + // If nonce == client_nonce nothing is changed + .and_then(|by_nonce| by_nonce.keys().find(|nonce| *nonce < &client_nonce)) + .map(|_| ()); + // ... or future + let should_check_in_future = self.future.by_address.row(&sender) + // if nonce == client_nonce we need to promote to current + .and_then(|by_nonce| by_nonce.keys().find(|nonce| *nonce <= &client_nonce)) + .map(|_| ()); + + if should_check_in_current.or(should_check_in_future).is_none() { + return; + } + // We will either move transaction to future or remove it completely // so there will be no transactions from this sender in current self.last_nonces.remove(&sender); @@ -768,16 +783,16 @@ impl TransactionQueue { } /// Checks the current nonce for all transactions' senders in the queue and removes the old transactions. - pub fn remove_old<F>(&mut self, fetch_account: &F) where - F: Fn(&Address) -> AccountDetails, + pub fn remove_old<F>(&mut self, fetch_nonce: F) where + F: Fn(&Address) -> U256, { - let senders = self.current.by_address - .keys() - .map(|key| (*key, fetch_account(key).nonce)) - .collect::<Vec<_>>(); + let senders = self.current.by_address.keys() + .chain(self.future.by_address.keys()) + .cloned() + .collect::<HashSet<_>>(); - for (sender, nonce) in senders { - self.remove_all(sender, nonce); + for sender in senders { + self.remove_all(sender, fetch_nonce(&sender)); } } @@ -2480,7 +2495,6 @@ mod test { let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx3, tx4) = new_tx_pair_default(1.into(), 0.into()); let nonce1 = tx1.nonce; - let new_details = |_a: &Address| AccountDetails { nonce: nonce1 + U256::one(), balance: !U256::zero() }; // Insert all transactions txq.add(tx1, TransactionOrigin::External, &default_account_details, &gas_estimator).unwrap(); @@ -2490,7 +2504,7 @@ mod test { assert_eq!(txq.top_transactions().len(), 4); // when - txq.remove_old(&new_details); + txq.remove_old(|_| nonce1 + U256::one()); // then assert_eq!(txq.top_transactions().len(), 2); From c91a614c3dd5503a7a8c52b01671fc9422ee3108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= <tomasz@ethcore.io> Date: Fri, 9 Dec 2016 20:24:33 +0100 Subject: [PATCH 30/74] Fixing tests --- ethcore/src/miner/transaction_queue.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 03a9da8e3..b4cc93a9c 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -769,6 +769,11 @@ impl TransactionQueue { return; } + self.remove_all_internal(sender, client_nonce); + } + + /// Always updates future and moves transactions from current to future. + fn remove_all_internal(&mut self, sender: Address, client_nonce: U256) { // We will either move transaction to future or remove it completely // so there will be no transactions from this sender in current self.last_nonces.remove(&sender); @@ -878,7 +883,7 @@ impl TransactionQueue { if order.is_some() { // This will keep consistency in queue // Moves all to future and then promotes a batch from current: - self.remove_all(sender, current_nonce); + self.remove_all_internal(sender, current_nonce); assert_eq!(self.future.by_priority.len() + self.current.by_priority.len(), self.by_hash.len()); return; } From b772901d7737aed1434c4669a31ddd898f9a49f7 Mon Sep 17 00:00:00 2001 From: Robert Habermeier <rphmeier@gmail.com> Date: Fri, 9 Dec 2016 19:36:40 +0100 Subject: [PATCH 31/74] network: process packets only after connection handler finishes --- util/network/src/host.rs | 6 ++++++ util/network/src/session.rs | 42 +++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/util/network/src/host.rs b/util/network/src/host.rs index 887b7ade0..f75158e4a 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -868,6 +868,12 @@ impl Host { let reserved = self.reserved_nodes.read(); if let Some(h) = handlers.get(&p).clone() { h.connected(&NetworkContext::new(io, p, session.clone(), self.sessions.clone(), &reserved), &token); + + // accumulate pending packets. + if let Some(session) = session.as_ref() { + let mut session = session.lock(); + packet_data.extend(session.mark_connected(p)); + } } } for (p, packet_id, data) in packet_data { diff --git a/util/network/src/session.rs b/util/network/src/session.rs index a1336a0ad..9c8bed9da 100644 --- a/util/network/src/session.rs +++ b/util/network/src/session.rs @@ -18,6 +18,8 @@ use std::{str, io}; use std::net::SocketAddr; use std::cmp::Ordering; use std::sync::*; +use std::collections::HashMap; + use mio::*; use mio::deprecated::{Handler, EventLoop}; use mio::tcp::*; @@ -36,6 +38,14 @@ use time; const PING_TIMEOUT_SEC: u64 = 15; const PING_INTERVAL_SEC: u64 = 30; +#[derive(Debug, Clone)] +enum ProtocolState { + // Packets pending protocol on_connect event return. + Pending(Vec<(Vec<u8>, u8)>), + // Protocol connected. + Connected, +} + /// Peer session over encrypted connection. /// When created waits for Hello packet exchange and signals ready state. /// Sends and receives protocol packets and handles basic packes such as ping/pong and disconnect. @@ -49,6 +59,8 @@ pub struct Session { ping_time_ns: u64, pong_time_ns: Option<u64>, state: State, + // Protocol states -- accumulates pending packets until signaled as ready. + protocol_states: HashMap<ProtocolId, ProtocolState>, } enum State { @@ -186,6 +198,7 @@ impl Session { ping_time_ns: 0, pong_time_ns: None, expired: false, + protocol_states: HashMap::new(), }) } @@ -361,6 +374,20 @@ impl Session { self.connection().token() } + /// Signal that a subprotocol has handled the connection successfully and + /// get all pending packets in order received. + pub fn mark_connected(&mut self, protocol: ProtocolId) -> Vec<(ProtocolId, u8, Vec<u8>)> { + match self.protocol_states.insert(protocol, ProtocolState::Connected) { + None => Vec::new(), + Some(ProtocolState::Connected) => { + debug!(target: "network", "Protocol {:?} marked as connected more than once", protocol); + Vec::new() + } + Some(ProtocolState::Pending(pending)) => + pending.into_iter().map(|(data, id)| (protocol, id, data)).collect(), + } + } + fn read_packet<Message>(&mut self, io: &IoContext<Message>, packet: Packet, host: &HostInfo) -> Result<SessionData, NetworkError> where Message: Send + Sync + Clone { if packet.data.len() < 2 { @@ -409,8 +436,19 @@ impl Session { // map to protocol let protocol = self.info.capabilities[i].protocol; let pid = packet_id - self.info.capabilities[i].id_offset; - trace!(target: "network", "Packet {} mapped to {:?}:{}, i={}, capabilities={:?}", packet_id, protocol, pid, i, self.info.capabilities); - Ok(SessionData::Packet { data: packet.data, protocol: protocol, packet_id: pid } ) + + match *self.protocol_states.entry(protocol).or_insert_with(|| ProtocolState::Pending(Vec::new())) { + ProtocolState::Connected => { + trace!(target: "network", "Packet {} mapped to {:?}:{}, i={}, capabilities={:?}", packet_id, protocol, pid, i, self.info.capabilities); + Ok(SessionData::Packet { data: packet.data, protocol: protocol, packet_id: pid } ) + } + ProtocolState::Pending(ref mut pending) => { + trace!(target: "network", "Packet {} deferred until protocol connection event completion", packet_id); + pending.push((packet.data, packet_id)); + + Ok(SessionData::Continue) + } + } }, _ => { debug!(target: "network", "Unknown packet: {:?}", packet_id); From 5f1fcf95e015921673c50e5250f8ee3cfcac4751 Mon Sep 17 00:00:00 2001 From: Gav Wood <gavin@ethcore.io> Date: Fri, 9 Dec 2016 23:01:43 +0100 Subject: [PATCH 32/74] Make *ID names consistent with std Rust (Id) --- ethcore/light/src/client.rs | 6 +- ethcore/light/src/provider.rs | 14 +-- ethcore/src/account_provider/mod.rs | 2 +- ethcore/src/blockchain/blockchain.rs | 40 ++++----- ethcore/src/client/client.rs | 124 +++++++++++++------------- ethcore/src/client/test_client.rs | 92 +++++++++---------- ethcore/src/client/traits.rs | 84 ++++++++--------- ethcore/src/miner/miner.rs | 8 +- ethcore/src/snapshot/error.rs | 4 +- ethcore/src/snapshot/mod.rs | 4 +- ethcore/src/snapshot/service.rs | 4 +- ethcore/src/snapshot/tests/service.rs | 6 +- ethcore/src/snapshot/watcher.rs | 4 +- ethcore/src/tests/client.rs | 20 ++--- ethcore/src/tests/rpc.rs | 4 +- ethcore/src/trace/db.rs | 18 ++-- ethcore/src/types/filter.rs | 28 +++--- ethcore/src/types/ids.rs | 12 +-- ethcore/src/types/trace_filter.rs | 4 +- ethstore/src/dir/disk.rs | 4 +- ethstore/src/ethstore.rs | 4 +- ethstore/src/json/error.rs | 4 +- ethstore/src/json/id.rs | 48 +++++----- ethstore/src/json/key_file.rs | 14 +-- ethstore/src/json/mod.rs.in | 2 +- ethstore/src/secret_store.rs | 4 +- js/src/jsonrpc/interfaces/parity.js | 4 +- json/src/misc/account_meta.rs | 2 +- parity/blockchain.rs | 10 +-- parity/configuration.rs | 12 +-- parity/dapps.rs | 4 +- parity/helpers.rs | 22 ++--- parity/informant.rs | 4 +- parity/snapshot.rs | 4 +- rpc/src/v1/impls/eth.rs | 28 +++--- rpc/src/v1/impls/eth_filter.rs | 10 +-- rpc/src/v1/impls/traces.rs | 8 +- rpc/src/v1/tests/eth.rs | 4 +- rpc/src/v1/tests/mocked/eth.rs | 4 +- rpc/src/v1/types/block_number.rs | 24 ++--- rpc/src/v1/types/filter.rs | 12 +-- rpc/src/v1/types/trace_filter.rs | 6 +- sync/src/block_sync.rs | 6 +- sync/src/blocks.rs | 10 +-- sync/src/chain.rs | 34 +++---- sync/src/tests/chain.rs | 12 +-- 46 files changed, 389 insertions(+), 389 deletions(-) diff --git a/ethcore/light/src/client.rs b/ethcore/light/src/client.rs index 8a5c43c48..0035406dc 100644 --- a/ethcore/light/src/client.rs +++ b/ethcore/light/src/client.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use ethcore::engines::Engine; -use ethcore::ids::BlockID; +use ethcore::ids::BlockId; use ethcore::service::ClientIoMessage; use ethcore::block_import_error::BlockImportError; use ethcore::block_status::BlockStatus; @@ -51,7 +51,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 } @@ -61,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/provider.rs b/ethcore/light/src/provider.rs index 264df0397..0feee1cab 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -20,7 +20,7 @@ use ethcore::blockchain_info::BlockChainInfo; use ethcore::client::{BlockChainClient, ProvingBlockChainClient}; use ethcore::transaction::SignedTransaction; -use ethcore::ids::BlockID; +use ethcore::ids::BlockId; use util::{Bytes, H256}; @@ -96,7 +96,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T { let best_num = self.chain_info().best_block_number; let start_num = req.block_num; - match self.block_hash(BlockID::Number(req.block_num)) { + match self.block_hash(BlockId::Number(req.block_num)) { Some(hash) if hash == req.block_hash => {} _=> { trace!(target: "les_provider", "unknown/non-canonical start block in header request: {:?}", (req.block_num, req.block_hash)); @@ -108,7 +108,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T { .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))) + .map(|x| self.block_header(BlockId::Number(x))) .take_while(|x| x.is_some()) .flat_map(|x| x) .collect() @@ -116,7 +116,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T { fn block_bodies(&self, req: request::Bodies) -> Vec<Bytes> { req.block_hashes.into_iter() - .map(|hash| self.block_body(BlockID::Hash(hash))) + .map(|hash| self.block_body(BlockId::Hash(hash))) .map(|body| body.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) .collect() } @@ -135,8 +135,8 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T { for request in req.requests { let proof = match request.key2 { - Some(key2) => self.prove_storage(request.key1, key2, request.from_level, BlockID::Hash(request.block)), - None => self.prove_account(request.key1, request.from_level, BlockID::Hash(request.block)), + Some(key2) => self.prove_storage(request.key1, key2, request.from_level, BlockId::Hash(request.block)), + None => self.prove_account(request.key1, request.from_level, BlockId::Hash(request.block)), }; let mut stream = RlpStream::new_list(proof.len()); @@ -153,7 +153,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T { fn contract_code(&self, req: request::ContractCodes) -> Vec<Bytes> { req.code_requests.into_iter() .map(|req| { - self.code_by_hash(req.account_key, BlockID::Hash(req.block_hash)) + self.code_by_hash(req.account_key, BlockId::Hash(req.block_hash)) }) .collect() } diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index a2c83f1ce..59fbda045 100644 --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -213,7 +213,7 @@ impl AccountProvider { Ok(AccountMeta { name: try!(self.sstore.name(&account)), meta: try!(self.sstore.meta(&account)), - uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a UUID + uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid }) } diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 0e8e0a271..c69cf3336 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -146,7 +146,7 @@ pub trait BlockProvider { } #[derive(Debug, Hash, Eq, PartialEq, Clone)] -enum CacheID { +enum CacheId { BlockHeader(H256), BlockBody(H256), BlockDetails(H256), @@ -160,7 +160,7 @@ impl bc::group::BloomGroupDatabase for BlockChain { fn blooms_at(&self, position: &bc::group::GroupPosition) -> Option<bc::group::BloomGroup> { let position = LogGroupPosition::from(position.clone()); let result = self.db.read_with_cache(db::COL_EXTRA, &self.blocks_blooms, &position).map(Into::into); - self.cache_man.lock().note_used(CacheID::BlocksBlooms(position)); + self.cache_man.lock().note_used(CacheId::BlocksBlooms(position)); result } } @@ -193,7 +193,7 @@ pub struct BlockChain { db: Arc<Database>, - cache_man: Mutex<CacheManager<CacheID>>, + cache_man: Mutex<CacheManager<CacheId>>, pending_best_block: RwLock<Option<BestBlock>>, pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>, @@ -270,7 +270,7 @@ impl BlockProvider for BlockChain { None => None }; - self.cache_man.lock().note_used(CacheID::BlockHeader(hash.clone())); + self.cache_man.lock().note_used(CacheId::BlockHeader(hash.clone())); result } @@ -306,7 +306,7 @@ impl BlockProvider for BlockChain { None => None }; - self.cache_man.lock().note_used(CacheID::BlockBody(hash.clone())); + self.cache_man.lock().note_used(CacheId::BlockBody(hash.clone())); result } @@ -314,28 +314,28 @@ impl BlockProvider for BlockChain { /// Get the familial details concerning a block. fn block_details(&self, hash: &H256) -> Option<BlockDetails> { let result = self.db.read_with_cache(db::COL_EXTRA, &self.block_details, hash); - self.cache_man.lock().note_used(CacheID::BlockDetails(hash.clone())); + self.cache_man.lock().note_used(CacheId::BlockDetails(hash.clone())); result } /// Get the hash of given block's number. fn block_hash(&self, index: BlockNumber) -> Option<H256> { let result = self.db.read_with_cache(db::COL_EXTRA, &self.block_hashes, &index); - self.cache_man.lock().note_used(CacheID::BlockHashes(index)); + self.cache_man.lock().note_used(CacheId::BlockHashes(index)); result } /// Get the address of transaction with given hash. fn transaction_address(&self, hash: &H256) -> Option<TransactionAddress> { let result = self.db.read_with_cache(db::COL_EXTRA, &self.transaction_addresses, hash); - self.cache_man.lock().note_used(CacheID::TransactionAddresses(hash.clone())); + self.cache_man.lock().note_used(CacheId::TransactionAddresses(hash.clone())); result } /// Get receipts of block with given hash. fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> { let result = self.db.read_with_cache(db::COL_EXTRA, &self.block_receipts, hash); - self.cache_man.lock().note_used(CacheID::BlockReceipts(hash.clone())); + self.cache_man.lock().note_used(CacheId::BlockReceipts(hash.clone())); result } @@ -809,7 +809,7 @@ impl BlockChain { let mut write_details = self.block_details.write(); batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update, CacheUpdatePolicy::Overwrite); - self.cache_man.lock().note_used(CacheID::BlockDetails(block_hash)); + self.cache_man.lock().note_used(CacheId::BlockDetails(block_hash)); } #[cfg_attr(feature="dev", allow(similar_names))] @@ -968,15 +968,15 @@ impl BlockChain { let mut cache_man = self.cache_man.lock(); for n in pending_hashes_keys { - cache_man.note_used(CacheID::BlockHashes(n)); + cache_man.note_used(CacheId::BlockHashes(n)); } for hash in enacted_txs_keys { - cache_man.note_used(CacheID::TransactionAddresses(hash)); + cache_man.note_used(CacheId::TransactionAddresses(hash)); } for hash in pending_block_hashes { - cache_man.note_used(CacheID::BlockDetails(hash)); + cache_man.note_used(CacheId::BlockDetails(hash)); } } @@ -1244,13 +1244,13 @@ impl BlockChain { cache_man.collect_garbage(current_size, | ids | { for id in &ids { match *id { - CacheID::BlockHeader(ref h) => { block_headers.remove(h); }, - CacheID::BlockBody(ref h) => { block_bodies.remove(h); }, - CacheID::BlockDetails(ref h) => { block_details.remove(h); } - CacheID::BlockHashes(ref h) => { block_hashes.remove(h); } - CacheID::TransactionAddresses(ref h) => { transaction_addresses.remove(h); } - CacheID::BlocksBlooms(ref h) => { blocks_blooms.remove(h); } - CacheID::BlockReceipts(ref h) => { block_receipts.remove(h); } + CacheId::BlockHeader(ref h) => { block_headers.remove(h); }, + CacheId::BlockBody(ref h) => { block_bodies.remove(h); }, + CacheId::BlockDetails(ref h) => { block_details.remove(h); } + CacheId::BlockHashes(ref h) => { block_hashes.remove(h); } + CacheId::TransactionAddresses(ref h) => { transaction_addresses.remove(h); } + CacheId::BlocksBlooms(ref h) => { blocks_blooms.remove(h); } + CacheId::BlockReceipts(ref h) => { block_receipts.remove(h); } } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 30b5b4653..acd4984ab 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -50,7 +50,7 @@ use log_entry::LocalizedLogEntry; use verification::queue::BlockQueue; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; use client::{ - BlockID, TransactionID, UncleID, TraceId, ClientConfig, BlockChainClient, + BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, ChainNotify, PruningInfo, ProvingBlockChainClient, }; @@ -580,13 +580,13 @@ impl Client { /// Attempt to get a copy of a specific block's final state. /// - /// This will not fail if given BlockID::Latest. + /// This will not fail if given BlockId::Latest. /// Otherwise, this can fail (but may not) if the DB prunes state. - pub fn state_at(&self, id: BlockID) -> Option<State> { + pub fn state_at(&self, id: BlockId) -> Option<State> { // fast path for latest state. match id.clone() { - BlockID::Pending => return self.miner.pending_state().or_else(|| Some(self.state())), - BlockID::Latest => return Some(self.state()), + BlockId::Pending => return self.miner.pending_state().or_else(|| Some(self.state())), + BlockId::Latest => return Some(self.state()), _ => {}, } @@ -611,15 +611,15 @@ impl Client { /// Attempt to get a copy of a specific block's beginning state. /// - /// This will not fail if given BlockID::Latest. + /// This will not fail if given BlockId::Latest. /// Otherwise, this can fail (but may not) if the DB prunes state. - pub fn state_at_beginning(&self, id: BlockID) -> Option<State> { + pub fn state_at_beginning(&self, id: BlockId) -> Option<State> { // fast path for latest state. match id { - BlockID::Pending => self.state_at(BlockID::Latest), + BlockId::Pending => self.state_at(BlockId::Latest), id => match self.block_number(id) { None | Some(0) => None, - Some(n) => self.state_at(BlockID::Number(n - 1)), + Some(n) => self.state_at(BlockId::Number(n - 1)), } } } @@ -689,18 +689,18 @@ impl Client { } /// Look up the block number for the given block ID. - pub fn block_number(&self, id: BlockID) -> Option<BlockNumber> { + pub fn block_number(&self, id: BlockId) -> Option<BlockNumber> { match id { - BlockID::Number(number) => Some(number), - BlockID::Hash(ref hash) => self.chain.read().block_number(hash), - BlockID::Earliest => Some(0), - BlockID::Latest | BlockID::Pending => Some(self.chain.read().best_block_number()), + BlockId::Number(number) => Some(number), + BlockId::Hash(ref hash) => self.chain.read().block_number(hash), + BlockId::Earliest => Some(0), + BlockId::Latest | BlockId::Pending => Some(self.chain.read().best_block_number()), } } /// Take a snapshot at the given block. /// If the ID given is "latest", this will default to 1000 blocks behind. - pub fn take_snapshot<W: snapshot_io::SnapshotWriter + Send>(&self, writer: W, at: BlockID, p: &snapshot::Progress) -> Result<(), EthcoreError> { + pub fn take_snapshot<W: snapshot_io::SnapshotWriter + Send>(&self, writer: W, at: BlockId, p: &snapshot::Progress) -> Result<(), EthcoreError> { let db = self.state_db.lock().journal_db().boxed_clone(); let best_block_number = self.chain_info().best_block_number; let block_number = try!(self.block_number(at).ok_or(snapshot::Error::InvalidStartingBlock(at))); @@ -712,13 +712,13 @@ impl Client { let history = ::std::cmp::min(self.history, 1000); let start_hash = match at { - BlockID::Latest => { + BlockId::Latest => { let start_num = match db.earliest_era() { Some(era) => ::std::cmp::max(era, best_block_number - history), None => best_block_number - history, }; - match self.block_hash(BlockID::Number(start_num)) { + match self.block_hash(BlockId::Number(start_num)) { Some(h) => h, None => return Err(snapshot::Error::InvalidStartingBlock(at).into()), } @@ -739,19 +739,19 @@ impl Client { self.history } - fn block_hash(chain: &BlockChain, id: BlockID) -> Option<H256> { + fn block_hash(chain: &BlockChain, id: BlockId) -> Option<H256> { match id { - BlockID::Hash(hash) => Some(hash), - BlockID::Number(number) => chain.block_hash(number), - BlockID::Earliest => chain.block_hash(0), - BlockID::Latest | BlockID::Pending => Some(chain.best_block_hash()), + BlockId::Hash(hash) => Some(hash), + BlockId::Number(number) => chain.block_hash(number), + BlockId::Earliest => chain.block_hash(0), + BlockId::Latest | BlockId::Pending => Some(chain.best_block_hash()), } } - fn transaction_address(&self, id: TransactionID) -> Option<TransactionAddress> { + fn transaction_address(&self, id: TransactionId) -> Option<TransactionAddress> { match id { - TransactionID::Hash(ref hash) => self.chain.read().transaction_address(hash), - TransactionID::Location(id, index) => Self::block_hash(&self.chain.read(), id).map(|hash| TransactionAddress { + TransactionId::Hash(ref hash) => self.chain.read().transaction_address(hash), + TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), id).map(|hash| TransactionAddress { block_hash: hash, index: index, }) @@ -805,7 +805,7 @@ impl snapshot::DatabaseRestore for Client { impl BlockChainClient for Client { - fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError> { + fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError> { let header = try!(self.block_header(block).ok_or(CallError::StatePruned)); let view = HeaderView::new(&header); let last_hashes = self.build_last_hashes(view.parent_hash()); @@ -841,11 +841,11 @@ impl BlockChainClient for Client { Ok(ret) } - fn replay(&self, id: TransactionID, analytics: CallAnalytics) -> Result<Executed, CallError> { + fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError> { let address = try!(self.transaction_address(id).ok_or(CallError::TransactionNotFound)); - let header_data = try!(self.block_header(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned)); - let body_data = try!(self.block_body(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned)); - let mut state = try!(self.state_at_beginning(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned)); + let header_data = try!(self.block_header(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)); + let body_data = try!(self.block_body(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)); + let mut state = try!(self.state_at_beginning(BlockId::Hash(address.block_hash)).ok_or(CallError::StatePruned)); let txs = BodyView::new(&body_data).transactions(); if address.index >= txs.len() { @@ -918,18 +918,18 @@ impl BlockChainClient for Client { self.chain.read().best_block_header() } - fn block_header(&self, id: BlockID) -> Option<Bytes> { + fn block_header(&self, id: BlockId) -> Option<Bytes> { let chain = self.chain.read(); Self::block_hash(&chain, id).and_then(|hash| chain.block_header_data(&hash)) } - fn block_body(&self, id: BlockID) -> Option<Bytes> { + fn block_body(&self, id: BlockId) -> Option<Bytes> { let chain = self.chain.read(); Self::block_hash(&chain, id).and_then(|hash| chain.block_body(&hash)) } - fn block(&self, id: BlockID) -> Option<Bytes> { - if let BlockID::Pending = id { + fn block(&self, id: BlockId) -> Option<Bytes> { + if let BlockId::Pending = id { if let Some(block) = self.miner.pending_block() { return Some(block.rlp_bytes(Seal::Without)); } @@ -940,7 +940,7 @@ impl BlockChainClient for Client { }) } - fn block_status(&self, id: BlockID) -> BlockStatus { + fn block_status(&self, id: BlockId) -> BlockStatus { let chain = self.chain.read(); match Self::block_hash(&chain, id) { Some(ref hash) if chain.is_known(hash) => BlockStatus::InChain, @@ -949,42 +949,42 @@ impl BlockChainClient for Client { } } - fn block_total_difficulty(&self, id: BlockID) -> Option<U256> { - if let BlockID::Pending = id { + fn block_total_difficulty(&self, id: BlockId) -> Option<U256> { + if let BlockId::Pending = id { if let Some(block) = self.miner.pending_block() { - return Some(*block.header.difficulty() + self.block_total_difficulty(BlockID::Latest).expect("blocks in chain have details; qed")); + return Some(*block.header.difficulty() + self.block_total_difficulty(BlockId::Latest).expect("blocks in chain have details; qed")); } } let chain = self.chain.read(); Self::block_hash(&chain, id).and_then(|hash| chain.block_details(&hash)).map(|d| d.total_difficulty) } - fn nonce(&self, address: &Address, id: BlockID) -> Option<U256> { + fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> { self.state_at(id).map(|s| s.nonce(address)) } - fn storage_root(&self, address: &Address, id: BlockID) -> Option<H256> { + fn storage_root(&self, address: &Address, id: BlockId) -> Option<H256> { self.state_at(id).and_then(|s| s.storage_root(address)) } - fn block_hash(&self, id: BlockID) -> Option<H256> { + fn block_hash(&self, id: BlockId) -> Option<H256> { let chain = self.chain.read(); Self::block_hash(&chain, id) } - fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> { + fn code(&self, address: &Address, id: BlockId) -> Option<Option<Bytes>> { self.state_at(id).map(|s| s.code(address).map(|c| (*c).clone())) } - fn balance(&self, address: &Address, id: BlockID) -> Option<U256> { + fn balance(&self, address: &Address, id: BlockId) -> Option<U256> { self.state_at(id).map(|s| s.balance(address)) } - fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256> { + fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option<H256> { self.state_at(id).map(|s| s.storage_at(address, position)) } - fn list_accounts(&self, id: BlockID, after: Option<&Address>, count: u64) -> Option<Vec<Address>> { + fn list_accounts(&self, id: BlockId, after: Option<&Address>, count: u64) -> Option<Vec<Address>> { if !self.factories.trie.is_fat() { trace!(target: "fatdb", "list_accounts: Not a fat DB"); return None; @@ -1022,7 +1022,7 @@ impl BlockChainClient for Client { Some(accounts) } - fn list_storage(&self, id: BlockID, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>> { + fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>> { if !self.factories.trie.is_fat() { trace!(target: "fatdb", "list_stroage: Not a fat DB"); return None; @@ -1066,20 +1066,20 @@ impl BlockChainClient for Client { Some(keys) } - fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction> { + fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction> { self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address)) } - fn transaction_block(&self, id: TransactionID) -> Option<H256> { + fn transaction_block(&self, id: TransactionId) -> Option<H256> { self.transaction_address(id).map(|addr| addr.block_hash) } - fn uncle(&self, id: UncleID) -> Option<Bytes> { + fn uncle(&self, id: UncleId) -> Option<Bytes> { let index = id.position; self.block_body(id.block).and_then(|body| BodyView::new(&body).uncle_rlp_at(index)) } - fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> { + fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> { let chain = self.chain.read(); self.transaction_address(id) .and_then(|address| chain.block_number(&address.block_hash).and_then(|block_number| { @@ -1163,7 +1163,7 @@ impl BlockChainClient for Client { if self.chain.read().is_known(&unverified.hash()) { return Err(BlockImportError::Import(ImportError::AlreadyInChain)); } - if self.block_status(BlockID::Hash(unverified.parent_hash())) == BlockStatus::Unknown { + if self.block_status(BlockId::Hash(unverified.parent_hash())) == BlockStatus::Unknown { return Err(BlockImportError::Block(BlockError::UnknownParent(unverified.parent_hash()))); } } @@ -1177,7 +1177,7 @@ impl BlockChainClient for Client { if self.chain.read().is_known(&header.hash()) { return Err(BlockImportError::Import(ImportError::AlreadyInChain)); } - if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown { + if self.block_status(BlockId::Hash(header.parent_hash())) == BlockStatus::Unknown { return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash()))); } } @@ -1200,7 +1200,7 @@ impl BlockChainClient for Client { self.engine.additional_params().into_iter().collect() } - fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>> { + fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>> { match (self.block_number(from_block), self.block_number(to_block)) { (Some(from), Some(to)) => Some(self.chain.read().blocks_with_bloom(bloom, from, to)), _ => None @@ -1242,20 +1242,20 @@ impl BlockChainClient for Client { let trace_address = trace.address; self.transaction_address(trace.transaction) .and_then(|tx_address| { - self.block_number(BlockID::Hash(tx_address.block_hash)) + self.block_number(BlockId::Hash(tx_address.block_hash)) .and_then(|number| self.tracedb.read().trace(number, tx_address.index, trace_address)) }) } - fn transaction_traces(&self, transaction: TransactionID) -> Option<Vec<LocalizedTrace>> { + fn transaction_traces(&self, transaction: TransactionId) -> Option<Vec<LocalizedTrace>> { self.transaction_address(transaction) .and_then(|tx_address| { - self.block_number(BlockID::Hash(tx_address.block_hash)) + self.block_number(BlockId::Hash(tx_address.block_hash)) .and_then(|number| self.tracedb.read().transaction_traces(number, tx_address.index)) }) } - fn block_traces(&self, block: BlockID) -> Option<Vec<LocalizedTrace>> { + fn block_traces(&self, block: BlockId) -> Option<Vec<LocalizedTrace>> { self.block_number(block) .and_then(|number| self.tracedb.read().block_traces(number)) } @@ -1290,13 +1290,13 @@ impl BlockChainClient for Client { self.engine.signing_network_id(&self.latest_env_info()) } - fn block_extra_info(&self, id: BlockID) -> Option<BTreeMap<String, String>> { + fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> { self.block_header(id) .map(|block| decode(&block)) .map(|header| self.engine.extra_info(&header)) } - fn uncle_extra_info(&self, id: UncleID) -> Option<BTreeMap<String, String>> { + fn uncle_extra_info(&self, id: UncleId) -> Option<BTreeMap<String, String>> { self.uncle(id) .map(|header| self.engine.extra_info(&decode(&header))) } @@ -1392,19 +1392,19 @@ impl MayPanic for Client { } impl ProvingBlockChainClient for Client { - fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockID) -> Vec<Bytes> { + fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockId) -> Vec<Bytes> { self.state_at(id) .and_then(move |state| state.prove_storage(key1, key2, from_level).ok()) .unwrap_or_else(Vec::new) } - fn prove_account(&self, key1: H256, from_level: u32, id: BlockID) -> Vec<Bytes> { + fn prove_account(&self, key1: H256, from_level: u32, id: BlockId) -> Vec<Bytes> { self.state_at(id) .and_then(move |state| state.prove_account(key1, from_level).ok()) .unwrap_or_else(Vec::new) } - fn code_by_hash(&self, account_key: H256, id: BlockID) -> Bytes { + fn code_by_hash(&self, account_key: H256, id: BlockId) -> Bytes { self.state_at(id) .and_then(move |state| state.code_by_address_hash(account_key).ok()) .and_then(|x| x) diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 433a5166f..e61fe729c 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -24,8 +24,8 @@ use devtools::*; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use blockchain::TreeRoute; use client::{ - BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, - TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError, + BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId, + TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError, }; use db::{NUM_COLUMNS, COL_STATE}; use header::{Header as BlockHeader, BlockNumber}; @@ -73,7 +73,7 @@ pub struct TestBlockChainClient { /// Execution result. pub execution_result: RwLock<Option<Result<Executed, CallError>>>, /// Transaction receipts. - pub receipts: RwLock<HashMap<TransactionID, LocalizedReceipt>>, + pub receipts: RwLock<HashMap<TransactionId, LocalizedReceipt>>, /// Logs pub logs: RwLock<Vec<LocalizedLogEntry>>, /// Block queue size. @@ -158,7 +158,7 @@ impl TestBlockChainClient { } /// Set the transaction receipt result - pub fn set_transaction_receipt(&self, id: TransactionID, receipt: LocalizedReceipt) { + pub fn set_transaction_receipt(&self, id: TransactionId, receipt: LocalizedReceipt) { self.receipts.write().insert(id, receipt); } @@ -256,8 +256,8 @@ impl TestBlockChainClient { /// Make a bad block by setting invalid extra data. pub fn corrupt_block(&self, n: BlockNumber) { - let hash = self.block_hash(BlockID::Number(n)).unwrap(); - let mut header: BlockHeader = decode(&self.block_header(BlockID::Number(n)).unwrap()); + let hash = self.block_hash(BlockId::Number(n)).unwrap(); + let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap()); header.set_extra_data(b"This extra data is way too long to be considered valid".to_vec()); let mut rlp = RlpStream::new_list(3); rlp.append(&header); @@ -268,8 +268,8 @@ impl TestBlockChainClient { /// Make a bad block by setting invalid parent hash. pub fn corrupt_block_parent(&self, n: BlockNumber) { - let hash = self.block_hash(BlockID::Number(n)).unwrap(); - let mut header: BlockHeader = decode(&self.block_header(BlockID::Number(n)).unwrap()); + let hash = self.block_hash(BlockId::Number(n)).unwrap(); + let mut header: BlockHeader = decode(&self.block_header(BlockId::Number(n)).unwrap()); header.set_parent_hash(H256::from(42)); let mut rlp = RlpStream::new_list(3); rlp.append(&header); @@ -285,12 +285,12 @@ impl TestBlockChainClient { blocks_read[&index].clone() } - fn block_hash(&self, id: BlockID) -> Option<H256> { + fn block_hash(&self, id: BlockId) -> Option<H256> { match id { - BlockID::Hash(hash) => Some(hash), - BlockID::Number(n) => self.numbers.read().get(&(n as usize)).cloned(), - BlockID::Earliest => self.numbers.read().get(&0).cloned(), - BlockID::Latest | BlockID::Pending => self.numbers.read().get(&(self.numbers.read().len() - 1)).cloned() + BlockId::Hash(hash) => Some(hash), + BlockId::Number(n) => self.numbers.read().get(&(n as usize)).cloned(), + BlockId::Earliest => self.numbers.read().get(&0).cloned(), + BlockId::Latest | BlockId::Pending => self.numbers.read().get(&(self.numbers.read().len() - 1)).cloned() } } @@ -363,46 +363,46 @@ impl MiningBlockChainClient for TestBlockChainClient { } impl BlockChainClient for TestBlockChainClient { - fn call(&self, _t: &SignedTransaction, _block: BlockID, _analytics: CallAnalytics) -> Result<Executed, CallError> { + fn call(&self, _t: &SignedTransaction, _block: BlockId, _analytics: CallAnalytics) -> Result<Executed, CallError> { self.execution_result.read().clone().unwrap() } - fn replay(&self, _id: TransactionID, _analytics: CallAnalytics) -> Result<Executed, CallError> { + fn replay(&self, _id: TransactionId, _analytics: CallAnalytics) -> Result<Executed, CallError> { self.execution_result.read().clone().unwrap() } - fn block_total_difficulty(&self, _id: BlockID) -> Option<U256> { + fn block_total_difficulty(&self, _id: BlockId) -> Option<U256> { Some(U256::zero()) } - fn block_hash(&self, id: BlockID) -> Option<H256> { + fn block_hash(&self, id: BlockId) -> Option<H256> { Self::block_hash(self, id) } - fn nonce(&self, address: &Address, id: BlockID) -> Option<U256> { + fn nonce(&self, address: &Address, id: BlockId) -> Option<U256> { match id { - BlockID::Latest => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params.account_start_nonce)), + BlockId::Latest => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params.account_start_nonce)), _ => None, } } - fn storage_root(&self, _address: &Address, _id: BlockID) -> Option<H256> { + fn storage_root(&self, _address: &Address, _id: BlockId) -> Option<H256> { None } fn latest_nonce(&self, address: &Address) -> U256 { - self.nonce(address, BlockID::Latest).unwrap() + self.nonce(address, BlockId::Latest).unwrap() } - fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>> { + fn code(&self, address: &Address, id: BlockId) -> Option<Option<Bytes>> { match id { - BlockID::Latest => Some(self.code.read().get(address).cloned()), + BlockId::Latest => Some(self.code.read().get(address).cloned()), _ => None, } } - fn balance(&self, address: &Address, id: BlockID) -> Option<U256> { - if let BlockID::Latest = id { + fn balance(&self, address: &Address, id: BlockId) -> Option<U256> { + if let BlockId::Latest = id { Some(self.balances.read().get(address).cloned().unwrap_or_else(U256::zero)) } else { None @@ -410,45 +410,45 @@ impl BlockChainClient for TestBlockChainClient { } fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, BlockID::Latest).unwrap() + self.balance(address, BlockId::Latest).unwrap() } - fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256> { - if let BlockID::Latest = id { + fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option<H256> { + if let BlockId::Latest = id { Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new)) } else { None } } - fn list_accounts(&self, _id: BlockID, _after: Option<&Address>, _count: u64) -> Option<Vec<Address>> { + fn list_accounts(&self, _id: BlockId, _after: Option<&Address>, _count: u64) -> Option<Vec<Address>> { None } - fn list_storage(&self, _id: BlockID, _account: &Address, _after: Option<&H256>, _count: u64) -> Option<Vec<H256>> { + fn list_storage(&self, _id: BlockId, _account: &Address, _after: Option<&H256>, _count: u64) -> Option<Vec<H256>> { None } - fn transaction(&self, _id: TransactionID) -> Option<LocalizedTransaction> { + fn transaction(&self, _id: TransactionId) -> Option<LocalizedTransaction> { None // Simple default. } - fn transaction_block(&self, _id: TransactionID) -> Option<H256> { + fn transaction_block(&self, _id: TransactionId) -> Option<H256> { None // Simple default. } - fn uncle(&self, _id: UncleID) -> Option<Bytes> { + fn uncle(&self, _id: UncleId) -> Option<Bytes> { None // Simple default. } - fn uncle_extra_info(&self, _id: UncleID) -> Option<BTreeMap<String, String>> { + fn uncle_extra_info(&self, _id: UncleId) -> Option<BTreeMap<String, String>> { None } - fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt> { + fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt> { self.receipts.read().get(&id).cloned() } - fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockID, _to_block: BlockID) -> Option<Vec<BlockNumber>> { + fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockId, _to_block: BlockId) -> Option<Vec<BlockNumber>> { unimplemented!(); } @@ -466,14 +466,14 @@ impl BlockChainClient for TestBlockChainClient { } fn best_block_header(&self) -> Bytes { - self.block_header(BlockID::Hash(self.chain_info().best_block_hash)).expect("Best block always have header.") + self.block_header(BlockId::Hash(self.chain_info().best_block_hash)).expect("Best block always have header.") } - fn block_header(&self, id: BlockID) -> Option<Bytes> { + fn block_header(&self, id: BlockId) -> Option<Bytes> { self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| Rlp::new(r).at(0).as_raw().to_vec())) } - fn block_body(&self, id: BlockID) -> Option<Bytes> { + fn block_body(&self, id: BlockId) -> Option<Bytes> { self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).map(|r| { let mut stream = RlpStream::new_list(2); stream.append_raw(Rlp::new(r).at(1).as_raw(), 1); @@ -482,21 +482,21 @@ impl BlockChainClient for TestBlockChainClient { })) } - fn block(&self, id: BlockID) -> Option<Bytes> { + fn block(&self, id: BlockId) -> Option<Bytes> { self.block_hash(id).and_then(|hash| self.blocks.read().get(&hash).cloned()) } - fn block_extra_info(&self, id: BlockID) -> Option<BTreeMap<String, String>> { + fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>> { self.block(id) .map(|block| BlockView::new(&block).header()) .map(|header| self.spec.engine.extra_info(&header)) } - fn block_status(&self, id: BlockID) -> BlockStatus { + fn block_status(&self, id: BlockId) -> BlockStatus { match id { - BlockID::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain, - BlockID::Hash(ref hash) if self.blocks.read().get(hash).is_some() => BlockStatus::InChain, + BlockId::Number(number) if (number as usize) < self.blocks.read().len() => BlockStatus::InChain, + BlockId::Hash(ref hash) if self.blocks.read().get(hash).is_some() => BlockStatus::InChain, _ => BlockStatus::Unknown } } @@ -649,11 +649,11 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } - fn transaction_traces(&self, _trace: TransactionID) -> Option<Vec<LocalizedTrace>> { + fn transaction_traces(&self, _trace: TransactionId) -> Option<Vec<LocalizedTrace>> { unimplemented!(); } - fn block_traces(&self, _trace: BlockID) -> Option<Vec<LocalizedTrace>> { + fn block_traces(&self, _trace: BlockId) -> Option<Vec<LocalizedTrace>> { unimplemented!(); } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index e23a564d4..ad21d2ba5 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -50,93 +50,93 @@ pub trait BlockChainClient : Sync + Send { fn keep_alive(&self) {} /// Get raw block header data by block id. - fn block_header(&self, id: BlockID) -> Option<Bytes>; + fn block_header(&self, id: BlockId) -> Option<Bytes>; /// Get raw block body data by block id. /// Block body is an RLP list of two items: uncles and transactions. - fn block_body(&self, id: BlockID) -> Option<Bytes>; + fn block_body(&self, id: BlockId) -> Option<Bytes>; /// Get raw block data by block header hash. - fn block(&self, id: BlockID) -> Option<Bytes>; + fn block(&self, id: BlockId) -> Option<Bytes>; /// Get block status by block header hash. - fn block_status(&self, id: BlockID) -> BlockStatus; + fn block_status(&self, id: BlockId) -> BlockStatus; /// Get block total difficulty. - fn block_total_difficulty(&self, id: BlockID) -> Option<U256>; + fn block_total_difficulty(&self, id: BlockId) -> Option<U256>; /// Attempt to get address nonce at given block. - /// May not fail on BlockID::Latest. - fn nonce(&self, address: &Address, id: BlockID) -> Option<U256>; + /// May not fail on BlockId::Latest. + fn nonce(&self, address: &Address, id: BlockId) -> Option<U256>; /// Attempt to get address storage root at given block. - /// May not fail on BlockID::Latest. - fn storage_root(&self, address: &Address, id: BlockID) -> Option<H256>; + /// May not fail on BlockId::Latest. + fn storage_root(&self, address: &Address, id: BlockId) -> Option<H256>; /// Get address nonce at the latest block's state. fn latest_nonce(&self, address: &Address) -> U256 { - self.nonce(address, BlockID::Latest) - .expect("nonce will return Some when given BlockID::Latest. nonce was given BlockID::Latest. \ + self.nonce(address, BlockId::Latest) + .expect("nonce will return Some when given BlockId::Latest. nonce was given BlockId::Latest. \ Therefore nonce has returned Some; qed") } /// Get block hash. - fn block_hash(&self, id: BlockID) -> Option<H256>; + fn block_hash(&self, id: BlockId) -> Option<H256>; /// Get address code at given block's state. - fn code(&self, address: &Address, id: BlockID) -> Option<Option<Bytes>>; + fn code(&self, address: &Address, id: BlockId) -> Option<Option<Bytes>>; /// Get address code at the latest block's state. fn latest_code(&self, address: &Address) -> Option<Bytes> { - self.code(address, BlockID::Latest) - .expect("code will return Some if given BlockID::Latest; qed") + self.code(address, BlockId::Latest) + .expect("code will return Some if given BlockId::Latest; qed") } /// Get address balance at the given block's state. /// - /// May not return None if given BlockID::Latest. + /// May not return None if given BlockId::Latest. /// Returns None if and only if the block's root hash has been pruned from the DB. - fn balance(&self, address: &Address, id: BlockID) -> Option<U256>; + fn balance(&self, address: &Address, id: BlockId) -> Option<U256>; /// Get address balance at the latest block's state. fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, BlockID::Latest) - .expect("balance will return Some if given BlockID::Latest. balance was given BlockID::Latest \ + self.balance(address, BlockId::Latest) + .expect("balance will return Some if given BlockId::Latest. balance was given BlockId::Latest \ Therefore balance has returned Some; qed") } /// Get value of the storage at given position at the given block's state. /// - /// May not return None if given BlockID::Latest. + /// May not return None if given BlockId::Latest. /// Returns None if and only if the block's root hash has been pruned from the DB. - fn storage_at(&self, address: &Address, position: &H256, id: BlockID) -> Option<H256>; + fn storage_at(&self, address: &Address, position: &H256, id: BlockId) -> Option<H256>; /// Get value of the storage at given position at the latest block's state. fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 { - self.storage_at(address, position, BlockID::Latest) - .expect("storage_at will return Some if given BlockID::Latest. storage_at was given BlockID::Latest. \ + self.storage_at(address, position, BlockId::Latest) + .expect("storage_at will return Some if given BlockId::Latest. storage_at was given BlockId::Latest. \ Therefore storage_at has returned Some; qed") } /// Get a list of all accounts in the block `id`, if fat DB is in operation, otherwise `None`. /// If `after` is set the list starts with the following item. - fn list_accounts(&self, id: BlockID, after: Option<&Address>, count: u64) -> Option<Vec<Address>>; + fn list_accounts(&self, id: BlockId, after: Option<&Address>, count: u64) -> Option<Vec<Address>>; /// Get a list of all storage keys in the block `id`, if fat DB is in operation, otherwise `None`. /// If `after` is set the list starts with the following item. - fn list_storage(&self, id: BlockID, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>>; + fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option<Vec<H256>>; /// Get transaction with given hash. - fn transaction(&self, id: TransactionID) -> Option<LocalizedTransaction>; + fn transaction(&self, id: TransactionId) -> Option<LocalizedTransaction>; /// Get the hash of block that contains the transaction, if any. - fn transaction_block(&self, id: TransactionID) -> Option<H256>; + fn transaction_block(&self, id: TransactionId) -> Option<H256>; /// Get uncle with given id. - fn uncle(&self, id: UncleID) -> Option<Bytes>; + fn uncle(&self, id: UncleId) -> Option<Bytes>; /// Get transaction receipt with given hash. - fn transaction_receipt(&self, id: TransactionID) -> Option<LocalizedReceipt>; + fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>; /// Get a tree route between `from` and `to`. /// See `BlockChain::tree_route`. @@ -173,16 +173,16 @@ pub trait BlockChainClient : Sync + Send { fn best_block_header(&self) -> Bytes; /// Returns numbers of blocks containing given bloom. - fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockID, to_block: BlockID) -> Option<Vec<BlockNumber>>; + fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>>; /// Returns logs matching given filter. fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>; /// Makes a non-persistent transaction call. - fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result<Executed, CallError>; + fn call(&self, t: &SignedTransaction, block: BlockId, analytics: CallAnalytics) -> Result<Executed, CallError>; /// Replays a given transaction for inspection. - fn replay(&self, t: TransactionID, analytics: CallAnalytics) -> Result<Executed, CallError>; + fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result<Executed, CallError>; /// Returns traces matching given filter. fn filter_traces(&self, filter: TraceFilter) -> Option<Vec<LocalizedTrace>>; @@ -191,10 +191,10 @@ pub trait BlockChainClient : Sync + Send { fn trace(&self, trace: TraceId) -> Option<LocalizedTrace>; /// Returns traces created by transaction. - fn transaction_traces(&self, trace: TransactionID) -> Option<Vec<LocalizedTrace>>; + fn transaction_traces(&self, trace: TransactionId) -> Option<Vec<LocalizedTrace>>; /// Returns traces created by transaction from block. - fn block_traces(&self, trace: BlockID) -> Option<Vec<LocalizedTrace>>; + fn block_traces(&self, trace: BlockId) -> Option<Vec<LocalizedTrace>>; /// Get last hashes starting from best block. fn last_hashes(&self) -> LastHashes; @@ -211,7 +211,7 @@ pub trait BlockChainClient : Sync + Send { let mut corpus = Vec::new(); while corpus.is_empty() { for _ in 0..sample_size { - let block_bytes = self.block(BlockID::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed"); + let block_bytes = self.block(BlockId::Hash(h)).expect("h is either the best_block_hash or an ancestor; qed"); let block = BlockView::new(&block_bytes); let header = block.header_view(); if header.number() == 0 { @@ -249,11 +249,11 @@ pub trait BlockChainClient : Sync + Send { /// Set the mode. fn set_mode(&self, mode: Mode); - /// Returns engine-related extra info for `BlockID`. - fn block_extra_info(&self, id: BlockID) -> Option<BTreeMap<String, String>>; + /// Returns engine-related extra info for `BlockId`. + fn block_extra_info(&self, id: BlockId) -> Option<BTreeMap<String, String>>; - /// Returns engine-related extra info for `UncleID`. - fn uncle_extra_info(&self, id: UncleID) -> Option<BTreeMap<String, String>>; + /// Returns engine-related extra info for `UncleId`. + fn uncle_extra_info(&self, id: UncleId) -> Option<BTreeMap<String, String>>; /// Returns information about pruning/data availability. fn pruning_info(&self) -> PruningInfo; @@ -288,15 +288,15 @@ pub trait ProvingBlockChainClient: BlockChainClient { /// Returns a vector of raw trie nodes (in order from the root) proving the storage query. /// Nodes after `from_level` may be omitted. /// An empty vector indicates unservable query. - fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockID) -> Vec<Bytes>; + fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockId) -> Vec<Bytes>; /// Prove account existence at a specific block id. /// The key is the keccak hash of the account's address. /// Returns a vector of raw trie nodes (in order from the root) proving the query. /// Nodes after `from_level` may be omitted. /// An empty vector indicates unservable query. - fn prove_account(&self, key1: H256, from_level: u32, id: BlockID) -> Vec<Bytes>; + fn prove_account(&self, key1: H256, from_level: u32, id: BlockId) -> Vec<Bytes>; /// Get code by address hash. - fn code_by_hash(&self, account_key: H256, id: BlockID) -> Bytes; + fn code_by_hash(&self, account_key: H256, id: BlockId) -> Bytes; } \ No newline at end of file diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index b171856b9..ad541391d 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -23,7 +23,7 @@ use account_provider::{AccountProvider, Error as AccountError}; use views::{BlockView, HeaderView}; use header::Header; use state::{State, CleanupMode}; -use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockID, CallAnalytics, TransactionID}; +use client::{MiningBlockChainClient, Executive, Executed, EnvInfo, TransactOptions, BlockId, CallAnalytics, TransactionId}; use client::TransactionImportResult; use executive::contract_address; use block::{ClosedBlock, SealedBlock, IsBlock, Block}; @@ -585,7 +585,7 @@ impl Miner { let best_block_header: Header = ::rlp::decode(&chain.best_block_header()); transactions.into_iter() .map(|tx| { - if chain.transaction_block(TransactionID::Hash(tx.hash())).is_some() { + if chain.transaction_block(TransactionId::Hash(tx.hash())).is_some() { debug!(target: "miner", "Rejected tx {:?}: already in the blockchain", tx.hash()); return Err(Error::Transaction(TransactionError::AlreadyImported)); } @@ -701,7 +701,7 @@ impl MinerService for Miner { Ok(ret) }, None => { - chain.call(t, BlockID::Latest, analytics) + chain.call(t, BlockId::Latest, analytics) } } } @@ -1094,7 +1094,7 @@ impl MinerService for Miner { fn fetch_transactions(chain: &MiningBlockChainClient, hash: &H256) -> Vec<SignedTransaction> { let block = chain - .block(BlockID::Hash(*hash)) + .block(BlockId::Hash(*hash)) // Client should send message after commit to db and inserting to chain. .expect("Expected in-chain blocks."); let block = BlockView::new(&block); diff --git a/ethcore/src/snapshot/error.rs b/ethcore/src/snapshot/error.rs index d417695f0..cc84d8e48 100644 --- a/ethcore/src/snapshot/error.rs +++ b/ethcore/src/snapshot/error.rs @@ -18,7 +18,7 @@ use std::fmt; -use ids::BlockID; +use ids::BlockId; use util::H256; use util::trie::TrieError; @@ -28,7 +28,7 @@ use rlp::DecoderError; #[derive(Debug)] pub enum Error { /// Invalid starting block for snapshot. - InvalidStartingBlock(BlockID), + InvalidStartingBlock(BlockId), /// Block not found. BlockNotFound(H256), /// Incomplete chain. diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 408941309..9b45f2687 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -27,7 +27,7 @@ use account_db::{AccountDB, AccountDBMut}; use blockchain::{BlockChain, BlockProvider}; use engines::Engine; use header::Header; -use ids::BlockID; +use ids::BlockId; use views::BlockView; use util::{Bytes, Hashable, HashDB, DBValue, snappy, U256, Uint}; @@ -129,7 +129,7 @@ pub fn take_snapshot<W: SnapshotWriter + Send>( p: &Progress ) -> Result<(), Error> { let start_header = try!(chain.block_header(&block_at) - .ok_or(Error::InvalidStartingBlock(BlockID::Hash(block_at)))); + .ok_or(Error::InvalidStartingBlock(BlockId::Hash(block_at)))); let state_root = start_header.state_root(); let number = start_header.number(); diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index 89ee68de0..05c4c1f9f 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -30,7 +30,7 @@ use blockchain::BlockChain; use client::{BlockChainClient, Client}; use engines::Engine; use error::Error; -use ids::BlockID; +use ids::BlockId; use service::ClientIoMessage; use io::IoChannel; @@ -354,7 +354,7 @@ impl Service { let writer = try!(LooseWriter::new(temp_dir.clone())); let guard = Guard::new(temp_dir.clone()); - let res = client.take_snapshot(writer, BlockID::Number(num), &self.progress); + let res = client.take_snapshot(writer, BlockId::Number(num), &self.progress); self.taking_snapshot.store(false, Ordering::SeqCst); if let Err(e) = res { diff --git a/ethcore/src/snapshot/tests/service.rs b/ethcore/src/snapshot/tests/service.rs index e136985c6..efdb12323 100644 --- a/ethcore/src/snapshot/tests/service.rs +++ b/ethcore/src/snapshot/tests/service.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use client::{BlockChainClient, Client}; -use ids::BlockID; +use ids::BlockId; use snapshot::service::{Service, ServiceParams}; use snapshot::{self, ManifestData, SnapshotService}; use spec::Spec; @@ -96,8 +96,8 @@ fn restored_is_equivalent() { assert_eq!(service.status(), ::snapshot::RestorationStatus::Inactive); for x in 0..NUM_BLOCKS { - let block1 = client.block(BlockID::Number(x as u64)).unwrap(); - let block2 = client2.block(BlockID::Number(x as u64)).unwrap(); + let block1 = client.block(BlockId::Number(x as u64)).unwrap(); + let block2 = client2.block(BlockId::Number(x as u64)).unwrap(); assert_eq!(block1, block2); } diff --git a/ethcore/src/snapshot/watcher.rs b/ethcore/src/snapshot/watcher.rs index 43439e437..ab4dde134 100644 --- a/ethcore/src/snapshot/watcher.rs +++ b/ethcore/src/snapshot/watcher.rs @@ -18,7 +18,7 @@ use util::Mutex; use client::{BlockChainClient, Client, ChainNotify}; -use ids::BlockID; +use ids::BlockId; use service::ClientIoMessage; use views::HeaderView; @@ -43,7 +43,7 @@ impl<F> Oracle for StandardOracle<F> where F: Send + Sync + Fn() -> bool { fn to_number(&self, hash: H256) -> Option<u64> { - self.client.block_header(BlockID::Hash(hash)).map(|h| HeaderView::new(&h).number()) + self.client.block_header(BlockId::Hash(hash)).map(|h| HeaderView::new(&h).number()) } fn is_major_importing(&self) -> bool { diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 427082823..0b688345d 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see <http://www.gnu.org/licenses/>. use io::IoChannel; -use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockID}; +use client::{BlockChainClient, MiningBlockChainClient, Client, ClientConfig, BlockId}; use state::CleanupMode; use ethereum; use block::IsBlock; @@ -99,7 +99,7 @@ fn imports_good_block() { client.flush_queue(); client.import_verified_blocks(); - let block = client.block_header(BlockID::Number(1)).unwrap(); + let block = client.block_header(BlockId::Number(1)).unwrap(); assert!(!block.is_empty()); } @@ -117,7 +117,7 @@ fn query_none_block() { IoChannel::disconnected(), &db_config ).unwrap(); - let non_existant = client.block_header(BlockID::Number(188)); + let non_existant = client.block_header(BlockId::Number(188)); assert!(non_existant.is_none()); } @@ -125,7 +125,7 @@ fn query_none_block() { fn query_bad_block() { let client_result = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]); let client = client_result.reference(); - let bad_block:Option<Bytes> = client.block_header(BlockID::Number(1)); + let bad_block:Option<Bytes> = client.block_header(BlockId::Number(1)); assert!(bad_block.is_none()); } @@ -146,8 +146,8 @@ fn returns_logs() { let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]); let client = client_result.reference(); let logs = client.logs(Filter { - from_block: BlockID::Earliest, - to_block: BlockID::Latest, + from_block: BlockId::Earliest, + to_block: BlockId::Latest, address: None, topics: vec![], limit: None, @@ -161,8 +161,8 @@ fn returns_logs_with_limit() { let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]); let client = client_result.reference(); let logs = client.logs(Filter { - from_block: BlockID::Earliest, - to_block: BlockID::Latest, + from_block: BlockId::Earliest, + to_block: BlockId::Latest, address: None, topics: vec![], limit: Some(2), @@ -176,7 +176,7 @@ fn returns_block_body() { let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]); let client = client_result.reference(); let block = BlockView::new(&dummy_block); - let body = client.block_body(BlockID::Hash(block.header().hash())).unwrap(); + let body = client.block_body(BlockId::Hash(block.header().hash())).unwrap(); let body = Rlp::new(&body); assert_eq!(body.item_count(), 2); assert_eq!(body.at(0).as_raw()[..], block.rlp().at(1).as_raw()[..]); @@ -187,7 +187,7 @@ fn returns_block_body() { fn imports_block_sequence() { let client_result = generate_dummy_client(6); let client = client_result.reference(); - let block = client.block_header(BlockID::Number(5)).unwrap(); + let block = client.block_header(BlockId::Number(5)).unwrap(); assert!(!block.is_empty()); } diff --git a/ethcore/src/tests/rpc.rs b/ethcore/src/tests/rpc.rs index b021e750d..2da94b3d0 100644 --- a/ethcore/src/tests/rpc.rs +++ b/ethcore/src/tests/rpc.rs @@ -19,7 +19,7 @@ use nanoipc; use std::sync::Arc; use std::sync::atomic::{Ordering, AtomicBool}; -use client::{Client, BlockChainClient, ClientConfig, BlockID}; +use client::{Client, BlockChainClient, ClientConfig, BlockId}; use client::remote::RemoteClient; use tests::helpers::*; use devtools::*; @@ -71,7 +71,7 @@ fn can_query_block() { run_test_worker(scope, stop_guard.share(), socket_path); let remote_client = nanoipc::generic_client::<RemoteClient<_>>(socket_path).unwrap(); - let non_existant_block = remote_client.block_header(BlockID::Number(999)); + let non_existant_block = remote_client.block_header(BlockId::Number(999)); assert!(non_existant_block.is_none()); }) diff --git a/ethcore/src/trace/db.rs b/ethcore/src/trace/db.rs index 14129d4d9..174c9b386 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/src/trace/db.rs @@ -94,7 +94,7 @@ impl Key<blooms::BloomGroup> for TraceGroupPosition { } #[derive(Debug, Hash, Eq, PartialEq)] -enum CacheID { +enum CacheId { Trace(H256), Bloom(TraceGroupPosition), } @@ -104,7 +104,7 @@ pub struct TraceDB<T> where T: DatabaseExtras { // cache traces: RwLock<HashMap<H256, FlatBlockTraces>>, blooms: RwLock<HashMap<TraceGroupPosition, blooms::BloomGroup>>, - cache_manager: RwLock<CacheManager<CacheID>>, + cache_manager: RwLock<CacheManager<CacheId>>, // db tracesdb: Arc<Database>, // config, @@ -119,7 +119,7 @@ impl<T> BloomGroupDatabase for TraceDB<T> where T: DatabaseExtras { fn blooms_at(&self, position: &GroupPosition) -> Option<BloomGroup> { let position = TraceGroupPosition::from(position.clone()); let result = self.tracesdb.read_with_cache(db::COL_TRACE, &self.blooms, &position).map(Into::into); - self.note_used(CacheID::Bloom(position)); + self.note_used(CacheId::Bloom(position)); result } } @@ -152,7 +152,7 @@ impl<T> TraceDB<T> where T: DatabaseExtras { } /// Let the cache system know that a cacheable item has been used. - fn note_used(&self, id: CacheID) { + fn note_used(&self, id: CacheId) { let mut cache_manager = self.cache_manager.write(); cache_manager.note_used(id); } @@ -168,8 +168,8 @@ impl<T> TraceDB<T> where T: DatabaseExtras { cache_manager.collect_garbage(current_size, | ids | { for id in &ids { match *id { - CacheID::Trace(ref h) => { traces.remove(h); }, - CacheID::Bloom(ref h) => { blooms.remove(h); }, + CacheId::Trace(ref h) => { traces.remove(h); }, + CacheId::Bloom(ref h) => { blooms.remove(h); }, } } traces.shrink_to_fit(); @@ -182,7 +182,7 @@ impl<T> TraceDB<T> where T: DatabaseExtras { /// Returns traces for block with hash. fn traces(&self, block_hash: &H256) -> Option<FlatBlockTraces> { let result = self.tracesdb.read_with_cache(db::COL_TRACE, &self.traces, block_hash); - self.note_used(CacheID::Trace(block_hash.clone())); + self.note_used(CacheId::Trace(block_hash.clone())); result } @@ -289,7 +289,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras { batch.extend_with_cache(db::COL_TRACE, &mut *blooms, blooms_to_insert, CacheUpdatePolicy::Remove); // note_used must be called after locking blooms to avoid cache/traces deadlock on garbage collection for key in blooms_keys { - self.note_used(CacheID::Bloom(key)); + self.note_used(CacheId::Bloom(key)); } } @@ -300,7 +300,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras { // cause this value might be queried by hash later batch.write_with_cache(db::COL_TRACE, &mut *traces, request.block_hash, request.traces, CacheUpdatePolicy::Overwrite); // note_used must be called after locking traces to avoid cache/traces deadlock on garbage collection - self.note_used(CacheID::Trace(request.block_hash.clone())); + self.note_used(CacheId::Trace(request.block_hash.clone())); } } diff --git a/ethcore/src/types/filter.rs b/ethcore/src/types/filter.rs index e3487e5f6..ecc997f71 100644 --- a/ethcore/src/types/filter.rs +++ b/ethcore/src/types/filter.rs @@ -18,17 +18,17 @@ use util::{Address, H256, Hashable, H2048}; use util::bloom::Bloomable; -use client::BlockID; +use client::BlockId; use log_entry::LogEntry; /// Blockchain Filter. #[derive(Binary, Debug, PartialEq)] pub struct Filter { /// Blockchain will be searched from this block. - pub from_block: BlockID, + pub from_block: BlockId, /// Till this block. - pub to_block: BlockID, + pub to_block: BlockId, /// Search addresses. /// @@ -114,14 +114,14 @@ impl Filter { mod tests { use util::FixedHash; use filter::Filter; - use client::BlockID; + use client::BlockId; use log_entry::LogEntry; #[test] fn test_bloom_possibilities_none() { let none_filter = Filter { - from_block: BlockID::Earliest, - to_block: BlockID::Latest, + from_block: BlockId::Earliest, + to_block: BlockId::Latest, address: None, topics: vec![None, None, None, None], limit: None, @@ -136,8 +136,8 @@ mod tests { #[test] fn test_bloom_possibilities_single_address_and_topic() { let filter = Filter { - from_block: BlockID::Earliest, - to_block: BlockID::Latest, + from_block: BlockId::Earliest, + to_block: BlockId::Latest, address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), topics: vec![ Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), @@ -155,8 +155,8 @@ mod tests { #[test] fn test_bloom_possibilities_single_address_and_many_topics() { let filter = Filter { - from_block: BlockID::Earliest, - to_block: BlockID::Latest, + from_block: BlockId::Earliest, + to_block: BlockId::Latest, address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), topics: vec![ Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), @@ -174,8 +174,8 @@ mod tests { #[test] fn test_bloom_possibilites_multiple_addresses_and_topics() { let filter = Filter { - from_block: BlockID::Earliest, - to_block: BlockID::Latest, + from_block: BlockId::Earliest, + to_block: BlockId::Latest, address: Some(vec![ "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), @@ -204,8 +204,8 @@ mod tests { #[test] fn test_filter_matches() { let filter = Filter { - from_block: BlockID::Earliest, - to_block: BlockID::Latest, + from_block: BlockId::Earliest, + to_block: BlockId::Latest, address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), topics: vec![ Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), diff --git a/ethcore/src/types/ids.rs b/ethcore/src/types/ids.rs index 1fe81f392..2c3c777b4 100644 --- a/ethcore/src/types/ids.rs +++ b/ethcore/src/types/ids.rs @@ -21,7 +21,7 @@ use header::BlockNumber; /// Uniquely identifies block. #[derive(Debug, PartialEq, Copy, Clone, Hash, Eq, Binary)] -pub enum BlockID { +pub enum BlockId { /// Block's sha3. /// Querying by hash is always faster. Hash(H256), @@ -37,28 +37,28 @@ pub enum BlockID { /// Uniquely identifies transaction. #[derive(Debug, PartialEq, Clone, Hash, Eq, Binary)] -pub enum TransactionID { +pub enum TransactionId { /// Transaction's sha3. Hash(H256), /// Block id and transaction index within this block. /// Querying by block position is always faster. - Location(BlockID, usize) + Location(BlockId, usize) } /// Uniquely identifies Trace. #[derive(Binary)] pub struct TraceId { /// Transaction - pub transaction: TransactionID, + pub transaction: TransactionId, /// Trace address within transaction. pub address: Vec<usize>, } /// Uniquely identifies Uncle. #[derive(Debug, PartialEq, Eq, Copy, Clone, Binary)] -pub struct UncleID { +pub struct UncleId { /// Block id. - pub block: BlockID, + pub block: BlockId, /// Position in block. pub position: usize } diff --git a/ethcore/src/types/trace_filter.rs b/ethcore/src/types/trace_filter.rs index c17cc9e85..af9d7c3ee 100644 --- a/ethcore/src/types/trace_filter.rs +++ b/ethcore/src/types/trace_filter.rs @@ -18,13 +18,13 @@ use std::ops::Range; use util::{Address}; -use types::ids::BlockID; +use types::ids::BlockId; /// Easy to use trace filter. #[derive(Binary)] pub struct Filter { /// Range of filtering. - pub range: Range<BlockID>, + pub range: Range<BlockId>, /// From address. pub from_address: Vec<Address>, /// To address. diff --git a/ethstore/src/dir/disk.rs b/ethstore/src/dir/disk.rs index 56b2c1ccb..c86123d1a 100644 --- a/ethstore/src/dir/disk.rs +++ b/ethstore/src/dir/disk.rs @@ -20,7 +20,7 @@ use std::collections::HashMap; use time; use ethkey::Address; use {json, SafeAccount, Error}; -use json::UUID; +use json::Uuid; use super::KeyDirectory; const IGNORED_FILES: &'static [&'static str] = &["thumbs.db", "address_book.json"]; @@ -113,7 +113,7 @@ impl KeyDirectory for DiskDirectory { // build file path let filename = account.filename.as_ref().cloned().unwrap_or_else(|| { let timestamp = time::strftime("%Y-%m-%dT%H-%M-%S", &time::now_utc()).expect("Time-format string is valid."); - format!("UTC--{}Z--{}", timestamp, UUID::from(account.id)) + format!("UTC--{}Z--{}", timestamp, Uuid::from(account.id)) }); // update account filename diff --git a/ethstore/src/ethstore.rs b/ethstore/src/ethstore.rs index 4991c4714..3747431fb 100644 --- a/ethstore/src/ethstore.rs +++ b/ethstore/src/ethstore.rs @@ -24,7 +24,7 @@ use dir::KeyDirectory; use account::SafeAccount; use {Error, SecretStore}; use json; -use json::UUID; +use json::Uuid; use parking_lot::RwLock; use presale::PresaleWallet; use import; @@ -154,7 +154,7 @@ impl SecretStore for EthStore { account.public(password) } - fn uuid(&self, address: &Address) -> Result<UUID, Error> { + fn uuid(&self, address: &Address) -> Result<Uuid, Error> { let account = try!(self.get(address)); Ok(account.id.into()) } diff --git a/ethstore/src/json/error.rs b/ethstore/src/json/error.rs index 5e077cfad..a3ea4d326 100644 --- a/ethstore/src/json/error.rs +++ b/ethstore/src/json/error.rs @@ -21,7 +21,7 @@ pub enum Error { UnsupportedCipher, InvalidCipherParams, UnsupportedKdf, - InvalidUUID, + InvalidUuid, UnsupportedVersion, InvalidCiphertext, InvalidH256, @@ -31,7 +31,7 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { - Error::InvalidUUID => write!(f, "Invalid UUID"), + Error::InvalidUuid => write!(f, "Invalid Uuid"), Error::UnsupportedVersion => write!(f, "Unsupported version"), Error::UnsupportedKdf => write!(f, "Unsupported kdf"), Error::InvalidCiphertext => write!(f, "Invalid ciphertext"), diff --git a/ethstore/src/json/id.rs b/ethstore/src/json/id.rs index ff282a9f8..664716d95 100644 --- a/ethstore/src/json/id.rs +++ b/ethstore/src/json/id.rs @@ -23,15 +23,15 @@ use super::Error; /// Universaly unique identifier. #[derive(Debug, PartialEq)] -pub struct UUID([u8; 16]); +pub struct Uuid([u8; 16]); -impl From<[u8; 16]> for UUID { +impl From<[u8; 16]> for Uuid { fn from(uuid: [u8; 16]) -> Self { - UUID(uuid) + Uuid(uuid) } } -impl<'a> Into<String> for &'a UUID { +impl<'a> Into<String> for &'a Uuid { fn into(self) -> String { let d1 = &self.0[0..4]; let d2 = &self.0[4..6]; @@ -42,44 +42,44 @@ impl<'a> Into<String> for &'a UUID { } } -impl Into<String> for UUID { +impl Into<String> for Uuid { fn into(self) -> String { Into::into(&self) } } -impl Into<[u8; 16]> for UUID { +impl Into<[u8; 16]> for Uuid { fn into(self) -> [u8; 16] { self.0 } } -impl fmt::Display for UUID { +impl fmt::Display for Uuid { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let s: String = (self as &UUID).into(); + let s: String = (self as &Uuid).into(); write!(f, "{}", s) } } fn copy_into(from: &str, into: &mut [u8]) -> Result<(), Error> { - let from = try!(from.from_hex().map_err(|_| Error::InvalidUUID)); + let from = try!(from.from_hex().map_err(|_| Error::InvalidUuid)); if from.len() != into.len() { - return Err(Error::InvalidUUID); + return Err(Error::InvalidUuid); } into.copy_from_slice(&from); Ok(()) } -impl str::FromStr for UUID { +impl str::FromStr for Uuid { type Err = Error; fn from_str(s: &str) -> Result<Self, Self::Err> { let parts: Vec<&str> = s.split("-").collect(); if parts.len() != 5 { - return Err(Error::InvalidUUID); + return Err(Error::InvalidUuid); } let mut uuid = [0u8; 16]; @@ -90,17 +90,17 @@ impl str::FromStr for UUID { try!(copy_into(parts[3], &mut uuid[8..10])); try!(copy_into(parts[4], &mut uuid[10..16])); - Ok(UUID(uuid)) + Ok(Uuid(uuid)) } } -impl From<&'static str> for UUID { +impl From<&'static str> for Uuid { fn from(s: &'static str) -> Self { s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s)) } } -impl Serialize for UUID { +impl Serialize for Uuid { fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { let s: String = self.into(); @@ -108,17 +108,17 @@ impl Serialize for UUID { } } -impl Deserialize for UUID { +impl Deserialize for Uuid { fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: Deserializer { - deserializer.deserialize(UUIDVisitor) + deserializer.deserialize(UuidVisitor) } } -struct UUIDVisitor; +struct UuidVisitor; -impl Visitor for UUIDVisitor { - type Value = UUID; +impl Visitor for UuidVisitor { + type Value = Uuid; fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: SerdeError { value.parse().map_err(SerdeError::custom) @@ -131,18 +131,18 @@ impl Visitor for UUIDVisitor { #[cfg(test)] mod tests { - use super::UUID; + use super::Uuid; #[test] fn uuid_from_str() { - let uuid: UUID = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into(); - assert_eq!(uuid, UUID::from([0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a, 0xe5, 0xb6])); + let uuid: Uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6".into(); + assert_eq!(uuid, Uuid::from([0x31, 0x98, 0xbc, 0x9c, 0x66, 0x72, 0x5a, 0xb3, 0xd9, 0x95, 0x49, 0x42, 0x34, 0x3a, 0xe5, 0xb6])); } #[test] fn uuid_from_and_to_str() { let from = "3198bc9c-6672-5ab3-d995-4942343ae5b6"; - let uuid: UUID = from.into(); + let uuid: Uuid = from.into(); let to: String = uuid.into(); assert_eq!(from, &to); } diff --git a/ethstore/src/json/key_file.rs b/ethstore/src/json/key_file.rs index 6e37c7c89..093479b3a 100644 --- a/ethstore/src/json/key_file.rs +++ b/ethstore/src/json/key_file.rs @@ -18,11 +18,11 @@ use std::io::{Read, Write}; use serde::{Deserialize, Deserializer, Error}; use serde::de::{Visitor, MapVisitor}; use serde_json; -use super::{UUID, Version, Crypto, H160}; +use super::{Uuid, Version, Crypto, H160}; #[derive(Debug, PartialEq, Serialize)] pub struct KeyFile { - pub id: UUID, + pub id: Uuid, pub version: Version, pub crypto: Crypto, pub address: H160, @@ -31,7 +31,7 @@ pub struct KeyFile { } enum KeyFileField { - ID, + Id, Version, Crypto, Address, @@ -56,7 +56,7 @@ impl Visitor for KeyFileFieldVisitor { where E: Error { match value { - "id" => Ok(KeyFileField::ID), + "id" => Ok(KeyFileField::Id), "version" => Ok(KeyFileField::Version), "crypto" => Ok(KeyFileField::Crypto), "Crypto" => Ok(KeyFileField::Crypto), @@ -94,7 +94,7 @@ impl Visitor for KeyFileVisitor { loop { match try!(visitor.visit_key()) { - Some(KeyFileField::ID) => { id = Some(try!(visitor.visit_value())); } + Some(KeyFileField::Id) => { id = Some(try!(visitor.visit_value())); } Some(KeyFileField::Version) => { version = Some(try!(visitor.visit_value())); } Some(KeyFileField::Crypto) => { crypto = Some(try!(visitor.visit_value())); } Some(KeyFileField::Address) => { address = Some(try!(visitor.visit_value())); } @@ -153,7 +153,7 @@ impl KeyFile { mod tests { use std::str::FromStr; use serde_json; - use json::{KeyFile, UUID, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt}; + use json::{KeyFile, Uuid, Version, Crypto, Cipher, Aes128Ctr, Kdf, Scrypt}; #[test] fn basic_keyfile() { @@ -183,7 +183,7 @@ mod tests { }"#; let expected = KeyFile { - id: UUID::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(), + id: Uuid::from_str("8777d9f6-7860-4b9b-88b7-0b57ee6b3a73").unwrap(), version: Version::V3, address: "6edddfc6349aff20bc6467ccf276c5b52487f7a8".into(), crypto: Crypto { diff --git a/ethstore/src/json/mod.rs.in b/ethstore/src/json/mod.rs.in index 133d9821e..8ad48b994 100644 --- a/ethstore/src/json/mod.rs.in +++ b/ethstore/src/json/mod.rs.in @@ -14,7 +14,7 @@ pub use self::cipher::{Cipher, CipherSer, CipherSerParams, Aes128Ctr}; pub use self::crypto::{Crypto, CipherText}; pub use self::error::Error; pub use self::hash::{H128, H160, H256}; -pub use self::id::UUID; +pub use self::id::Uuid; pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams}; pub use self::key_file::KeyFile; pub use self::presale::{PresaleWallet, Encseed}; diff --git a/ethstore/src/secret_store.rs b/ethstore/src/secret_store.rs index 06f38922b..bf0a4ec44 100644 --- a/ethstore/src/secret_store.rs +++ b/ethstore/src/secret_store.rs @@ -16,7 +16,7 @@ use ethkey::{Address, Message, Signature, Secret, Public}; use Error; -use json::UUID; +use json::Uuid; pub trait SecretStore: Send + Sync { fn insert_account(&self, secret: Secret, password: &str) -> Result<Address, Error>; @@ -30,7 +30,7 @@ pub trait SecretStore: Send + Sync { fn public(&self, account: &Address, password: &str) -> Result<Public, Error>; fn accounts(&self) -> Result<Vec<Address>, Error>; - fn uuid(&self, account: &Address) -> Result<UUID, Error>; + fn uuid(&self, account: &Address) -> Result<Uuid, Error>; fn name(&self, account: &Address) -> Result<String, Error>; fn meta(&self, account: &Address) -> Result<String, Error>; diff --git a/js/src/jsonrpc/interfaces/parity.js b/js/src/jsonrpc/interfaces/parity.js index 067ced1fc..76886af35 100644 --- a/js/src/jsonrpc/interfaces/parity.js +++ b/js/src/jsonrpc/interfaces/parity.js @@ -43,7 +43,7 @@ export default { }, uuid: { type: String, - desc: 'The account UUID, or null if not available/unknown/not applicable.' + desc: 'The account Uuid, or null if not available/unknown/not applicable.' } } } @@ -66,7 +66,7 @@ export default { }, uuid: { type: String, - desc: 'The account UUID, or null if not available/unknown/not applicable.' + desc: 'The account Uuid, or null if not available/unknown/not applicable.' } } } diff --git a/json/src/misc/account_meta.rs b/json/src/misc/account_meta.rs index 400f9b8df..a347a8cf6 100644 --- a/json/src/misc/account_meta.rs +++ b/json/src/misc/account_meta.rs @@ -29,7 +29,7 @@ pub struct AccountMeta { pub name: String, /// The rest of the metadata of the account. pub meta: String, - /// The 128-bit UUID of the account, if it has one (brain-wallets don't). + /// The 128-bit Uuid of the account, if it has one (brain-wallets don't). pub uuid: Option<String>, } diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 0750d369d..a9d81e5c3 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -25,7 +25,7 @@ use io::{PanicHandler, ForwardPanic}; use util::{ToPretty, Uint, U256, H256, Address, Hashable}; use rlp::PayloadInfo; use ethcore::service::ClientService; -use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockImportError, BlockChainClient, BlockID}; +use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockImportError, BlockChainClient, BlockId}; use ethcore::error::ImportError; use ethcore::miner::Miner; use ethcore::verification::queue::VerifierSettings; @@ -101,8 +101,8 @@ pub struct ExportBlockchain { pub wal: bool, pub fat_db: Switch, pub tracing: Switch, - pub from_block: BlockID, - pub to_block: BlockID, + pub from_block: BlockId, + pub to_block: BlockId, pub check_seal: bool, } @@ -119,7 +119,7 @@ pub struct ExportState { pub wal: bool, pub fat_db: Switch, pub tracing: Switch, - pub at: BlockID, + pub at: BlockId, pub storage: bool, pub code: bool, pub min_balance: Option<U256>, @@ -384,7 +384,7 @@ fn execute_export(cmd: ExportBlockchain) -> Result<String, String> { if i % 10000 == 0 { info!("#{}", i); } - let b = try!(client.block(BlockID::Number(i)).ok_or("Error exporting incomplete chain")); + let b = try!(client.block(BlockId::Number(i)).ok_or("Error exporting incomplete chain")); match format { DataFormat::Binary => { out.write(&b).expect("Couldn't write to stream."); } DataFormat::Hex => { out.write_fmt(format_args!("{}", b.pretty())).expect("Couldn't write to stream."); } diff --git a/parity/configuration.rs b/parity/configuration.rs index 60116ef99..5b7c34c27 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -729,7 +729,7 @@ mod tests { use super::*; use cli::Args; use ethcore_rpc::NetworkSettings; - use ethcore::client::{VMType, BlockID}; + use ethcore::client::{VMType, BlockId}; use ethcore::miner::{MinerOptions, PrioritizationStrategy}; use helpers::{replace_home, default_network_config}; use run::RunCmd; @@ -838,8 +838,8 @@ mod tests { wal: true, tracing: Default::default(), fat_db: Default::default(), - from_block: BlockID::Number(1), - to_block: BlockID::Latest, + from_block: BlockId::Number(1), + to_block: BlockId::Latest, check_seal: true, }))); } @@ -860,7 +860,7 @@ mod tests { wal: true, tracing: Default::default(), fat_db: Default::default(), - at: BlockID::Latest, + at: BlockId::Latest, storage: true, code: true, min_balance: None, @@ -884,8 +884,8 @@ mod tests { wal: true, tracing: Default::default(), fat_db: Default::default(), - from_block: BlockID::Number(1), - to_block: BlockID::Latest, + from_block: BlockId::Number(1), + to_block: BlockId::Latest, check_seal: true, }))); } diff --git a/parity/dapps.rs b/parity/dapps.rs index 16ae4dd98..ec6fd8846 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -106,7 +106,7 @@ mod server { use util::{Bytes, Address, U256}; use ethcore::transaction::{Transaction, Action}; - use ethcore::client::{Client, BlockChainClient, BlockID}; + use ethcore::client::{Client, BlockChainClient, BlockId}; use rpc_apis; use ethcore_rpc::is_major_importing; @@ -182,7 +182,7 @@ mod server { data: data, }.fake_sign(from); - self.client.call(&transaction, BlockID::Latest, Default::default()) + self.client.call(&transaction, BlockId::Latest, Default::default()) .map_err(|e| format!("{:?}", e)) .map(|executed| { executed.output diff --git a/parity/helpers.rs b/parity/helpers.rs index 654270b64..60a04bc45 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -20,7 +20,7 @@ use std::time::Duration; use std::fs::File; use util::{clean_0x, U256, Uint, Address, path, CompactionProfile}; use util::journaldb::Algorithm; -use ethcore::client::{Mode, BlockID, VMType, DatabaseCompactionProfile, ClientConfig, VerifierType}; +use ethcore::client::{Mode, BlockId, VMType, DatabaseCompactionProfile, ClientConfig, VerifierType}; use ethcore::miner::{PendingSet, GasLimit, PrioritizationStrategy}; use cache::CacheConfig; use dir::DatabaseDirectories; @@ -62,13 +62,13 @@ pub fn to_mode(s: &str, timeout: u64, alarm: u64) -> Result<Mode, String> { } } -pub fn to_block_id(s: &str) -> Result<BlockID, String> { +pub fn to_block_id(s: &str) -> Result<BlockId, String> { if s == "latest" { - Ok(BlockID::Latest) + Ok(BlockId::Latest) } else if let Ok(num) = s.parse() { - Ok(BlockID::Number(num)) + Ok(BlockId::Number(num)) } else if let Ok(hash) = s.parse() { - Ok(BlockID::Hash(hash)) + Ok(BlockId::Hash(hash)) } else { Err("Invalid block.".into()) } @@ -327,7 +327,7 @@ mod tests { use std::io::Write; use devtools::RandomTempPath; use util::{U256}; - use ethcore::client::{Mode, BlockID}; + use ethcore::client::{Mode, BlockId}; use ethcore::miner::PendingSet; use super::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_address, to_addresses, to_price, geth_ipc_path, to_bootnodes, password_from_file}; @@ -361,13 +361,13 @@ mod tests { #[test] fn test_to_block_id() { - assert_eq!(to_block_id("latest").unwrap(), BlockID::Latest); - assert_eq!(to_block_id("0").unwrap(), BlockID::Number(0)); - assert_eq!(to_block_id("2").unwrap(), BlockID::Number(2)); - assert_eq!(to_block_id("15").unwrap(), BlockID::Number(15)); + assert_eq!(to_block_id("latest").unwrap(), BlockId::Latest); + assert_eq!(to_block_id("0").unwrap(), BlockId::Number(0)); + assert_eq!(to_block_id("2").unwrap(), BlockId::Number(2)); + assert_eq!(to_block_id("15").unwrap(), BlockId::Number(15)); assert_eq!( to_block_id("9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e").unwrap(), - BlockID::Hash("9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e".parse().unwrap()) + BlockId::Hash("9fc84d84f6a785dc1bd5abacfcf9cbdd3b6afb80c0f799bfb2fd42c44a0c224e".parse().unwrap()) ); } diff --git a/parity/informant.rs b/parity/informant.rs index d3e3c8a20..1caeb1b7c 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -184,12 +184,12 @@ impl ChainNotify for Informant { let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing; let txs_imported = imported.iter() .take(imported.len() - if ripe {1} else {0}) - .filter_map(|h| self.client.block(BlockID::Hash(*h))) + .filter_map(|h| self.client.block(BlockId::Hash(*h))) .map(|b| BlockView::new(&b).transactions_count()) .sum(); if ripe { - if let Some(block) = imported.last().and_then(|h| self.client.block(BlockID::Hash(*h))) { + if let Some(block) = imported.last().and_then(|h| self.client.block(BlockId::Hash(*h))) { let view = BlockView::new(&block); let header = view.header(); let tx_count = view.transactions_count(); diff --git a/parity/snapshot.rs b/parity/snapshot.rs index d8323084d..804047596 100644 --- a/parity/snapshot.rs +++ b/parity/snapshot.rs @@ -26,7 +26,7 @@ use ethcore::snapshot::service::Service as SnapshotService; use ethcore::service::ClientService; use ethcore::client::{Mode, DatabaseCompactionProfile, VMType}; use ethcore::miner::Miner; -use ethcore::ids::BlockID; +use ethcore::ids::BlockId; use cache::CacheConfig; use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_bool}; @@ -60,7 +60,7 @@ pub struct SnapshotCommand { pub file_path: Option<String>, pub wal: bool, pub kind: Kind, - pub block_at: BlockID, + pub block_at: BlockId, } // helper for reading chunks from arbitrary reader and feeding them into the diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 6b9f47de3..d68d59e59 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -32,7 +32,7 @@ use util::sha3::*; use util::{FromHex, Mutex}; use rlp::{self, UntrustedRlp, View}; use ethcore::account_provider::AccountProvider; -use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID}; +use ethcore::client::{MiningBlockChainClient, BlockId, TransactionId, UncleId}; use ethcore::header::{Header as BlockHeader, BlockNumber as EthBlockNumber}; use ethcore::block::IsBlock; use ethcore::views::*; @@ -119,7 +119,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where } } - fn block(&self, id: BlockID, include_txs: bool) -> Result<Option<RichBlock>, Error> { + fn block(&self, id: BlockId, include_txs: bool) -> Result<Option<RichBlock>, Error> { let client = take_weak!(self.client); match (client.block(id.clone()), client.block_total_difficulty(id)) { (Some(bytes), Some(total_difficulty)) => { @@ -159,20 +159,20 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where } } - fn transaction(&self, id: TransactionID) -> Result<Option<Transaction>, Error> { + fn transaction(&self, id: TransactionId) -> Result<Option<Transaction>, Error> { match take_weak!(self.client).transaction(id) { Some(t) => Ok(Some(Transaction::from(t))), None => Ok(None), } } - fn uncle(&self, id: UncleID) -> Result<Option<RichBlock>, Error> { + fn uncle(&self, id: UncleId) -> Result<Option<RichBlock>, Error> { let client = take_weak!(self.client); let uncle: BlockHeader = match client.uncle(id) { Some(rlp) => rlp::decode(&rlp), None => { return Ok(None); } }; - let parent_difficulty = match client.block_total_difficulty(BlockID::Hash(uncle.parent_hash().clone())) { + let parent_difficulty = match client.block_total_difficulty(BlockId::Hash(uncle.parent_hash().clone())) { Some(difficulty) => difficulty, None => { return Ok(None); } }; @@ -393,7 +393,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where fn block_transaction_count_by_hash(&self, hash: RpcH256) -> Result<Option<RpcU256>, Error> { try!(self.active()); Ok( - take_weak!(self.client).block(BlockID::Hash(hash.into())) + take_weak!(self.client).block(BlockId::Hash(hash.into())) .map(|bytes| BlockView::new(&bytes).transactions_count().into()) ) } @@ -416,7 +416,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where try!(self.active()); Ok( - take_weak!(self.client).block(BlockID::Hash(hash.into())) + take_weak!(self.client).block(BlockId::Hash(hash.into())) .map(|bytes| BlockView::new(&bytes).uncles_count().into()) ) } @@ -449,7 +449,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where fn block_by_hash(&self, hash: RpcH256, include_txs: bool) -> Result<Option<RichBlock>, Error> { try!(self.active()); - self.block(BlockID::Hash(hash.into()), include_txs) + self.block(BlockId::Hash(hash.into()), include_txs) } fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> Result<Option<RichBlock>, Error> { @@ -463,19 +463,19 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where let hash: H256 = hash.into(); let miner = take_weak!(self.miner); let client = take_weak!(self.client); - Ok(try!(self.transaction(TransactionID::Hash(hash))).or_else(|| miner.transaction(client.chain_info().best_block_number, &hash).map(Into::into))) + Ok(try!(self.transaction(TransactionId::Hash(hash))).or_else(|| miner.transaction(client.chain_info().best_block_number, &hash).map(Into::into))) } fn transaction_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> Result<Option<Transaction>, Error> { try!(self.active()); - self.transaction(TransactionID::Location(BlockID::Hash(hash.into()), index.value())) + self.transaction(TransactionId::Location(BlockId::Hash(hash.into()), index.value())) } fn transaction_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> Result<Option<Transaction>, Error> { try!(self.active()); - self.transaction(TransactionID::Location(num.into(), index.value())) + self.transaction(TransactionId::Location(num.into(), index.value())) } fn transaction_receipt(&self, hash: RpcH256) -> Result<Option<Receipt>, Error> { @@ -488,7 +488,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where (Some(receipt), true) => Ok(Some(receipt.into())), _ => { let client = take_weak!(self.client); - let receipt = client.transaction_receipt(TransactionID::Hash(hash)); + let receipt = client.transaction_receipt(TransactionId::Hash(hash)); Ok(receipt.map(Into::into)) } } @@ -497,13 +497,13 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where fn uncle_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> Result<Option<RichBlock>, Error> { try!(self.active()); - self.uncle(UncleID { block: BlockID::Hash(hash.into()), position: index.value() }) + self.uncle(UncleId { block: BlockId::Hash(hash.into()), position: index.value() }) } fn uncle_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> Result<Option<RichBlock>, Error> { try!(self.active()); - self.uncle(UncleID { block: num.into(), position: index.value() }) + self.uncle(UncleId { block: num.into(), position: index.value() }) } fn compilers(&self) -> Result<Vec<String>, Error> { diff --git a/rpc/src/v1/impls/eth_filter.rs b/rpc/src/v1/impls/eth_filter.rs index dd1c937ac..695ff5251 100644 --- a/rpc/src/v1/impls/eth_filter.rs +++ b/rpc/src/v1/impls/eth_filter.rs @@ -21,7 +21,7 @@ use std::collections::HashSet; use jsonrpc_core::*; use ethcore::miner::MinerService; use ethcore::filter::Filter as EthcoreFilter; -use ethcore::client::{BlockChainClient, BlockID}; +use ethcore::client::{BlockChainClient, BlockId}; use util::Mutex; use v1::traits::EthFilter; use v1::types::{BlockNumber, Index, Filter, FilterChanges, Log, H256 as RpcH256, U256 as RpcU256}; @@ -98,7 +98,7 @@ impl<C, M> EthFilter for EthFilterClient<C, M> // + 1, cause we want to return hashes including current block hash. let current_number = client.chain_info().best_block_number + 1; let hashes = (*block_number..current_number).into_iter() - .map(BlockID::Number) + .map(BlockId::Number) .filter_map(|id| client.block_hash(id)) .map(Into::into) .collect::<Vec<RpcH256>>(); @@ -140,10 +140,10 @@ impl<C, M> EthFilter for EthFilterClient<C, M> // build appropriate filter let mut filter: EthcoreFilter = filter.clone().into(); - filter.from_block = BlockID::Number(*block_number); - filter.to_block = BlockID::Latest; + filter.from_block = BlockId::Number(*block_number); + filter.to_block = BlockId::Latest; - // retrieve logs in range from_block..min(BlockID::Latest..to_block) + // retrieve logs in range from_block..min(BlockId::Latest..to_block) let mut logs = client.logs(filter.clone()) .into_iter() .map(From::from) diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index e85d961a1..0b287ce29 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -19,7 +19,7 @@ use std::sync::{Weak, Arc}; use jsonrpc_core::*; use rlp::{UntrustedRlp, View}; -use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId}; +use ethcore::client::{BlockChainClient, CallAnalytics, TransactionId, TraceId}; use ethcore::miner::MinerService; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::Traces; @@ -100,7 +100,7 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M: from_params::<(H256,)>(params) .and_then(|(transaction_hash,)| { let client = take_weak!(self.client); - let traces = client.transaction_traces(TransactionID::Hash(transaction_hash.into())); + let traces = client.transaction_traces(TransactionId::Hash(transaction_hash.into())); let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(LocalizedTrace::from).collect()); Ok(to_value(&traces)) }) @@ -112,7 +112,7 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M: .and_then(|(transaction_hash, address)| { let client = take_weak!(self.client); let id = TraceId { - transaction: TransactionID::Hash(transaction_hash.into()), + transaction: TransactionId::Hash(transaction_hash.into()), address: address.into_iter().map(|i| i.value()).collect() }; let trace = client.trace(id); @@ -153,7 +153,7 @@ impl<C, M> Traces for TracesClient<C, M> where C: BlockChainClient + 'static, M: try!(self.active()); from_params::<(H256, _)>(params) .and_then(|(transaction_hash, flags)| { - match take_weak!(self.client).replay(TransactionID::Hash(transaction_hash.into()), to_call_analytics(flags)) { + match take_weak!(self.client).replay(TransactionId::Hash(transaction_hash.into()), to_call_analytics(flags)) { Ok(e) => Ok(to_value(&TraceResults::from(e))), _ => Ok(Value::Null), } diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 3aa83fec9..428cae3e0 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use std::time::Duration; use ethcore::client::{BlockChainClient, Client, ClientConfig}; -use ethcore::ids::BlockID; +use ethcore::ids::BlockId; use ethcore::spec::{Genesis, Spec}; use ethcore::block::Block; use ethcore::views::BlockView; @@ -425,7 +425,7 @@ fn verify_transaction_counts(name: String, chain: BlockChain) { assert_eq!(tester.handler.handle_request_sync(&req), Some(res)); // uncles can share block numbers, so skip them. - if tester.client.block_hash(BlockID::Number(number)) == Some(hash) { + if tester.client.block_hash(BlockId::Number(number)) == Some(hash) { let (req, res) = by_number(number, count, &mut id); assert_eq!(tester.handler.handle_request_sync(&req), Some(res)); } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 05c95346d..7a7a1f682 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -24,7 +24,7 @@ use rlp; use util::{Uint, U256, Address, H256, FixedHash, Mutex}; use ethcore::account_provider::AccountProvider; -use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID}; +use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionId}; use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; use ethcore::receipt::LocalizedReceipt; use ethcore::transaction::{Transaction, Action}; @@ -952,7 +952,7 @@ fn rpc_eth_transaction_receipt() { let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap(); let tester = EthTester::default(); - tester.client.set_transaction_receipt(TransactionID::Hash(hash), receipt); + tester.client.set_transaction_receipt(TransactionId::Hash(hash), receipt); let request = r#"{ "jsonrpc": "2.0", diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs index 01625f8ed..0a2b2f305 100644 --- a/rpc/src/v1/types/block_number.rs +++ b/rpc/src/v1/types/block_number.rs @@ -16,7 +16,7 @@ use serde::{Deserialize, Deserializer, Error}; use serde::de::Visitor; -use ethcore::client::BlockID; +use ethcore::client::BlockId; /// Represents rpc api block number param. #[derive(Debug, PartialEq, Clone)] @@ -64,20 +64,20 @@ impl Visitor for BlockNumberVisitor { } } -impl Into<BlockID> for BlockNumber { - fn into(self) -> BlockID { +impl Into<BlockId> for BlockNumber { + fn into(self) -> BlockId { match self { - BlockNumber::Num(n) => BlockID::Number(n), - BlockNumber::Earliest => BlockID::Earliest, - BlockNumber::Latest => BlockID::Latest, - BlockNumber::Pending => BlockID::Pending, + BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => BlockId::Pending, } } } #[cfg(test)] mod tests { - use ethcore::client::BlockID; + use ethcore::client::BlockId; use super::*; use serde_json; @@ -90,10 +90,10 @@ mod tests { #[test] fn block_number_into() { - assert_eq!(BlockID::Number(100), BlockNumber::Num(100).into()); - assert_eq!(BlockID::Earliest, BlockNumber::Earliest.into()); - assert_eq!(BlockID::Latest, BlockNumber::Latest.into()); - assert_eq!(BlockID::Pending, BlockNumber::Pending.into()); + assert_eq!(BlockId::Number(100), BlockNumber::Num(100).into()); + assert_eq!(BlockId::Earliest, BlockNumber::Earliest.into()); + assert_eq!(BlockId::Latest, BlockNumber::Latest.into()); + assert_eq!(BlockId::Pending, BlockNumber::Pending.into()); } } diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index bcc4d2e8d..5254b1602 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer, Error}; use serde_json::value; use jsonrpc_core::Value; use ethcore::filter::Filter as EthFilter; -use ethcore::client::BlockID; +use ethcore::client::BlockId; use v1::types::{BlockNumber, H160, H256, Log}; /// Variadic value @@ -73,8 +73,8 @@ pub struct Filter { impl Into<EthFilter> for Filter { fn into(self) -> EthFilter { EthFilter { - from_block: self.from_block.map_or_else(|| BlockID::Latest, Into::into), - to_block: self.to_block.map_or_else(|| BlockID::Latest, Into::into), + from_block: self.from_block.map_or_else(|| BlockId::Latest, Into::into), + to_block: self.to_block.map_or_else(|| BlockId::Latest, Into::into), address: self.address.and_then(|address| match address { VariadicValue::Null => None, VariadicValue::Single(a) => Some(vec![a.into()]), @@ -128,7 +128,7 @@ mod tests { use super::{VariadicValue, Topic, Filter}; use v1::types::BlockNumber; use ethcore::filter::Filter as EthFilter; - use ethcore::client::BlockID; + use ethcore::client::BlockId; #[test] fn topic_deserialization() { @@ -173,8 +173,8 @@ mod tests { let eth_filter: EthFilter = filter.into(); assert_eq!(eth_filter, EthFilter { - from_block: BlockID::Earliest, - to_block: BlockID::Latest, + from_block: BlockId::Earliest, + to_block: BlockId::Latest, address: Some(vec![]), topics: vec![ None, diff --git a/rpc/src/v1/types/trace_filter.rs b/rpc/src/v1/types/trace_filter.rs index 6c7460f9b..2517b1585 100644 --- a/rpc/src/v1/types/trace_filter.rs +++ b/rpc/src/v1/types/trace_filter.rs @@ -16,7 +16,7 @@ //! Trace filter deserialization. -use ethcore::client::BlockID; +use ethcore::client::BlockId; use ethcore::client; use v1::types::{BlockNumber, H160}; @@ -40,8 +40,8 @@ pub struct TraceFilter { impl Into<client::TraceFilter> for TraceFilter { fn into(self) -> client::TraceFilter { - let start = self.from_block.map_or(BlockID::Latest, Into::into); - let end = self.to_block.map_or(BlockID::Latest, Into::into); + let start = self.from_block.map_or(BlockId::Latest, Into::into); + let end = self.to_block.map_or(BlockId::Latest, Into::into); client::TraceFilter { range: start..end, from_address: self.from_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), diff --git a/sync/src/block_sync.rs b/sync/src/block_sync.rs index eea977906..79c7a65a9 100644 --- a/sync/src/block_sync.rs +++ b/sync/src/block_sync.rs @@ -22,7 +22,7 @@ use util::*; use rlp::*; use ethcore::views::{BlockView}; use ethcore::header::{BlockNumber, Header as BlockHeader}; -use ethcore::client::{BlockStatus, BlockID, BlockImportError}; +use ethcore::client::{BlockStatus, BlockId, BlockImportError}; use ethcore::block::Block; use ethcore::error::{ImportError, BlockError}; use sync_io::SyncIo; @@ -225,7 +225,7 @@ impl BlockDownloader { trace!(target: "sync", "Error decoding block header RLP: {:?}", e); BlockDownloaderImportError::Invalid })); - match io.chain().block_status(BlockID::Hash(hash.clone())) { + match io.chain().block_status(BlockId::Hash(hash.clone())) { BlockStatus::InChain | BlockStatus::Queued => { match self.state { State::Blocks => trace!(target: "sync", "Header already in chain {} ({})", number, hash), @@ -353,7 +353,7 @@ impl BlockDownloader { debug!(target: "sync", "Could not revert to previous ancient block, last: {} ({})", self.last_imported_block, self.last_imported_hash); self.reset(); } else { - match io.chain().block_hash(BlockID::Number(self.last_imported_block - 1)) { + match io.chain().block_hash(BlockId::Number(self.last_imported_block - 1)) { Some(h) => { self.last_imported_block -= 1; self.last_imported_hash = h; diff --git a/sync/src/blocks.rs b/sync/src/blocks.rs index bf0c4b244..bcb9973dc 100644 --- a/sync/src/blocks.rs +++ b/sync/src/blocks.rs @@ -475,7 +475,7 @@ impl BlockCollection { #[cfg(test)] mod test { use super::BlockCollection; - use ethcore::client::{TestBlockChainClient, EachBlockWith, BlockID, BlockChainClient}; + use ethcore::client::{TestBlockChainClient, EachBlockWith, BlockId, BlockChainClient}; use ethcore::views::HeaderView; use ethcore::header::BlockNumber; use util::*; @@ -497,7 +497,7 @@ mod test { assert!(is_empty(&bc)); let client = TestBlockChainClient::new(); client.add_blocks(100, EachBlockWith::Nothing); - let hashes = (0 .. 100).map(|i| (&client as &BlockChainClient).block_hash(BlockID::Number(i)).unwrap()).collect(); + let hashes = (0 .. 100).map(|i| (&client as &BlockChainClient).block_hash(BlockId::Number(i)).unwrap()).collect(); bc.reset_to(hashes); assert!(!is_empty(&bc)); bc.clear(); @@ -511,7 +511,7 @@ mod test { let client = TestBlockChainClient::new(); let nblocks = 200; client.add_blocks(nblocks, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockID::Number(i as BlockNumber)).unwrap()).collect(); + let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect(); let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect(); let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect(); @@ -564,7 +564,7 @@ mod test { let client = TestBlockChainClient::new(); let nblocks = 200; client.add_blocks(nblocks, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockID::Number(i as BlockNumber)).unwrap()).collect(); + let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect(); let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect(); let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect(); @@ -586,7 +586,7 @@ mod test { let client = TestBlockChainClient::new(); let nblocks = 200; client.add_blocks(nblocks, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockID::Number(i as BlockNumber)).unwrap()).collect(); + let blocks: Vec<_> = (0 .. nblocks).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect(); let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect(); let heads: Vec<_> = hashes.iter().enumerate().filter_map(|(i, h)| if i % 20 == 0 { Some(h.clone()) } else { None }).collect(); diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 2d53ad5ee..b3bf57158 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -94,7 +94,7 @@ use rlp::*; use network::*; use ethcore::views::{HeaderView}; use ethcore::header::{BlockNumber, Header as BlockHeader}; -use ethcore::client::{BlockChainClient, BlockStatus, BlockID, BlockChainInfo, BlockImportError}; +use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockImportError}; use ethcore::error::*; use ethcore::snapshot::{ManifestData, RestorationStatus}; use sync_io::SyncIo; @@ -934,7 +934,7 @@ impl ChainSync { io.disable_peer(peer_id); continue; } - match io.chain().block_status(BlockID::Hash(hash.clone())) { + match io.chain().block_status(BlockId::Hash(hash.clone())) { BlockStatus::InChain => { trace!(target: "sync", "New block hash already in chain {:?}", hash); }, @@ -1155,7 +1155,7 @@ impl ChainSync { return; } - let have_latest = io.chain().block_status(BlockID::Hash(peer_latest)) != BlockStatus::Unknown; + let have_latest = io.chain().block_status(BlockId::Hash(peer_latest)) != BlockStatus::Unknown; if !have_latest && (higher_difficulty || force || self.state == SyncState::NewBlocks) { // check if got new blocks to download trace!(target: "sync", "Syncing with {}, force={}, td={:?}, our td={}, state={:?}", peer_id, force, peer_difficulty, syncing_difficulty, self.state); @@ -1450,11 +1450,11 @@ impl ChainSync { // id is a hash let hash: H256 = try!(r.val_at(0)); trace!(target: "sync", "{} -> GetBlockHeaders (hash: {}, max: {}, skip: {}, reverse:{})", peer_id, hash, max_headers, skip, reverse); - match io.chain().block_header(BlockID::Hash(hash)) { + match io.chain().block_header(BlockId::Hash(hash)) { Some(hdr) => { let number = From::from(HeaderView::new(&hdr).number()); debug_assert_eq!(HeaderView::new(&hdr).sha3(), hash); - if max_headers == 1 || io.chain().block_hash(BlockID::Number(number)) != Some(hash) { + if max_headers == 1 || io.chain().block_hash(BlockId::Number(number)) != Some(hash) { // Non canonical header or single header requested // TODO: handle single-step reverse hashchains of non-canon hashes trace!(target:"sync", "Returning single header: {:?}", hash); @@ -1487,7 +1487,7 @@ impl ChainSync { trace!(target: "sync", "{}: Returning cached fork header", peer_id); data.extend_from_slice(hdr); count += 1; - } else if let Some(mut hdr) = io.chain().block_header(BlockID::Number(number)) { + } else if let Some(mut hdr) = io.chain().block_header(BlockId::Number(number)) { data.append(&mut hdr); count += 1; } else { @@ -1521,7 +1521,7 @@ impl ChainSync { let mut added = 0usize; let mut data = Bytes::new(); for i in 0..count { - if let Some(mut hdr) = io.chain().block_body(BlockID::Hash(try!(r.val_at::<H256>(i)))) { + if let Some(mut hdr) = io.chain().block_body(BlockId::Hash(try!(r.val_at::<H256>(i)))) { data.append(&mut hdr); added += 1; } @@ -1778,7 +1778,7 @@ impl ChainSync { let mut rlp_stream = RlpStream::new_list(blocks.len()); for block_hash in blocks { let mut hash_rlp = RlpStream::new_list(2); - let number = HeaderView::new(&chain.block_header(BlockID::Hash(block_hash.clone())) + let number = HeaderView::new(&chain.block_header(BlockId::Hash(block_hash.clone())) .expect("chain.tree_route and chain.find_uncles only return hahses of blocks that are in the blockchain. qed.")).number(); hash_rlp.append(&block_hash); hash_rlp.append(&number); @@ -1795,7 +1795,7 @@ impl ChainSync { /// creates latest block rlp for the given client fn create_latest_block_rlp(chain: &BlockChainClient) -> Bytes { let mut rlp_stream = RlpStream::new_list(2); - rlp_stream.append_raw(&chain.block(BlockID::Hash(chain.chain_info().best_block_hash)).expect("Best block always exists"), 1); + rlp_stream.append_raw(&chain.block(BlockId::Hash(chain.chain_info().best_block_hash)).expect("Best block always exists"), 1); rlp_stream.append(&chain.chain_info().total_difficulty); rlp_stream.out() } @@ -1803,8 +1803,8 @@ impl ChainSync { /// creates latest block rlp for the given client fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes { let mut rlp_stream = RlpStream::new_list(2); - rlp_stream.append_raw(&chain.block(BlockID::Hash(hash.clone())).expect("Block has just been sealed; qed"), 1); - rlp_stream.append(&chain.block_total_difficulty(BlockID::Hash(hash.clone())).expect("Block has just been sealed; qed.")); + rlp_stream.append_raw(&chain.block(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed"), 1); + rlp_stream.append(&chain.block_total_difficulty(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed.")); rlp_stream.out() } @@ -1812,7 +1812,7 @@ impl ChainSync { fn get_lagging_peers(&mut self, chain_info: &BlockChainInfo, io: &SyncIo) -> Vec<PeerId> { let latest_hash = chain_info.best_block_hash; self.peers.iter_mut().filter_map(|(&id, ref mut peer_info)| - match io.chain().block_status(BlockID::Hash(peer_info.latest_hash.clone())) { + match io.chain().block_status(BlockId::Hash(peer_info.latest_hash.clone())) { BlockStatus::InChain => { if peer_info.latest_hash != latest_hash { Some(id) @@ -1863,7 +1863,7 @@ impl ChainSync { fn propagate_new_hashes(&mut self, chain_info: &BlockChainInfo, io: &mut SyncIo, peers: &[PeerId]) -> usize { trace!(target: "sync", "Sending NewHashes to {:?}", peers); let mut sent = 0; - let last_parent = HeaderView::new(&io.chain().block_header(BlockID::Hash(chain_info.best_block_hash.clone())) + let last_parent = HeaderView::new(&io.chain().block_header(BlockId::Hash(chain_info.best_block_hash.clone())) .expect("Best block always exists")).parent_hash(); for peer_id in peers { sent += match ChainSync::create_new_hashes_rlp(io.chain(), &last_parent, &chain_info.best_block_hash) { @@ -2125,7 +2125,7 @@ mod tests { let mut client = TestBlockChainClient::new(); client.add_blocks(100, EachBlockWith::Nothing); - let blocks: Vec<_> = (0 .. 100).map(|i| (&client as &BlockChainClient).block(BlockID::Number(i as BlockNumber)).unwrap()).collect(); + let blocks: Vec<_> = (0 .. 100).map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap()).collect(); let headers: Vec<_> = blocks.iter().map(|b| Rlp::new(b).at(0).as_raw().to_vec()).collect(); let hashes: Vec<_> = headers.iter().map(|h| HeaderView::new(h).sha3()).collect(); @@ -2298,7 +2298,7 @@ mod tests { let mut client = TestBlockChainClient::new(); client.add_blocks(100, EachBlockWith::Uncle); let mut queue = VecDeque::new(); - let hash = client.block_hash(BlockID::Number(99)).unwrap(); + let hash = client.block_hash(BlockId::Number(99)).unwrap(); let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(5), &client); let chain_info = client.chain_info(); let ss = TestSnapshotService::new(); @@ -2556,7 +2556,7 @@ mod tests { // Add some balance to clients and reset nonces for h in &[good_blocks[0], retracted_blocks[0]] { - let block = client.block(BlockID::Hash(*h)).unwrap(); + let block = client.block(BlockId::Hash(*h)).unwrap(); let view = BlockView::new(&block); client.set_balance(view.transactions()[0].sender().unwrap(), U256::from(1_000_000_000)); client.set_nonce(view.transactions()[0].sender().unwrap(), U256::from(0)); @@ -2575,7 +2575,7 @@ mod tests { } // We need to update nonce status (because we say that the block has been imported) for h in &[good_blocks[0]] { - let block = client.block(BlockID::Hash(*h)).unwrap(); + let block = client.block(BlockId::Hash(*h)).unwrap(); let view = BlockView::new(&block); client.set_nonce(view.transactions()[0].sender().unwrap(), U256::from(1)); } diff --git a/sync/src/tests/chain.rs b/sync/src/tests/chain.rs index 361d53e29..02b9063fa 100644 --- a/sync/src/tests/chain.rs +++ b/sync/src/tests/chain.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see <http://www.gnu.org/licenses/>. use util::*; -use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockID, EachBlockWith}; +use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockId, EachBlockWith}; use chain::{SyncState}; use super::helpers::*; use SyncConfig; @@ -27,7 +27,7 @@ fn two_peers() { net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); net.sync(); - assert!(net.peer(0).chain.block(BlockID::Number(1000)).is_some()); + assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some()); assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read()); } @@ -37,7 +37,7 @@ fn long_chain() { let mut net = TestNet::new(2); net.peer(1).chain.add_blocks(50000, EachBlockWith::Nothing); net.sync(); - assert!(net.peer(0).chain.block(BlockID::Number(50000)).is_some()); + assert!(net.peer(0).chain.block(BlockId::Number(50000)).is_some()); assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read()); } @@ -71,7 +71,7 @@ fn empty_blocks() { net.peer(2).chain.add_blocks(5, with); } net.sync(); - assert!(net.peer(0).chain.block(BlockID::Number(1000)).is_some()); + assert!(net.peer(0).chain.block(BlockId::Number(1000)).is_some()); assert_eq!(*net.peer(0).chain.blocks.read(), *net.peer(1).chain.blocks.read()); } @@ -123,13 +123,13 @@ fn net_hard_fork() { let ref_client = TestBlockChainClient::new(); ref_client.add_blocks(50, EachBlockWith::Uncle); { - let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockID::Number(50)).unwrap()))); + let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap()))); net.peer(0).chain.add_blocks(100, EachBlockWith::Uncle); net.sync(); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 100); } { - let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockID::Number(50)).unwrap()))); + let mut net = TestNet::new_with_fork(2, Some((50, ref_client.block_hash(BlockId::Number(50)).unwrap()))); net.peer(0).chain.add_blocks(100, EachBlockWith::Nothing); net.sync(); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 0); From 5c555aa18f158a3824259696ebce14d10a174fad Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 01:25:23 +0100 Subject: [PATCH 33/74] Fixes error in Transfer modal (mix between props and own property) (#3788) --- js/src/modals/Transfer/store.js | 6 ++---- js/src/modals/Transfer/transfer.js | 11 +++++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/js/src/modals/Transfer/store.js b/js/src/modals/Transfer/store.js index cbb10f17f..3a8f54f92 100644 --- a/js/src/modals/Transfer/store.js +++ b/js/src/modals/Transfer/store.js @@ -107,10 +107,9 @@ export default class TransferStore { constructor (api, props) { this.api = api; - const { account, balance, gasLimit, senders, onClose, newError, sendersBalances } = props; + const { account, balance, gasLimit, senders, newError, sendersBalances } = props; this.account = account; this.balance = balance; - this.onClose = onClose; this.isWallet = account && account.wallet; this.newError = newError; @@ -136,8 +135,7 @@ export default class TransferStore { this.stage -= 1; } - @action onClose = () => { - this.onClose && this.onClose(); + @action handleClose = () => { this.stage = 0; } diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js index 00e84adaf..0c96a1168 100644 --- a/js/src/modals/Transfer/transfer.js +++ b/js/src/modals/Transfer/transfer.js @@ -208,7 +208,7 @@ class Transfer extends Component { <Button icon={ <ContentClear /> } label='Cancel' - onClick={ this.store.onClose } /> + onClick={ this.handleClose } /> ); const nextBtn = ( <Button @@ -234,7 +234,7 @@ class Transfer extends Component { <Button icon={ <ActionDoneAll /> } label='Close' - onClick={ this.store.onClose } /> + onClick={ this.handleClose } /> ); switch (stage) { @@ -264,6 +264,13 @@ class Transfer extends Component { </div> ); } + + handleClose = () => { + const { onClose } = this.props; + + this.store.handleClose(); + typeof onClose === 'function' && onClose(); + } } function mapStateToProps (initState, initProps) { From 923f85d90deb4c33609c0413765a8d648d241bc1 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 01:26:03 +0100 Subject: [PATCH 34/74] Update Material-UI (#3785) * Update MUI => Fixes wrong error styled inputs * Update postcss import --- js/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/package.json b/js/package.json index 46b0233a0..524c280dc 100644 --- a/js/package.json +++ b/js/package.json @@ -101,8 +101,8 @@ "mock-local-storage": "1.0.2", "mock-socket": "6.0.3", "nock": "9.0.2", - "postcss-import": "8.1.0", - "postcss-loader": "1.1.1", + "postcss-import": "9.0.0", + "postcss-loader": "1.2.0", "postcss-nested": "1.0.0", "postcss-simple-vars": "3.0.0", "progress": "1.1.8", @@ -139,7 +139,7 @@ "js-sha3": "0.5.5", "lodash": "4.17.2", "marked": "0.3.6", - "material-ui": "0.16.4", + "material-ui": "0.16.5", "material-ui-chip-input": "0.11.1", "mobx": "2.6.4", "mobx-react": "4.0.3", From 6655e7e3c0e2c3c23e4f3ba80c5886cf0fb78172 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 01:26:28 +0100 Subject: [PATCH 35/74] Add support for wallets without getOwner() interface (#3779) * Make Wallet Mist compatible #3282 * Owners icons on load * Fix oversized logo on load * Don't fetch registry twice (even when pending) * Better logging... * Better contract view : show if no events // show loading events * Better decimal typed input * PR grumble --- js/src/contracts/registry.js | 38 +++++---- .../modals/CreateWallet/createWalletStore.js | 3 +- js/src/redux/providers/personal.js | 3 - js/src/redux/providers/signer.js | 3 - js/src/redux/providers/status.js | 3 - js/src/ui/Form/Input/input.js | 38 +++++---- js/src/ui/Form/TypedInput/typedInput.js | 52 ++++++++++--- js/src/ui/Page/page.css | 6 +- js/src/util/wallets.js | 77 ++++++++++++++++++- js/src/views/Accounts/Summary/summary.js | 11 ++- js/src/views/Application/TabBar/tabBar.js | 2 +- js/src/views/Contract/Events/events.js | 34 ++++++-- js/src/views/Contract/Queries/queries.js | 4 + js/src/views/Contract/contract.js | 24 ++++-- js/src/views/Wallet/Details/details.js | 4 +- 15 files changed, 228 insertions(+), 74 deletions(-) diff --git a/js/src/contracts/registry.js b/js/src/contracts/registry.js index 2f61f7f4a..9354a59e5 100644 --- a/js/src/contracts/registry.js +++ b/js/src/contracts/registry.js @@ -19,7 +19,10 @@ import * as abis from './abi'; export default class Registry { constructor (api) { this._api = api; - this._contracts = []; + + this._contracts = {}; + this._pendingContracts = {}; + this._instance = null; this._fetching = false; this._queue = []; @@ -59,20 +62,25 @@ export default class Registry { getContract (_name) { const name = _name.toLowerCase(); - return new Promise((resolve, reject) => { - if (this._contracts[name]) { - resolve(this._contracts[name]); - return; - } + if (this._contracts[name]) { + return Promise.resolve(this._contracts[name]); + } - this - .lookupAddress(name) - .then((address) => { - this._contracts[name] = this._api.newContract(abis[name], address); - resolve(this._contracts[name]); - }) - .catch(reject); - }); + if (this._pendingContracts[name]) { + return this._pendingContracts[name]; + } + + const promise = this + .lookupAddress(name) + .then((address) => { + this._contracts[name] = this._api.newContract(abis[name], address); + delete this._pendingContracts[name]; + return this._contracts[name]; + }); + + this._pendingContracts[name] = promise; + + return promise; } getContractInstance (_name) { @@ -89,7 +97,7 @@ export default class Registry { return instance.getAddress.call({}, [sha3, 'A']); }) .then((address) => { - console.log('lookupAddress', name, sha3, address); + console.log('[lookupAddress]', `(${sha3}) ${name}: ${address}`); return address; }); } diff --git a/js/src/modals/CreateWallet/createWalletStore.js b/js/src/modals/CreateWallet/createWalletStore.js index 3edf8f638..b28bfbd26 100644 --- a/js/src/modals/CreateWallet/createWalletStore.js +++ b/js/src/modals/CreateWallet/createWalletStore.js @@ -23,6 +23,7 @@ import { wallet as walletAbi } from '~/contracts/abi'; import { wallet as walletCode, walletLibraryRegKey, fullWalletCode } from '~/contracts/code/wallet'; import { validateUint, validateAddress, validateName } from '~/util/validation'; +import { toWei } from '~/api/util/wei'; import WalletsUtils from '~/util/wallets'; const STEPS = { @@ -47,7 +48,7 @@ export default class CreateWalletStore { address: '', owners: [], required: 1, - daylimit: 0, + daylimit: toWei(1), name: '', description: '' diff --git a/js/src/redux/providers/personal.js b/js/src/redux/providers/personal.js index e061051b0..7629c4f46 100644 --- a/js/src/redux/providers/personal.js +++ b/js/src/redux/providers/personal.js @@ -36,9 +36,6 @@ export default class Personal { } this._store.dispatch(personalAccountsInfo(accountsInfo)); - }) - .then((subscriptionId) => { - console.log('personal._subscribeAccountsInfo', 'subscriptionId', subscriptionId); }); } diff --git a/js/src/redux/providers/signer.js b/js/src/redux/providers/signer.js index 5ece371c2..11b30c6b4 100644 --- a/js/src/redux/providers/signer.js +++ b/js/src/redux/providers/signer.js @@ -34,9 +34,6 @@ export default class Signer { } this._store.dispatch(signerRequestsToConfirm(pending || [])); - }) - .then((subscriptionId) => { - console.log('signer._subscribeRequestsToConfirm', 'subscriptionId', subscriptionId); }); } } diff --git a/js/src/redux/providers/status.js b/js/src/redux/providers/status.js index 936fe3f25..830192bbe 100644 --- a/js/src/redux/providers/status.js +++ b/js/src/redux/providers/status.js @@ -59,9 +59,6 @@ export default class Status { .catch((error) => { console.warn('status._subscribeBlockNumber', 'getBlockByNumber', error); }); - }) - .then((subscriptionId) => { - console.log('status._subscribeBlockNumber', 'subscriptionId', subscriptionId); }); } diff --git a/js/src/ui/Form/Input/input.js b/js/src/ui/Form/Input/input.js index 701851ef9..f25ce207c 100644 --- a/js/src/ui/Form/Input/input.js +++ b/js/src/ui/Form/Input/input.js @@ -113,32 +113,38 @@ export default class Input extends Component { <TextField autoComplete='off' className={ className } - style={ textFieldStyle } - - readOnly={ readOnly } - errorText={ error } + floatingLabelFixed floatingLabelText={ label } - fullWidth + hintText={ hint } + id={ NAME_ID } + inputStyle={ inputStyle } + fullWidth + + max={ max } + min={ min } + multiLine={ multiLine } name={ NAME_ID } - id={ NAME_ID } - rows={ rows } - type={ type || 'text' } - underlineDisabledStyle={ UNDERLINE_DISABLED } - underlineStyle={ readOnly ? UNDERLINE_READONLY : UNDERLINE_NORMAL } - underlineFocusStyle={ readOnly ? { display: 'none' } : null } - underlineShow={ !hideUnderline } - value={ value } + onBlur={ this.onBlur } onChange={ this.onChange } onKeyDown={ this.onKeyDown } onPaste={ this.onPaste } - inputStyle={ inputStyle } - min={ min } - max={ max } + + readOnly={ readOnly } + rows={ rows } + style={ textFieldStyle } + type={ type || 'text' } + + underlineDisabledStyle={ UNDERLINE_DISABLED } + underlineStyle={ readOnly ? UNDERLINE_READONLY : UNDERLINE_NORMAL } + underlineFocusStyle={ readOnly ? { display: 'none' } : null } + underlineShow={ !hideUnderline } + + value={ value } > { children } </TextField> diff --git a/js/src/ui/Form/TypedInput/typedInput.js b/js/src/ui/Form/TypedInput/typedInput.js index a81ec7b79..a54032999 100644 --- a/js/src/ui/Form/TypedInput/typedInput.js +++ b/js/src/ui/Form/TypedInput/typedInput.js @@ -53,13 +53,13 @@ export default class TypedInput extends Component { }; state = { - isEth: true, + isEth: false, ethValue: 0 }; - componentDidMount () { + componentWillMount () { if (this.props.isEth && this.props.value) { - this.setState({ ethValue: fromWei(this.props.value) }); + this.setState({ isEth: true, ethValue: fromWei(this.props.value) }); } } @@ -164,28 +164,32 @@ export default class TypedInput extends Component { } if (type === ABI_TYPES.INT) { - return this.renderNumber(); + return this.renderEth(); } if (type === ABI_TYPES.FIXED) { - return this.renderNumber(); + return this.renderFloat(); } return this.renderDefault(); } renderEth () { - const { ethValue } = this.state; + const { ethValue, isEth } = this.state; const value = ethValue && typeof ethValue.toNumber === 'function' ? ethValue.toNumber() : ethValue; + const input = isEth + ? this.renderFloat(value, this.onEthValueChange) + : this.renderInteger(value, this.onEthValueChange); + return ( <div className={ styles.ethInput }> <div className={ styles.input }> - { this.renderNumber(value, this.onEthValueChange) } - { this.state.isEth ? (<div className={ styles.label }>ETH</div>) : null } + { input } + { isEth ? (<div className={ styles.label }>ETH</div>) : null } </div> <div className={ styles.toggle }> <Toggle @@ -198,8 +202,9 @@ export default class TypedInput extends Component { ); } - renderNumber (value = this.props.value, onChange = this.onChange) { + renderInteger (value = this.props.value, onChange = this.onChange) { const { label, error, param, hint, min, max } = this.props; + const realValue = value && typeof value.toNumber === 'function' ? value.toNumber() : value; @@ -212,6 +217,35 @@ export default class TypedInput extends Component { error={ error } onChange={ onChange } type='number' + step={ 1 } + min={ min !== null ? min : (param.signed ? null : 0) } + max={ max !== null ? max : null } + /> + ); + } + + /** + * Decimal numbers have to be input via text field + * because of some react issues with input number fields. + * Once the issue is fixed, this could be a number again. + * + * @see https://github.com/facebook/react/issues/1549 + */ + renderFloat (value = this.props.value, onChange = this.onChange) { + const { label, error, param, hint, min, max } = this.props; + + const realValue = value && typeof value.toNumber === 'function' + ? value.toNumber() + : value; + + return ( + <Input + label={ label } + hint={ hint } + value={ realValue } + error={ error } + onChange={ onChange } + type='text' min={ min !== null ? min : (param.signed ? null : 0) } max={ max !== null ? max : null } /> diff --git a/js/src/ui/Page/page.css b/js/src/ui/Page/page.css index 9b7cfd62b..72a78dc22 100644 --- a/js/src/ui/Page/page.css +++ b/js/src/ui/Page/page.css @@ -17,8 +17,8 @@ .layout { padding: 0.25em 0.25em 1em 0.25em; -} -.layout>div { - padding-bottom: 0.75em; + > * { + margin-bottom: 0.75em; + } } diff --git a/js/src/util/wallets.js b/js/src/util/wallets.js index c335ce03c..1f0bc6735 100644 --- a/js/src/util/wallets.js +++ b/js/src/util/wallets.js @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -import { range } from 'lodash'; +import { range, uniq } from 'lodash'; import { bytesToHex, toHex } from '~/api/util/format'; +import { validateAddress } from '~/util/validation'; export default class WalletsUtils { @@ -26,10 +27,82 @@ export default class WalletsUtils { static fetchOwners (walletContract) { const walletInstance = walletContract.instance; + return walletInstance .m_numOwners.call() .then((mNumOwners) => { - return Promise.all(range(mNumOwners.toNumber()).map((idx) => walletInstance.getOwner.call({}, [ idx ]))); + const promises = range(mNumOwners.toNumber()) + .map((idx) => walletInstance.getOwner.call({}, [ idx ])); + + return Promise + .all(promises) + .then((owners) => { + const uniqOwners = uniq(owners); + + // If all owners are the zero account : must be Mist wallet contract + if (uniqOwners.length === 1 && /^(0x)?0*$/.test(owners[0])) { + return WalletsUtils.fetchMistOwners(walletContract, mNumOwners.toNumber()); + } + + return owners; + }); + }); + } + + static fetchMistOwners (walletContract, mNumOwners) { + const walletAddress = walletContract.address; + + return WalletsUtils + .getMistOwnersOffset(walletContract) + .then((result) => { + if (!result || result.offset === -1) { + return []; + } + + const owners = [ result.address ]; + + if (mNumOwners === 1) { + return owners; + } + + const initOffset = result.offset + 1; + let promise = Promise.resolve(); + + range(initOffset, initOffset + mNumOwners - 1).forEach((offset) => { + promise = promise + .then(() => { + return walletContract.api.eth.getStorageAt(walletAddress, offset); + }) + .then((result) => { + const resultAddress = '0x' + (result || '').slice(-40); + const { address } = validateAddress(resultAddress); + + owners.push(address); + }); + }); + + return promise.then(() => owners); + }); + } + + static getMistOwnersOffset (walletContract, offset = 3) { + return walletContract.api.eth + .getStorageAt(walletContract.address, offset) + .then((result) => { + if (result && !/^(0x)?0*$/.test(result)) { + const resultAddress = '0x' + result.slice(-40); + const { address, addressError } = validateAddress(resultAddress); + + if (!addressError) { + return { offset, address }; + } + } + + if (offset >= 100) { + return { offset: -1 }; + } + + return WalletsUtils.getMistOwnersOffset(walletContract, offset + 1); }); } diff --git a/js/src/views/Accounts/Summary/summary.js b/js/src/views/Accounts/Summary/summary.js index aeff8a2e5..3183a2903 100644 --- a/js/src/views/Accounts/Summary/summary.js +++ b/js/src/views/Accounts/Summary/summary.js @@ -75,6 +75,13 @@ export default class Summary extends Component { return true; } + const prevOwners = this.props.owners; + const nextOwners = nextProps.owners; + + if (!isEqual(prevOwners, nextOwners)) { + return true; + } + return false; } @@ -123,8 +130,8 @@ export default class Summary extends Component { return ( <div className={ styles.owners }> { - ownersValid.map((owner) => ( - <div key={ owner.address }> + ownersValid.map((owner, index) => ( + <div key={ `${index}_${owner.address}` }> <div data-tip data-for={ `owner_${owner.address}` } diff --git a/js/src/views/Application/TabBar/tabBar.js b/js/src/views/Application/TabBar/tabBar.js index 8a79f216e..9d9f55874 100644 --- a/js/src/views/Application/TabBar/tabBar.js +++ b/js/src/views/Application/TabBar/tabBar.js @@ -188,7 +188,7 @@ class TabBar extends Component { return ( <ToolbarGroup> <div className={ styles.logo }> - <img src={ imagesEthcoreBlock } /> + <img src={ imagesEthcoreBlock } height={ 28 } /> </div> </ToolbarGroup> ); diff --git a/js/src/views/Contract/Events/events.js b/js/src/views/Contract/Events/events.js index 69ae8fd6a..c29e624bf 100644 --- a/js/src/views/Contract/Events/events.js +++ b/js/src/views/Contract/Events/events.js @@ -17,7 +17,7 @@ import React, { Component, PropTypes } from 'react'; import { uniq } from 'lodash'; -import { Container } from '~/ui'; +import { Container, Loading } from '~/ui'; import Event from './Event'; import styles from '../contract.css'; @@ -25,18 +25,38 @@ import styles from '../contract.css'; export default class Events extends Component { static contextTypes = { api: PropTypes.object - } + }; static propTypes = { - events: PropTypes.array, - isTest: PropTypes.bool.isRequired - } + isTest: PropTypes.bool.isRequired, + isLoading: PropTypes.bool, + events: PropTypes.array + }; + + static defaultProps = { + isLoading: false, + events: [] + }; render () { - const { events, isTest } = this.props; + const { events, isTest, isLoading } = this.props; + + if (isLoading) { + return ( + <Container title='events'> + <div> + <Loading size={ 2 } /> + </div> + </Container> + ); + } if (!events || !events.length) { - return null; + return ( + <Container title='events'> + <p>No events has been sent from this contract.</p> + </Container> + ); } const eventsKey = uniq(events.map((e) => e.key)); diff --git a/js/src/views/Contract/Queries/queries.js b/js/src/views/Contract/Queries/queries.js index 9a13037f6..1bfd2fa5f 100644 --- a/js/src/views/Contract/Queries/queries.js +++ b/js/src/views/Contract/Queries/queries.js @@ -54,6 +54,10 @@ export default class Queries extends Component { .filter((fn) => fn.inputs.length > 0) .map((fn) => this.renderInputQuery(fn)); + if (queries.length + noInputQueries.length + withInputQueries.length === 0) { + return null; + } + return ( <Container title='queries'> <div className={ styles.methods }> diff --git a/js/src/views/Contract/contract.js b/js/src/views/Contract/contract.js index 35ad95fe2..4f7570ebd 100644 --- a/js/src/views/Contract/contract.js +++ b/js/src/views/Contract/contract.js @@ -40,7 +40,7 @@ import styles from './contract.css'; class Contract extends Component { static contextTypes = { api: React.PropTypes.object.isRequired - } + }; static propTypes = { setVisibleAccounts: PropTypes.func.isRequired, @@ -50,7 +50,7 @@ class Contract extends Component { contracts: PropTypes.object, isTest: PropTypes.bool, params: PropTypes.object - } + }; state = { contract: null, @@ -64,8 +64,9 @@ class Contract extends Component { allEvents: [], minedEvents: [], pendingEvents: [], - queryValues: {} - } + queryValues: {}, + loadingEvents: true + }; componentDidMount () { const { api } = this.context; @@ -115,7 +116,7 @@ class Contract extends Component { render () { const { balances, contracts, params, isTest } = this.props; - const { allEvents, contract, queryValues } = this.state; + const { allEvents, contract, queryValues, loadingEvents } = this.state; const account = contracts[params.address]; const balance = balances[params.address]; @@ -134,12 +135,17 @@ class Contract extends Component { account={ account } balance={ balance } /> + <Queries contract={ contract } - values={ queryValues } /> + values={ queryValues } + /> + <Events isTest={ isTest } - events={ allEvents } /> + isLoading={ loadingEvents } + events={ allEvents } + /> { this.renderDetails(account) } </Page> @@ -358,6 +364,10 @@ class Contract extends Component { } _receiveEvents = (error, logs) => { + if (this.state.loadingEvents) { + this.setState({ loadingEvents: false }); + } + if (error) { console.error('_receiveEvents', error); return; diff --git a/js/src/views/Wallet/Details/details.js b/js/src/views/Wallet/Details/details.js index 547b02601..fb08bbde2 100644 --- a/js/src/views/Wallet/Details/details.js +++ b/js/src/views/Wallet/Details/details.js @@ -55,9 +55,9 @@ export default class WalletDetails extends Component { return null; } - const ownersList = owners.map((address) => ( + const ownersList = owners.map((address, idx) => ( <InputAddress - key={ address } + key={ `${idx}_${address}` } value={ address } disabled text From b9c04fcd0090a93fe0365a5d5bc2dafac184d6e4 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 01:26:47 +0100 Subject: [PATCH 36/74] Fixes to the Wallet UI (#3787) * Correct number of transactions for contracts * Remove daily limit info if set to 0 * Hide tx count for contracts --- js/src/views/Account/Header/header.js | 10 ++++++---- js/src/views/Contract/contract.js | 1 + js/src/views/Wallet/wallet.js | 9 ++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/js/src/views/Account/Header/header.js b/js/src/views/Account/Header/header.js index b126951b2..6ce88aef4 100644 --- a/js/src/views/Account/Header/header.js +++ b/js/src/views/Account/Header/header.js @@ -31,12 +31,14 @@ export default class Header extends Component { account: PropTypes.object, balance: PropTypes.object, className: PropTypes.string, - children: PropTypes.node + children: PropTypes.node, + isContract: PropTypes.bool }; static defaultProps = { className: '', - children: null + children: null, + isContract: false }; render () { @@ -88,9 +90,9 @@ export default class Header extends Component { } renderTxCount () { - const { balance } = this.props; + const { balance, isContract } = this.props; - if (!balance) { + if (!balance || isContract) { return null; } diff --git a/js/src/views/Contract/contract.js b/js/src/views/Contract/contract.js index 4f7570ebd..e501e4fe5 100644 --- a/js/src/views/Contract/contract.js +++ b/js/src/views/Contract/contract.js @@ -134,6 +134,7 @@ class Contract extends Component { <Header account={ account } balance={ balance } + isContract /> <Queries diff --git a/js/src/views/Wallet/wallet.js b/js/src/views/Wallet/wallet.js index e67eaefdb..1a5fbeecd 100644 --- a/js/src/views/Wallet/wallet.js +++ b/js/src/views/Wallet/wallet.js @@ -127,6 +127,7 @@ class Wallet extends Component { className={ styles.header } account={ wallet } balance={ balance } + isContract > { this.renderInfos() } </Header> @@ -152,7 +153,13 @@ class Wallet extends Component { return null; } - const limit = api.util.fromWei(dailylimit.limit).toFormat(3); + const _limit = api.util.fromWei(dailylimit.limit); + + if (_limit.equals(0)) { + return null; + } + + const limit = _limit.toFormat(3); const spent = api.util.fromWei(dailylimit.spent).toFormat(3); const date = moment(dailylimit.last.toNumber() * 24 * 3600 * 1000); From f6564dcc2f61f892d9a85f057209db5f802cf5b0 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 01:32:39 +0100 Subject: [PATCH 37/74] Fix dapps separation --- js/src/views/Dapps/dapps.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/js/src/views/Dapps/dapps.js b/js/src/views/Dapps/dapps.js index cf847202a..b0b731b93 100644 --- a/js/src/views/Dapps/dapps.js +++ b/js/src/views/Dapps/dapps.js @@ -72,9 +72,17 @@ export default class Dapps extends Component { ] } /> <Page> - { this.renderList(this.store.visibleLocal) } - { this.renderList(this.store.visibleBuiltin) } - { this.renderList(this.store.visibleNetwork, externalOverlay) } + <div> + { this.renderList(this.store.visibleLocal) } + </div> + + <div> + { this.renderList(this.store.visibleBuiltin) } + </div> + + <div> + { this.renderList(this.store.visibleNetwork, externalOverlay) } + </div> </Page> </div> ); From 0d9b1882a31346ab5ab831ea373767c22723b2f9 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 01:56:38 +0100 Subject: [PATCH 38/74] Treat tabs as real link (enable Ctrl+Click for new Tab) --- js/src/views/Application/TabBar/tabBar.css | 31 +++++++---- js/src/views/Application/TabBar/tabBar.js | 64 +++++----------------- 2 files changed, 33 insertions(+), 62 deletions(-) diff --git a/js/src/views/Application/TabBar/tabBar.css b/js/src/views/Application/TabBar/tabBar.css index b11df4e40..7b74622d3 100644 --- a/js/src/views/Application/TabBar/tabBar.css +++ b/js/src/views/Application/TabBar/tabBar.css @@ -30,24 +30,31 @@ } } -.tabs button, +.tabLink { + display: flex; + + > * { + flex: 1; + } + + &:hover { + background: rgba(0, 0, 0, 0.4) !important; + } + + &.tabactive, &.tabactive:hover { + color: white !important; + background: rgba(0, 0, 0, 0.25) !important; + border-radius: 4px 4px 0 0; + } +} + +.tabLink, .settings, .logo, .last { background: rgba(0, 0, 0, 0.5) !important; /* rgba(0, 0, 0, 0.25) !important; */ } -.tabs button:hover { - background: rgba(0, 0, 0, 0.4) !important; -} - -button.tabactive, -button.tabactive:hover { - color: white !important; - background: rgba(0, 0, 0, 0.25) !important; - border-radius: 4px 4px 0 0; -} - .tabbarTooltip { left: 3.3em; top: 0.5em; diff --git a/js/src/views/Application/TabBar/tabBar.js b/js/src/views/Application/TabBar/tabBar.js index 9d9f55874..ccf1290c5 100644 --- a/js/src/views/Application/TabBar/tabBar.js +++ b/js/src/views/Application/TabBar/tabBar.js @@ -16,6 +16,7 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; +import { Link } from 'react-router'; import { bindActionCreators } from 'redux'; import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar'; import { Tab as MUITab } from 'material-ui/Tabs'; @@ -40,8 +41,7 @@ class Tab extends Component { active: PropTypes.bool, view: PropTypes.object, children: PropTypes.node, - pendings: PropTypes.number, - onChange: PropTypes.func + pendings: PropTypes.number }; shouldComponentUpdate (nextProps) { @@ -60,7 +60,6 @@ class Tab extends Component { selected={ active } icon={ view.icon } label={ label } - onTouchTap={ this.handleClick } > { children } </MUITab> @@ -118,11 +117,6 @@ class Tab extends Component { return this.renderLabel(label, null); } - - handleClick = () => { - const { onChange, view } = this.props; - onChange(view); - } } class TabBar extends Component { @@ -142,34 +136,12 @@ class TabBar extends Component { pending: [] }; - state = { - activeViewId: '' - }; - - setActiveView (props = this.props) { - const { hash, views } = props; - const view = views.find((view) => view.value === hash); - - this.setState({ activeViewId: view.id }); - } - - componentWillMount () { - this.setActiveView(); - } - - componentWillReceiveProps (nextProps) { - if (nextProps.hash !== this.props.hash) { - this.setActiveView(nextProps); - } - } - shouldComponentUpdate (nextProps, nextState) { const prevViews = this.props.views.map((v) => v.id).sort(); const nextViews = nextProps.views.map((v) => v.id).sort(); return (nextProps.hash !== this.props.hash) || (nextProps.pending.length !== this.props.pending.length) || - (nextState.activeViewId !== this.state.activeViewId) || (!isEqual(prevViews, nextViews)); } @@ -206,7 +178,6 @@ class TabBar extends Component { renderTabs () { const { views, pending } = this.props; - const { activeViewId } = this.state; const items = views .map((view, index) => { @@ -216,36 +187,29 @@ class TabBar extends Component { ) : null; - const active = activeViewId === view.id; - return ( - <Tab - active={ active } - view={ view } - onChange={ this.onChange } + <Link key={ view.id } - pendings={ pending.length } + to={ view.route } + activeClassName={ styles.tabactive } + className={ styles.tabLink } > - { body } - </Tab> + <Tab + view={ view } + pendings={ pending.length } + > + { body } + </Tab> + </Link> ); }); return ( - <div - className={ styles.tabs } - onChange={ this.onChange }> + <div className={ styles.tabs }> { items } </div> ); } - - onChange = (view) => { - const { router } = this.context; - - router.push(view.route); - this.setState({ activeViewId: view.id }); - } } function mapStateToProps (state) { From 5f09eb9d04e238e9097f37468d68c4dc9ab93bd6 Mon Sep 17 00:00:00 2001 From: keorn <pczaban@gmail.com> Date: Sat, 10 Dec 2016 10:38:10 +0100 Subject: [PATCH 39/74] update tests to new spec (#3790) --- ethcore/src/engines/authority_round.rs | 28 ++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 21a6e4761..98190c1ea 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -347,7 +347,6 @@ mod tests { use tests::helpers::*; use account_provider::AccountProvider; use spec::Spec; - use std::time::UNIX_EPOCH; #[test] fn has_valid_metadata() { @@ -442,13 +441,30 @@ mod tests { let engine = Spec::new_test_round().engine; let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); - let time = UNIX_EPOCH.elapsed().unwrap().as_secs(); // Two authorities. - let mut step = time - time % 2; - header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); + // Spec starts with step 2. + header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); assert!(engine.verify_block_seal(&header).is_err()); - step = step + 1; - header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); + header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); assert!(engine.verify_block_seal(&header).is_ok()); } + + #[test] + fn rejects_future_block() { + let mut header: Header = Header::default(); + let tap = AccountProvider::transient_provider(); + let addr = tap.insert_account("0".sha3(), "0").unwrap(); + + header.set_author(addr); + + let engine = Spec::new_test_round().engine; + + let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); + // Two authorities. + // Spec starts with step 2. + header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); + assert!(engine.verify_block_seal(&header).is_ok()); + header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); + assert!(engine.verify_block_seal(&header).is_err()); + } } From 6eb63a7316c3605c107a8288ce36b641ab2ea23d Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jacogr@gmail.com> Date: Sat, 10 Dec 2016 10:49:39 +0100 Subject: [PATCH 40/74] Update CI builds (#3780) * Only run languages tests in appropriate areas * Drop echo --- .gitlab-ci.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d27b58f9a..6df465035 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -423,12 +423,8 @@ test-rust-stable: before_script: - git submodule update --init --recursive - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v ^js/ | wc -l) - - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - - echo "rust/js modified: $RUST_FILES_MODIFIED / $JS_FILES_MODIFIED" - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - export RUST_BACKTRACE=1 - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi - if [ "$RUST_FILES_MODIFIED" = 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi tags: - rust @@ -439,11 +435,8 @@ js-test: before_script: - git submodule update --init --recursive - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - - echo $JS_FILES_MODIFIED - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - - export RUST_BACKTRACE=1 - - echo $JS_FILES_MODIFIED - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi tags: - rust From 51b9034a5e7a0839945e79e237d154f470c20466 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 12:44:48 +0100 Subject: [PATCH 41/74] Don't show addresses while loading balances (fix flash of unsorted) --- js/src/views/Addresses/addresses.js | 37 +++++++++++++++++++---------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/js/src/views/Addresses/addresses.js b/js/src/views/Addresses/addresses.js index 5e0ed4e18..a3e52ec55 100644 --- a/js/src/views/Addresses/addresses.js +++ b/js/src/views/Addresses/addresses.js @@ -23,7 +23,7 @@ import { uniq, isEqual } from 'lodash'; import List from '../Accounts/List'; import Summary from '../Accounts/Summary'; import { AddAddress } from '~/modals'; -import { Actionbar, ActionbarExport, ActionbarImport, ActionbarSearch, ActionbarSort, Button, Page } from '~/ui'; +import { Actionbar, ActionbarExport, ActionbarImport, ActionbarSearch, ActionbarSort, Button, Page, Loading } from '~/ui'; import { setVisibleAccounts } from '~/redux/providers/personalActions'; import styles from './addresses.css'; @@ -72,27 +72,40 @@ class Addresses extends Component { } render () { - const { balances, contacts, hasContacts } = this.props; - const { searchValues, sortOrder } = this.state; - return ( <div> { this.renderActionbar() } { this.renderAddAddress() } <Page> - <List - link='address' - search={ searchValues } - accounts={ contacts } - balances={ balances } - empty={ !hasContacts } - order={ sortOrder } - handleAddSearchToken={ this.onAddSearchToken } /> + { this.renderAccountsList() } </Page> </div> ); } + renderAccountsList () { + const { balances, contacts, hasContacts } = this.props; + const { searchValues, sortOrder } = this.state; + + if (hasContacts && Object.keys(balances).length === 0) { + return ( + <Loading size={ 3 } /> + ); + } + + return ( + <List + link='address' + search={ searchValues } + accounts={ contacts } + balances={ balances } + empty={ !hasContacts } + order={ sortOrder } + handleAddSearchToken={ this.onAddSearchToken } + /> + ); + } + renderSortButton () { const onChange = (sortOrder) => { this.setState({ sortOrder }); From 7eb30b12494a257b1ea35c38d6ba38565d5e1b81 Mon Sep 17 00:00:00 2001 From: Gav Wood <gavin@ethcore.io> Date: Sat, 10 Dec 2016 13:52:43 +0100 Subject: [PATCH 42/74] Fix build. --- ethcore/light/src/net/tests/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 6e2bc9f33..29c1a819a 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -19,7 +19,7 @@ use ethcore::blockchain_info::BlockChainInfo; use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; -use ethcore::ids::BlockID; +use ethcore::ids::BlockId; use ethcore::transaction::SignedTransaction; use network::PeerId; From 0977b82eeb5b647e2c2b5b5a204c1f8620a6105f Mon Sep 17 00:00:00 2001 From: Gav Wood <gavin@ethcore.io> Date: Sat, 10 Dec 2016 13:54:17 +0100 Subject: [PATCH 43/74] More fixes. --- ethcore/light/src/net/tests/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 29c1a819a..876432ce2 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -94,7 +94,7 @@ impl Provider for TestProvider { let best_num = self.0.client.chain_info().best_block_number; let start_num = req.block_num; - match self.0.client.block_hash(BlockID::Number(req.block_num)) { + match self.0.client.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)); @@ -106,7 +106,7 @@ impl Provider for TestProvider { .map(|x: u64| x.saturating_mul(req.skip + 1)) .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.0.client.block_header(BlockID::Number(x))) + .map(|x| self.0.client.block_header(BlockId::Number(x))) .take_while(|x| x.is_some()) .flat_map(|x| x) .collect() @@ -114,7 +114,7 @@ impl Provider for TestProvider { fn block_bodies(&self, req: request::Bodies) -> Vec<Bytes> { req.block_hashes.into_iter() - .map(|hash| self.0.client.block_body(BlockID::Hash(hash))) + .map(|hash| self.0.client.block_body(BlockId::Hash(hash))) .map(|body| body.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) .collect() } @@ -285,7 +285,7 @@ fn get_block_headers() { let request = Headers { block_num: 1, - block_hash: provider.client.block_hash(BlockID::Number(1)).unwrap(), + block_hash: provider.client.block_hash(BlockId::Number(1)).unwrap(), max: 10, skip: 0, reverse: false, @@ -294,7 +294,7 @@ fn get_block_headers() { let request_body = encode_request(&Request::Headers(request.clone()), req_id); let response = { - let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockID::Number(i + 1)).unwrap()).collect(); + let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockId::Number(i + 1)).unwrap()).collect(); assert_eq!(headers.len(), 10); let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Headers, 10); @@ -334,14 +334,14 @@ fn get_block_bodies() { } let request = request::Bodies { - block_hashes: (0..10).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()).collect(), + block_hashes: (0..10).map(|i| provider.client.block_hash(BlockId::Number(i)).unwrap()).collect(), }; let req_id = 111; let request_body = encode_request(&Request::Bodies(request.clone()), req_id); let response = { - let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockID::Number(i + 1)).unwrap()).collect(); + let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockId::Number(i + 1)).unwrap()).collect(); assert_eq!(bodies.len(), 10); let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10); @@ -382,7 +382,7 @@ fn get_block_receipts() { // find the first 10 block hashes starting with `f` because receipts are only provided // by the test client in that case. - let block_hashes: Vec<_> = (0..1000).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()) + let block_hashes: Vec<_> = (0..1000).map(|i| provider.client.block_hash(BlockId::Number(i)).unwrap()) .filter(|hash| format!("{}", hash).starts_with("f")).take(10).collect(); let request = request::Receipts { From 591d086f42d4ba5e92936df277f14054e42050d2 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 14:19:15 +0100 Subject: [PATCH 44/74] Better use of React-Router (maintaining old routes) --- js/src/main.js | 59 +++++++++++++++++++----- js/src/views/Accounts/Summary/summary.js | 2 +- js/src/views/Addresses/addresses.js | 2 +- js/src/views/Contracts/contracts.js | 2 +- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/js/src/main.js b/js/src/main.js index d508c50fc..c1dda9d57 100644 --- a/js/src/main.js +++ b/js/src/main.js @@ -15,7 +15,7 @@ // along with Parity. If not, see <http://www.gnu.org/licenses/>. import React, { Component, PropTypes } from 'react'; -import { Redirect, Router, Route } from 'react-router'; +import { Redirect, Router, Route, IndexRoute } from 'react-router'; import { Accounts, Account, Addresses, Address, Application, Contract, Contracts, WriteContract, Wallet, Dapp, Dapps, Settings, SettingsBackground, SettingsParity, SettingsProxy, SettingsViews, Signer, Status } from '~/views'; @@ -26,6 +26,23 @@ export default class MainApplication extends Component { routerHistory: PropTypes.any.isRequired }; + handleDeprecatedRoute = (nextState, replace) => { + const { address } = nextState.params; + const redirectMap = { + account: 'accounts', + address: 'addresses', + contract: 'contracts' + }; + + const oldRoute = nextState.routes[0].path; + const newRoute = Object.keys(redirectMap).reduce((newRoute, key) => { + return newRoute.replace(new RegExp(`^/${key}`), '/' + redirectMap[key]); + }, oldRoute); + + console.warn(`Route "${oldRoute}" is deprecated. Please use "${newRoute}"`); + replace(newRoute.replace(':address', address)); + } + render () { const { routerHistory } = this.props; @@ -34,26 +51,46 @@ export default class MainApplication extends Component { <Redirect from='/' to='/accounts' /> <Redirect from='/auth' to='/accounts' query={ {} } /> <Redirect from='/settings' to='/settings/views' /> + + { /** Backward Compatible links */ } + <Route path='/account/:address' onEnter={ this.handleDeprecatedRoute } /> + <Route path='/address/:address' onEnter={ this.handleDeprecatedRoute } /> + <Route path='/contract/:address' onEnter={ this.handleDeprecatedRoute } /> + <Route path='/' component={ Application }> - <Route path='accounts' component={ Accounts } /> - <Route path='account/:address' component={ Account } /> - <Route path='wallet/:address' component={ Wallet } /> - <Route path='addresses' component={ Addresses } /> - <Route path='address/:address' component={ Address } /> + <Route path='accounts'> + <IndexRoute component={ Accounts } /> + <Route path=':address' component={ Account } /> + <Route path='/wallet/:address' component={ Wallet } /> + </Route> + + <Route path='addresses'> + <IndexRoute component={ Addresses } /> + <Route path=':address' component={ Address } /> + </Route> + <Route path='apps' component={ Dapps } /> <Route path='app/:id' component={ Dapp } /> - <Route path='contracts' component={ Contracts } /> - <Route path='contracts/write' component={ WriteContract } /> - <Route path='contract/:address' component={ Contract } /> + + <Route path='contracts'> + <IndexRoute component={ Contracts } /> + <Route path='write' component={ WriteContract } /> + <Route path=':address' component={ Contract } /> + </Route> + <Route path='settings' component={ Settings }> <Route path='background' component={ SettingsBackground } /> <Route path='proxy' component={ SettingsProxy } /> <Route path='views' component={ SettingsViews } /> <Route path='parity' component={ SettingsParity } /> </Route> + <Route path='signer' component={ Signer } /> - <Route path='status' component={ Status } /> - <Route path='status/:subpage' component={ Status } /> + + <Route path='status'> + <IndexRoute component={ Status } /> + <Route path=':subpage' component={ Status } /> + </Route> </Route> </Router> ); diff --git a/js/src/views/Accounts/Summary/summary.js b/js/src/views/Accounts/Summary/summary.js index 3183a2903..a19b9a9de 100644 --- a/js/src/views/Accounts/Summary/summary.js +++ b/js/src/views/Accounts/Summary/summary.js @@ -153,7 +153,7 @@ export default class Summary extends Component { const { link, noLink, account, name } = this.props; const { address } = account; - const viewLink = `/${link || 'account'}/${address}`; + const viewLink = `/${link || 'accounts'}/${address}`; const content = ( <IdentityName address={ address } name={ name } unknown /> diff --git a/js/src/views/Addresses/addresses.js b/js/src/views/Addresses/addresses.js index a3e52ec55..fd26d94e5 100644 --- a/js/src/views/Addresses/addresses.js +++ b/js/src/views/Addresses/addresses.js @@ -95,7 +95,7 @@ class Addresses extends Component { return ( <List - link='address' + link='addresses' search={ searchValues } accounts={ contacts } balances={ balances } diff --git a/js/src/views/Contracts/contracts.js b/js/src/views/Contracts/contracts.js index b84705b32..524954f80 100644 --- a/js/src/views/Contracts/contracts.js +++ b/js/src/views/Contracts/contracts.js @@ -85,7 +85,7 @@ class Contracts extends Component { { this.renderDeployContract() } <Page> <List - link='contract' + link='contracts' search={ searchValues } accounts={ contracts } balances={ balances } From 02c788a40378e7690a11ca288ef4e372cad79fd2 Mon Sep 17 00:00:00 2001 From: arkpar <arkady.paronyan@gmail.com> Date: Sat, 10 Dec 2016 14:20:34 +0100 Subject: [PATCH 45/74] Network connectivity fixes --- ethcore/res/ethereum/frontier.json | 3 --- sync/src/chain.rs | 2 +- sync/src/sync_io.rs | 4 ++++ util/network/src/host.rs | 14 +++++++++----- util/network/src/session.rs | 8 ++++---- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/ethcore/res/ethereum/frontier.json b/ethcore/res/ethereum/frontier.json index 2c520c46a..3a9dce456 100644 --- a/ethcore/res/ethereum/frontier.json +++ b/ethcore/res/ethereum/frontier.json @@ -170,9 +170,6 @@ "enode://cadc6e573b6bc2a9128f2f635ac0db3353e360b56deef239e9be7e7fce039502e0ec670b595f6288c0d2116812516ad6b6ff8d5728ff45eba176989e40dead1e@37.128.191.230:30303", "enode://595a9a06f8b9bc9835c8723b6a82105aea5d55c66b029b6d44f229d6d135ac3ecdd3e9309360a961ea39d7bee7bac5d03564077a4e08823acc723370aace65ec@46.20.235.22:30303", "enode://029178d6d6f9f8026fc0bc17d5d1401aac76ec9d86633bba2320b5eed7b312980c0a210b74b20c4f9a8b0b2bf884b111fa9ea5c5f916bb9bbc0e0c8640a0f56c@216.158.85.185:30303", - "enode://84f5d5957b4880a8b0545e32e05472318898ad9fc8ebe1d56c90c12334a98e12351eccfdf3a2bf72432ac38b57e9d348400d17caa083879ade3822390f89773f@10.1.52.78:30303", - "enode://f90dc9b9bf7b8db97726b7849e175f1eb2707f3d8f281c929336e398dd89b0409fc6aeceb89e846278e9d3ecc3857cebfbe6758ff352ece6fe5d42921ee761db@10.1.173.87:30303", - "enode://6a868ced2dec399c53f730261173638a93a40214cf299ccf4d42a76e3fa54701db410669e8006347a4b3a74fa090bb35af0320e4bc8d04cf5b7f582b1db285f5@10.3.149.199:30303", "enode://fdd1b9bb613cfbc200bba17ce199a9490edc752a833f88d4134bf52bb0d858aa5524cb3ec9366c7a4ef4637754b8b15b5dc913e4ed9fdb6022f7512d7b63f181@212.47.247.103:30303", "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 2d53ad5ee..9f054bec8 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -1678,7 +1678,7 @@ impl ChainSync { pub fn on_packet(&mut self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { if packet_id != STATUS_PACKET && !self.peers.contains_key(&peer) { - debug!(target:"sync", "Unexpected packet from unregistered peer: {}:{}", peer, io.peer_info(peer)); + debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_info(peer)); return; } let rlp = UntrustedRlp::new(data); diff --git a/sync/src/sync_io.rs b/sync/src/sync_io.rs index 8dc8c65c0..e9e2d3396 100644 --- a/sync/src/sync_io.rs +++ b/sync/src/sync_io.rs @@ -131,6 +131,10 @@ impl<'s, 'h> SyncIo for NetSyncIo<'s, 'h> { fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { self.network.protocol_version(*protocol, peer_id).unwrap_or(0) } + + fn peer_info(&self, peer_id: PeerId) -> String { + self.network.peer_client_version(peer_id) + } } diff --git a/util/network/src/host.rs b/util/network/src/host.rs index f75158e4a..975fb87b8 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -22,7 +22,7 @@ use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::ops::*; use std::cmp::min; use std::path::{Path, PathBuf}; -use std::io::{Read, Write}; +use std::io::{Read, Write, ErrorKind}; use std::fs; use ethkey::{KeyPair, Secret, Random, Generator}; use mio::*; @@ -381,8 +381,6 @@ pub struct Host { impl Host { /// Create a new instance pub fn new(mut config: NetworkConfiguration, stats: Arc<NetworkStats>) -> Result<Host, NetworkError> { - trace!(target: "host", "Creating new Host object"); - let mut listen_address = match config.listen_address { None => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), DEFAULT_PORT)), Some(addr) => addr, @@ -405,6 +403,7 @@ impl Host { // Setup the server socket let tcp_listener = try!(TcpListener::bind(&listen_address)); listen_address = SocketAddr::new(listen_address.ip(), try!(tcp_listener.local_addr()).port()); + debug!(target: "network", "Listening at {:?}", listen_address); let udp_port = config.udp_port.unwrap_or(listen_address.port()); let local_endpoint = NodeEndpoint { address: listen_address, udp_port: udp_port }; @@ -707,7 +706,10 @@ impl Host { } }; match TcpStream::connect(&address) { - Ok(socket) => socket, + Ok(socket) => { + trace!(target: "network", "Connecting to {:?}", address); + socket + }, Err(e) => { debug!(target: "network", "Can't connect to address {:?}: {:?}", address, e); return; @@ -749,7 +751,9 @@ impl Host { let socket = match self.tcp_listener.lock().accept() { Ok((sock, _addr)) => sock, Err(e) => { - debug!(target: "network", "Error accepting connection: {:?}", e); + if e.kind() != ErrorKind::WouldBlock { + debug!(target: "network", "Error accepting connection: {:?}", e); + } break }, }; diff --git a/util/network/src/session.rs b/util/network/src/session.rs index 9c8bed9da..3aab05d9a 100644 --- a/util/network/src/session.rs +++ b/util/network/src/session.rs @@ -435,16 +435,16 @@ impl Session { // map to protocol let protocol = self.info.capabilities[i].protocol; - let pid = packet_id - self.info.capabilities[i].id_offset; + let protocol_packet_id = packet_id - self.info.capabilities[i].id_offset; match *self.protocol_states.entry(protocol).or_insert_with(|| ProtocolState::Pending(Vec::new())) { ProtocolState::Connected => { - trace!(target: "network", "Packet {} mapped to {:?}:{}, i={}, capabilities={:?}", packet_id, protocol, pid, i, self.info.capabilities); - Ok(SessionData::Packet { data: packet.data, protocol: protocol, packet_id: pid } ) + trace!(target: "network", "Packet {} mapped to {:?}:{}, i={}, capabilities={:?}", packet_id, protocol, protocol_packet_id, i, self.info.capabilities); + Ok(SessionData::Packet { data: packet.data, protocol: protocol, packet_id: protocol_packet_id } ) } ProtocolState::Pending(ref mut pending) => { trace!(target: "network", "Packet {} deferred until protocol connection event completion", packet_id); - pending.push((packet.data, packet_id)); + pending.push((packet.data, protocol_packet_id)); Ok(SessionData::Continue) } From 13607d48be99d52240912138c1cb1479d7d63abb Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 14:26:35 +0100 Subject: [PATCH 46/74] Better use of Tab Bar --- js/src/views/Application/TabBar/tabBar.js | 59 +++++++++++------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/js/src/views/Application/TabBar/tabBar.js b/js/src/views/Application/TabBar/tabBar.js index ccf1290c5..63f569f1f 100644 --- a/js/src/views/Application/TabBar/tabBar.js +++ b/js/src/views/Application/TabBar/tabBar.js @@ -17,7 +17,6 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { Link } from 'react-router'; -import { bindActionCreators } from 'redux'; import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar'; import { Tab as MUITab } from 'material-ui/Tabs'; import { isEqual } from 'lodash'; @@ -27,37 +26,24 @@ import { Badge, Tooltip } from '~/ui'; import styles from './tabBar.css'; import imagesEthcoreBlock from '../../../../assets/images/parity-logo-white-no-text.svg'; -const TABMAP = { - accounts: 'account', - wallet: 'account', - addresses: 'address', - apps: 'app', - contracts: 'contract', - deploy: 'contract' -}; - class Tab extends Component { static propTypes = { - active: PropTypes.bool, view: PropTypes.object, children: PropTypes.node, pendings: PropTypes.number }; shouldComponentUpdate (nextProps) { - return nextProps.active !== this.props.active || - (nextProps.view.id === 'signer' && nextProps.pendings !== this.props.pendings); + return (nextProps.view.id === 'signer' && nextProps.pendings !== this.props.pendings); } render () { - const { active, view, children } = this.props; + const { view, children } = this.props; const label = this.getLabel(view); return ( <MUITab - className={ active ? styles.tabactive : '' } - selected={ active } icon={ view.icon } label={ label } > @@ -126,7 +112,6 @@ class TabBar extends Component { static propTypes = { views: PropTypes.array.isRequired, - hash: PropTypes.string.isRequired, pending: PropTypes.array, isTest: PropTypes.bool, netChain: PropTypes.string @@ -140,8 +125,7 @@ class TabBar extends Component { const prevViews = this.props.views.map((v) => v.id).sort(); const nextViews = nextProps.views.map((v) => v.id).sort(); - return (nextProps.hash !== this.props.hash) || - (nextProps.pending.length !== this.props.pending.length) || + return (nextProps.pending.length !== this.props.pending.length) || (!isEqual(prevViews, nextViews)); } @@ -212,28 +196,41 @@ class TabBar extends Component { } } -function mapStateToProps (state) { - const { views } = state.settings; +function mapStateToProps (initState) { + const { views } = initState.settings; - const filteredViews = Object + let filteredViewIds = Object .keys(views) - .filter((id) => views[id].fixed || views[id].active) + .filter((id) => views[id].fixed || views[id].active); + + let filteredViews = filteredViewIds .map((id) => ({ ...views[id], id })); - const windowHash = (window.location.hash || '').split('?')[0].split('/')[1]; - const hash = TABMAP[windowHash] || windowHash; + return (state) => { + const { views } = state.settings; - return { views: filteredViews, hash }; -} + const viewIds = Object + .keys(views) + .filter((id) => views[id].fixed || views[id].active); -function mapDispatchToProps (dispatch) { - return bindActionCreators({}, dispatch); + if (isEqual(viewIds, filteredViewIds)) { + return { views: filteredViews }; + } + + filteredViewIds = viewIds; + filteredViews = viewIds + .map((id) => ({ + ...views[id], + id + })); + + return { views: filteredViews }; + }; } export default connect( - mapStateToProps, - mapDispatchToProps + mapStateToProps )(TabBar); From 81c5085b35ad05313d9999ff80018e375d5c8413 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 14:31:20 +0100 Subject: [PATCH 47/74] Don't create new Contracts instance if already exists --- js/src/contracts/contracts.js | 4 ++++ js/src/views/Account/account.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/js/src/contracts/contracts.js b/js/src/contracts/contracts.js index f61a63690..a8020b825 100644 --- a/js/src/contracts/contracts.js +++ b/js/src/contracts/contracts.js @@ -62,6 +62,10 @@ export default class Contracts { } static create (api) { + if (instance) { + return instance; + } + return new Contracts(api); } diff --git a/js/src/views/Account/account.js b/js/src/views/Account/account.js index cbacd5280..840de05b9 100644 --- a/js/src/views/Account/account.js +++ b/js/src/views/Account/account.js @@ -26,7 +26,7 @@ import VerifyIcon from 'material-ui/svg-icons/action/verified-user'; import { EditMeta, DeleteAccount, Shapeshift, SMSVerification, Transfer, PasswordManager } from '~/modals'; import { Actionbar, Button, Page } from '~/ui'; -import shapeshiftBtn from '../../../assets/images/shapeshift-btn.png'; +import shapeshiftBtn from '~/../assets/images/shapeshift-btn.png'; import Header from './Header'; import Transactions from './Transactions'; From 65f586ed1478f8dc4fc389b94d9e582319703f92 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 14:32:54 +0100 Subject: [PATCH 48/74] Fix tab bar active style --- js/src/views/Application/TabBar/tabBar.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/src/views/Application/TabBar/tabBar.css b/js/src/views/Application/TabBar/tabBar.css index 7b74622d3..172750c8f 100644 --- a/js/src/views/Application/TabBar/tabBar.css +++ b/js/src/views/Application/TabBar/tabBar.css @@ -42,9 +42,12 @@ } &.tabactive, &.tabactive:hover { - color: white !important; background: rgba(0, 0, 0, 0.25) !important; border-radius: 4px 4px 0 0; + + * { + color: white !important; + } } } From ef93262311c97ef7f1b0f845a45652894c3c5121 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 15:19:05 +0100 Subject: [PATCH 49/74] See addresses outside address book + Save them --- js/src/modals/AddAddress/addAddress.js | 9 ++ js/src/views/Account/Header/header.css | 4 + js/src/views/Account/Header/header.js | 27 ++++-- js/src/views/Address/address.js | 109 +++++++++++++++++++------ 4 files changed, 119 insertions(+), 30 deletions(-) diff --git a/js/src/modals/AddAddress/addAddress.js b/js/src/modals/AddAddress/addAddress.js index e44cb0b3c..a72158cc7 100644 --- a/js/src/modals/AddAddress/addAddress.js +++ b/js/src/modals/AddAddress/addAddress.js @@ -28,6 +28,7 @@ export default class AddAddress extends Component { static propTypes = { contacts: PropTypes.object.isRequired, + address: PropTypes.string, onClose: PropTypes.func }; @@ -39,6 +40,12 @@ export default class AddAddress extends Component { description: '' }; + componentWillMount () { + if (this.props.address) { + this.onEditAddress(null, this.props.address); + } + } + render () { return ( <Modal @@ -77,6 +84,8 @@ export default class AddAddress extends Component { hint='the network address for the entry' error={ addressError } value={ address } + disabled={ !!this.props.address } + allowCopy={ false } onChange={ this.onEditAddress } /> <Input label='address name' diff --git a/js/src/views/Account/Header/header.css b/js/src/views/Account/Header/header.css index 74390face..26c2a5b22 100644 --- a/js/src/views/Account/Header/header.css +++ b/js/src/views/Account/Header/header.css @@ -31,6 +31,10 @@ .infoline, .uuidline { line-height: 1.618em; + + &.bigaddress { + font-size: 1.25em; + } } .infoline, diff --git a/js/src/views/Account/Header/header.js b/js/src/views/Account/Header/header.js index 6ce88aef4..7df87bd9c 100644 --- a/js/src/views/Account/Header/header.js +++ b/js/src/views/Account/Header/header.js @@ -32,18 +32,20 @@ export default class Header extends Component { balance: PropTypes.object, className: PropTypes.string, children: PropTypes.node, - isContract: PropTypes.bool + isContract: PropTypes.bool, + hideName: PropTypes.bool }; static defaultProps = { className: '', children: null, - isContract: false + isContract: false, + hideName: false }; render () { const { api } = this.context; - const { account, balance, className, children } = this.props; + const { account, balance, className, children, hideName } = this.props; const { address, meta, uuid } = account; if (!account) { @@ -60,17 +62,20 @@ export default class Header extends Component { <IdentityIcon address={ address } /> <div className={ styles.floatleft }> - <ContainerTitle title={ <IdentityName address={ address } unknown /> } /> - <div className={ styles.addressline }> + { this.renderName(address) } + + <div className={ [ hideName ? styles.bigaddress : '', styles.addressline ].join(' ') }> <CopyToClipboard data={ address } /> <div className={ styles.address }>{ address }</div> </div> + { uuidText } <div className={ styles.infoline }> { meta.description } </div> { this.renderTxCount() } </div> + <div className={ styles.tags }> <Tags tags={ meta.tags } /> </div> @@ -89,6 +94,18 @@ export default class Header extends Component { ); } + renderName (address) { + const { hideName } = this.props; + + if (hideName) { + return null; + } + + return ( + <ContainerTitle title={ <IdentityName address={ address } unknown /> } /> + ); + } + renderTxCount () { const { balance, isContract } = this.props; diff --git a/js/src/views/Address/address.js b/js/src/views/Address/address.js index c1427b2be..9c39203ba 100644 --- a/js/src/views/Address/address.js +++ b/js/src/views/Address/address.js @@ -19,8 +19,9 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import ActionDelete from 'material-ui/svg-icons/action/delete'; import ContentCreate from 'material-ui/svg-icons/content/create'; +import ContentAdd from 'material-ui/svg-icons/content/add'; -import { EditMeta } from '~/modals'; +import { EditMeta, AddAddress } from '~/modals'; import { Actionbar, Button, Page } from '~/ui'; import Header from '../Account/Header'; @@ -32,7 +33,7 @@ class Address extends Component { static contextTypes = { api: PropTypes.object.isRequired, router: PropTypes.object.isRequired - } + }; static propTypes = { setVisibleAccounts: PropTypes.func.isRequired, @@ -40,12 +41,13 @@ class Address extends Component { contacts: PropTypes.object, balances: PropTypes.object, params: PropTypes.object - } + }; state = { showDeleteDialog: false, - showEditDialog: false - } + showEditDialog: false, + showAdd: false + }; componentDidMount () { this.setVisibleAccounts(); @@ -73,32 +75,69 @@ class Address extends Component { render () { const { contacts, balances } = this.props; const { address } = this.props.params; - const { showDeleteDialog } = this.state; + + if (Object.keys(contacts).length === 0) { + return null; + } const contact = (contacts || {})[address]; const balance = (balances || {})[address]; - if (!contact) { + return ( + <div> + { this.renderAddAddress(contact, address) } + { this.renderEditDialog(contact) } + { this.renderActionbar(contact) } + { this.renderDelete(contact) } + <Page> + <Header + account={ contact || { address, meta: {} } } + balance={ balance } + hideName={ !contact } + /> + <Transactions + address={ address } + /> + </Page> + </div> + ); + } + + renderAddAddress (contact, address) { + if (contact) { + return null; + } + + const { contacts } = this.props; + const { showAdd } = this.state; + + if (!showAdd) { return null; } return ( - <div> - { this.renderEditDialog(contact) } - { this.renderActionbar(contact) } - <Delete - account={ contact } - visible={ showDeleteDialog } - route='/addresses' - onClose={ this.closeDeleteDialog } /> - <Page> - <Header - account={ contact } - balance={ balance } /> - <Transactions - address={ address } /> - </Page> - </div> + <AddAddress + contacts={ contacts } + onClose={ this.onCloseAdd } + address={ address } + /> + ); + } + + renderDelete (contact) { + if (!contact) { + return null; + } + + const { showDeleteDialog } = this.state; + + return ( + <Delete + account={ contact } + visible={ showDeleteDialog } + route='/addresses' + onClose={ this.closeDeleteDialog } + /> ); } @@ -116,17 +155,27 @@ class Address extends Component { onClick={ this.showDeleteDialog } /> ]; + const addToBook = ( + <Button + key='newAddress' + icon={ <ContentAdd /> } + label='save address' + onClick={ this.onOpenAdd } + /> + ); + return ( <Actionbar title='Address Information' - buttons={ !contact ? [] : buttons } /> + buttons={ !contact ? [ addToBook ] : buttons } + /> ); } renderEditDialog (contact) { const { showEditDialog } = this.state; - if (!showEditDialog) { + if (!contact || !showEditDialog) { return null; } @@ -151,6 +200,16 @@ class Address extends Component { showDeleteDialog = () => { this.setState({ showDeleteDialog: true }); } + + onOpenAdd = () => { + this.setState({ + showAdd: true + }); + } + + onCloseAdd = () => { + this.setState({ showAdd: false }); + } } function mapStateToProps (state) { From 054b4810d552fcea61079ac4eb41650a0569cd74 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 15:30:39 +0100 Subject: [PATCH 50/74] Fix wallet data's prop types --- js/src/modals/CreateWallet/WalletInfo/walletInfo.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/modals/CreateWallet/WalletInfo/walletInfo.js b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js index bbbe5877f..cc5c96964 100644 --- a/js/src/modals/CreateWallet/WalletInfo/walletInfo.js +++ b/js/src/modals/CreateWallet/WalletInfo/walletInfo.js @@ -30,10 +30,12 @@ export default class WalletInfo extends Component { owners: PropTypes.array.isRequired, required: PropTypes.oneOfType([ PropTypes.string, + PropTypes.object, PropTypes.number ]).isRequired, daylimit: PropTypes.oneOfType([ PropTypes.string, + PropTypes.object, PropTypes.number ]).isRequired, From d9da8a48ffdecd49655eebd75c749115d22078da Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 16:46:00 +0100 Subject: [PATCH 51/74] Add sender balance in transfer modal --- js/src/modals/Transfer/Details/details.js | 4 +- js/src/modals/Transfer/store.js | 3 +- js/src/modals/Transfer/transfer.js | 5 ++- js/src/redux/providers/balancesReducer.js | 2 +- .../ui/Form/AddressSelect/addressSelect.css | 9 ++++- js/src/ui/Form/AddressSelect/addressSelect.js | 37 +++++++++++++++++-- 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/js/src/modals/Transfer/Details/details.js b/js/src/modals/Transfer/Details/details.js index 53f04c489..20ac06f85 100644 --- a/js/src/modals/Transfer/Details/details.js +++ b/js/src/modals/Transfer/Details/details.js @@ -134,6 +134,7 @@ export default class Details extends Component { images: PropTypes.object.isRequired, sender: PropTypes.string, senderError: PropTypes.string, + sendersBalances: PropTypes.object, recipient: PropTypes.string, recipientError: PropTypes.string, tag: PropTypes.string, @@ -203,7 +204,7 @@ export default class Details extends Component { } renderFromAddress () { - const { sender, senderError, senders } = this.props; + const { sender, senderError, senders, sendersBalances } = this.props; if (!senders) { return null; @@ -218,6 +219,7 @@ export default class Details extends Component { hint='the sender address' value={ sender } onChange={ this.onEditSender } + balances={ sendersBalances } /> </div> ); diff --git a/js/src/modals/Transfer/store.js b/js/src/modals/Transfer/store.js index 3a8f54f92..e08d7203d 100644 --- a/js/src/modals/Transfer/store.js +++ b/js/src/modals/Transfer/store.js @@ -54,6 +54,7 @@ export default class TransferStore { @observable sender = ''; @observable senderError = null; + @observable sendersBalances = {}; @observable total = '0.0'; @observable totalError = null; @@ -66,8 +67,6 @@ export default class TransferStore { onClose = null; senders = null; - sendersBalances = null; - isWallet = false; wallet = null; diff --git a/js/src/modals/Transfer/transfer.js b/js/src/modals/Transfer/transfer.js index 0c96a1168..57dc569f2 100644 --- a/js/src/modals/Transfer/transfer.js +++ b/js/src/modals/Transfer/transfer.js @@ -155,8 +155,8 @@ class Transfer extends Component { renderDetailsPage () { const { account, balance, images, senders } = this.props; - const { valueAll, extras, recipient, recipientError, sender, senderError } = this.store; - const { tag, total, totalError, value, valueError } = this.store; + const { recipient, recipientError, sender, senderError, sendersBalances } = this.store; + const { valueAll, extras, tag, total, totalError, value, valueError } = this.store; return ( <Details @@ -170,6 +170,7 @@ class Transfer extends Component { recipientError={ recipientError } sender={ sender } senderError={ senderError } + sendersBalances={ sendersBalances } tag={ tag } total={ total } totalError={ totalError } diff --git a/js/src/redux/providers/balancesReducer.js b/js/src/redux/providers/balancesReducer.js index 01923a4f4..e21ae676d 100644 --- a/js/src/redux/providers/balancesReducer.js +++ b/js/src/redux/providers/balancesReducer.js @@ -44,7 +44,7 @@ export default handleActions({ const { token, value } = t; const { tag } = token; - const tokenIndex = nextTokens.findIndex((tok) => tok.token.tag === tag); + const tokenIndex = nextTokens.findIndex((tok) => tok.token && tok.token.tag === tag); if (tokenIndex === -1) { nextTokens.push({ diff --git a/js/src/ui/Form/AddressSelect/addressSelect.css b/js/src/ui/Form/AddressSelect/addressSelect.css index 30671db73..01bc8901d 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.css +++ b/js/src/ui/Form/AddressSelect/addressSelect.css @@ -15,7 +15,9 @@ /* along with Parity. If not, see <http://www.gnu.org/licenses/>. */ .account { - padding: 4px 0 0 0; + padding: 0.25em 0; + display: flex; + align-items: center; } .name { @@ -27,6 +29,11 @@ padding: 0 0 0 1em; } +.balance { + color: #aaa; + padding-left: 1em; +} + .image { display: inline-block; height: 32px; diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js index d0f331c34..2fbcc80bf 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.js +++ b/js/src/ui/Form/AddressSelect/addressSelect.js @@ -21,6 +21,8 @@ import AutoComplete from '../AutoComplete'; import IdentityIcon from '../../IdentityIcon'; import IdentityName from '../../IdentityName'; +import { fromWei } from '~/api/util/wei'; + import styles from './addressSelect.css'; export default class AddressSelect extends Component { @@ -40,7 +42,8 @@ export default class AddressSelect extends Component { value: PropTypes.string, tokens: PropTypes.object, onChange: PropTypes.func.isRequired, - allowInput: PropTypes.bool + allowInput: PropTypes.bool, + balances: PropTypes.object } state = { @@ -129,7 +132,34 @@ export default class AddressSelect extends Component { }; } + renderBalance (address) { + const { balances = {} } = this.props; + const balance = balances[address]; + + if (!balance) { + return null; + } + + const ethToken = balance.tokens.find((t) => t.token.tag.toLowerCase() === 'eth'); + + if (!ethToken) { + return null; + } + + const value = fromWei(ethToken.value); + + return ( + <div className={ styles.balance }> + { value.toFormat(3) }<small> { 'ETH' }</small> + </div> + ); + } + renderMenuItem (address) { + const balance = this.props.balances + ? this.renderBalance(address) + : null; + const item = ( <div className={ styles.account }> <IdentityIcon @@ -139,6 +169,8 @@ export default class AddressSelect extends Component { <IdentityName className={ styles.name } address={ address } /> + + { balance } </div> ); @@ -155,11 +187,10 @@ export default class AddressSelect extends Component { getSearchText () { const entry = this.getEntry(); - const { value } = this.state; return entry && entry.name ? entry.name.toUpperCase() - : value; + : this.state.value; } getEntry () { From 84116130f6b93ad6cfaa094818e28d459998174a Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 16:58:03 +0100 Subject: [PATCH 52/74] Add sender balances to contract (exec/deploy) --- .../DeployContract/DetailsStep/detailsStep.js | 3 +++ .../modals/DeployContract/deployContract.js | 22 +++++++++++++++++-- .../DetailsStep/detailsStep.js | 15 ++++++++----- .../modals/ExecuteContract/executeContract.js | 14 +++++++++--- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index aa0a30e55..9db223793 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -37,6 +37,7 @@ export default class DetailsStep extends Component { onParamsChange: PropTypes.func.isRequired, onInputsChange: PropTypes.func.isRequired, + balances: PropTypes.object, fromAddress: PropTypes.string, fromAddressError: PropTypes.string, name: PropTypes.string, @@ -77,6 +78,7 @@ export default class DetailsStep extends Component { render () { const { accounts, + balances, readOnly, fromAddress, fromAddressError, @@ -97,6 +99,7 @@ export default class DetailsStep extends Component { value={ fromAddress } error={ fromAddressError } accounts={ accounts } + balances={ balances } onChange={ this.onFromAddressChange } /> <Input diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index 5bf4fc389..21325f786 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -15,8 +15,10 @@ // along with Parity. If not, see <http://www.gnu.org/licenses/>. import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; import ContentClear from 'material-ui/svg-icons/content/clear'; +import { pick } from 'lodash'; import { BusyStep, CompletedStep, CopyToClipboard, Button, IdentityIcon, Modal, TxHash } from '~/ui'; import { ERRORS, validateAbi, validateCode, validateName } from '~/util/validation'; @@ -36,7 +38,7 @@ const STEPS = { COMPLETED: { title: 'completed' } }; -export default class DeployContract extends Component { +class DeployContract extends Component { static contextTypes = { api: PropTypes.object.isRequired, store: PropTypes.object.isRequired @@ -45,6 +47,7 @@ export default class DeployContract extends Component { static propTypes = { accounts: PropTypes.object.isRequired, onClose: PropTypes.func.isRequired, + balances: PropTypes.object, abi: PropTypes.string, code: PropTypes.string, readOnly: PropTypes.bool, @@ -192,7 +195,7 @@ export default class DeployContract extends Component { } renderStep () { - const { accounts, readOnly } = this.props; + const { accounts, readOnly, balances } = this.props; const { address, deployError, step, deployState, txhash, rejected } = this.state; if (deployError) { @@ -216,6 +219,7 @@ export default class DeployContract extends Component { <DetailsStep { ...this.state } accounts={ accounts } + balances={ balances } readOnly={ readOnly } onFromAddressChange={ this.onFromAddressChange } onDescriptionChange={ this.onDescriptionChange } @@ -394,3 +398,17 @@ export default class DeployContract extends Component { this.props.onClose(); } } + +function mapStateToProps (initState, initProps) { + const fromAddresses = Object.keys(initProps.accounts); + + return (state) => { + const balances = pick(state.balances.balances, fromAddresses); + return { balances }; + }; +} + +export default connect( + mapStateToProps +)(DeployContract); + diff --git a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js index 3ffb929a9..7bbe7be84 100644 --- a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js +++ b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js @@ -35,22 +35,24 @@ export default class DetailsStep extends Component { amount: PropTypes.string, amountError: PropTypes.string, onAmountChange: PropTypes.func.isRequired, + onFromAddressChange: PropTypes.func.isRequired, + onValueChange: PropTypes.func.isRequired, + values: PropTypes.array.isRequired, + valuesError: PropTypes.array.isRequired, + + balances: PropTypes.object, fromAddress: PropTypes.string, fromAddressError: PropTypes.string, gasEdit: PropTypes.bool, - onFromAddressChange: PropTypes.func.isRequired, func: PropTypes.object, funcError: PropTypes.string, onFuncChange: PropTypes.func, onGasEditClick: PropTypes.func, - values: PropTypes.array.isRequired, - valuesError: PropTypes.array.isRequired, - warning: PropTypes.string, - onValueChange: PropTypes.func.isRequired + warning: PropTypes.string } render () { - const { accounts, amount, amountError, fromAddress, fromAddressError, gasEdit, onGasEditClick, onFromAddressChange, onAmountChange } = this.props; + const { accounts, amount, amountError, balances, fromAddress, fromAddressError, gasEdit, onGasEditClick, onFromAddressChange, onAmountChange } = this.props; return ( <Form> @@ -61,6 +63,7 @@ export default class DetailsStep extends Component { value={ fromAddress } error={ fromAddressError } accounts={ accounts } + balances={ balances } onChange={ onFromAddressChange } /> { this.renderFunctionSelect() } { this.renderParameters() } diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index 7b4e8ccd2..c3ac96490 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -18,6 +18,8 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { observer } from 'mobx-react'; +import { pick } from 'lodash'; + import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; import ContentClear from 'material-ui/svg-icons/content/clear'; import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back'; @@ -57,6 +59,7 @@ class ExecuteContract extends Component { isTest: PropTypes.bool, fromAddress: PropTypes.string, accounts: PropTypes.object, + balances: PropTypes.object, contract: PropTypes.object, gasLimit: PropTypes.object.isRequired, onClose: PropTypes.func.isRequired, @@ -362,10 +365,15 @@ class ExecuteContract extends Component { } } -function mapStateToProps (state) { - const { gasLimit } = state.nodeStatus; +function mapStateToProps (initState, initProps) { + const fromAddresses = Object.keys(initProps.accounts); - return { gasLimit }; + return (state) => { + const balances = pick(state.balances.balances, fromAddresses); + const { gasLimit } = state.nodeStatus; + + return { gasLimit, balances }; + }; } function mapDispatchToProps (dispatch) { From cd6ab072170bfe30ac04a79008c9024d784141e6 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 17:06:44 +0100 Subject: [PATCH 53/74] Use the new `onClose` autocomplete prop --- js/src/ui/Form/AutoComplete/autocomplete.js | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/js/src/ui/Form/AutoComplete/autocomplete.js b/js/src/ui/Form/AutoComplete/autocomplete.js index c7a5dd141..f5ad43201 100644 --- a/js/src/ui/Form/AutoComplete/autocomplete.js +++ b/js/src/ui/Form/AutoComplete/autocomplete.js @@ -44,7 +44,6 @@ export default class AutoComplete extends Component { lastChangedValue: undefined, entry: null, open: false, - fakeBlur: false, dataSource: [] } @@ -78,7 +77,7 @@ export default class AutoComplete extends Component { onUpdateInput={ onUpdateInput } searchText={ value } onFocus={ this.onFocus } - onBlur={ this.onBlur } + onClose={ this.onClose } animation={ PopoverAnimationVertical } filter={ filter } popoverProps={ { open } } @@ -121,7 +120,6 @@ export default class AutoComplete extends Component { case 'down': const { menu } = muiAutocomplete.refs; menu && menu.handleKeyDown(event); - this.setState({ fakeBlur: true }); break; case 'enter': @@ -155,22 +153,12 @@ export default class AutoComplete extends Component { this.setState({ entry, open: false }); } - onBlur = (event) => { + onClose = (event) => { const { onUpdateInput } = this.props; - // TODO: Handle blur gracefully where we use onUpdateInput (currently replaces - // input where text is allowed with the last selected value from the dropdown) if (!onUpdateInput) { - window.setTimeout(() => { - const { entry, fakeBlur } = this.state; - - if (fakeBlur) { - this.setState({ fakeBlur: false }); - return; - } - - this.handleOnChange(entry); - }, 200); + const { entry } = this.state; + this.handleOnChange(entry); } } From 2346f29731da1816eab4115c8e130c6ad4745aa3 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 18:35:54 +0100 Subject: [PATCH 54/74] Add dividers to AutoComplete --- js/src/ui/Form/AddressSelect/addressSelect.js | 32 +++++-- js/src/ui/Form/AutoComplete/autocomplete.js | 94 +++++++++++++++---- 2 files changed, 103 insertions(+), 23 deletions(-) diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js index 2fbcc80bf..4bd93caa9 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.js +++ b/js/src/ui/Form/AddressSelect/addressSelect.js @@ -47,23 +47,41 @@ export default class AddressSelect extends Component { } state = { + autocompleteEntries: [], entries: {}, addresses: [], value: '' } entriesFromProps (props = this.props) { - const { accounts, contacts, contracts, wallets } = props; - const entries = Object.assign({}, accounts || {}, wallets || {}, contacts || {}, contracts || {}); - return entries; + const { accounts = {}, contacts = {}, contracts = {}, wallets = {} } = props; + + const autocompleteEntries = [].concat( + Object.values(wallets), + 'divider', + Object.values(accounts), + 'divider', + Object.values(contacts), + 'divider', + Object.values(contracts) + ); + + const entries = { + ...wallets, + ...accounts, + ...contacts, + ...contracts + }; + + return { autocompleteEntries, entries }; } componentWillMount () { const { value } = this.props; - const entries = this.entriesFromProps(); + const { entries, autocompleteEntries } = this.entriesFromProps(); const addresses = Object.keys(entries).sort(); - this.setState({ entries, addresses, value }); + this.setState({ autocompleteEntries, entries, addresses, value }); } componentWillReceiveProps (newProps) { @@ -74,7 +92,7 @@ export default class AddressSelect extends Component { render () { const { allowInput, disabled, error, hint, label } = this.props; - const { entries, value } = this.state; + const { autocompleteEntries, value } = this.state; const searchText = this.getSearchText(); const icon = this.renderIdentityIcon(value); @@ -92,7 +110,7 @@ export default class AddressSelect extends Component { onUpdateInput={ allowInput && this.onUpdateInput } value={ searchText } filter={ this.handleFilter } - entries={ entries } + entries={ autocompleteEntries } entry={ this.getEntry() || {} } renderItem={ this.renderItem } /> diff --git a/js/src/ui/Form/AutoComplete/autocomplete.js b/js/src/ui/Form/AutoComplete/autocomplete.js index f5ad43201..0e2acc98b 100644 --- a/js/src/ui/Form/AutoComplete/autocomplete.js +++ b/js/src/ui/Form/AutoComplete/autocomplete.js @@ -17,9 +17,10 @@ import React, { Component, PropTypes } from 'react'; import keycode from 'keycode'; import { MenuItem, AutoComplete as MUIAutoComplete } from 'material-ui'; +import Divider from 'material-ui/Divider'; import { PopoverAnimationVertical } from 'material-ui/Popover'; -import { isEqual } from 'lodash'; +import { isEqual, range } from 'lodash'; export default class AutoComplete extends Component { static propTypes = { @@ -38,14 +39,17 @@ export default class AutoComplete extends Component { PropTypes.array, PropTypes.object ]) - } + }; state = { lastChangedValue: undefined, entry: null, open: false, - dataSource: [] - } + dataSource: [], + dividerBreaks: [] + }; + + dividersVisibility = {}; componentWillMount () { const dataSource = this.getDataSource(); @@ -63,7 +67,7 @@ export default class AutoComplete extends Component { } render () { - const { disabled, error, hint, label, value, className, filter, onUpdateInput } = this.props; + const { disabled, error, hint, label, value, className, onUpdateInput } = this.props; const { open, dataSource } = this.state; return ( @@ -79,7 +83,7 @@ export default class AutoComplete extends Component { onFocus={ this.onFocus } onClose={ this.onClose } animation={ PopoverAnimationVertical } - filter={ filter } + filter={ this.handleFilter } popoverProps={ { open } } openOnFocus menuCloseDelay={ 0 } @@ -99,18 +103,76 @@ export default class AutoComplete extends Component { ? entries : Object.values(entries); - if (renderItem && typeof renderItem === 'function') { - return entriesArray.map(entry => renderItem(entry)); + let currentDivider = 0; + let firstSet = false; + + const dataSource = entriesArray.map((entry, index) => { + // Render divider + if (typeof entry === 'string' && entry.toLowerCase() === 'divider') { + // Don't add divider if nothing before + if (!firstSet) { + return undefined; + } + + const item = { + text: '', + divider: currentDivider, + isDivider: true, + value: ( + <Divider /> + ) + }; + + currentDivider++; + return item; + } + + let item; + + if (renderItem && typeof renderItem === 'function') { + item = renderItem(entry); + } else { + item = { + text: entry, + value: ( + <MenuItem + primaryText={ entry } + /> + ) + }; + } + + if (!firstSet) { + item.first = true; + firstSet = true; + } + + item.divider = currentDivider; + + return item; + }).filter((item) => item !== undefined); + + return dataSource; + } + + handleFilter = (searchText, name, item) => { + if (item.isDivider) { + return this.dividersVisibility[item.divider]; } - return entriesArray.map(entry => ({ - text: entry, - value: ( - <MenuItem - primaryText={ entry } - /> - ) - })); + if (item.first) { + this.dividersVisibility = {}; + } + + const { filter } = this.props; + const show = filter(searchText, name, item); + + // Show the related divider + if (show) { + this.dividersVisibility[item.divider] = true; + } + + return show; } onKeyDown = (event) => { From 69c0086ada5b55a0924662b301e0b1fefbb93f4b Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 19:14:29 +0100 Subject: [PATCH 55/74] Better Autocomplete Divider --- js/src/ui/Form/AutoComplete/autocomplete.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/js/src/ui/Form/AutoComplete/autocomplete.js b/js/src/ui/Form/AutoComplete/autocomplete.js index 0e2acc98b..f78ab41dc 100644 --- a/js/src/ui/Form/AutoComplete/autocomplete.js +++ b/js/src/ui/Form/AutoComplete/autocomplete.js @@ -16,11 +16,23 @@ import React, { Component, PropTypes } from 'react'; import keycode from 'keycode'; -import { MenuItem, AutoComplete as MUIAutoComplete } from 'material-ui'; -import Divider from 'material-ui/Divider'; +import { MenuItem, AutoComplete as MUIAutoComplete, Divider as MUIDivider } from 'material-ui'; import { PopoverAnimationVertical } from 'material-ui/Popover'; -import { isEqual, range } from 'lodash'; +import { isEqual } from 'lodash'; + +// Hack to prevent "Unknown prop `disableFocusRipple` on <hr> tag" error +class Divider extends Component { + static muiName = MUIDivider.muiName; + + render () { + return ( + <div style={ { margin: '0.25em 0' } }> + <MUIDivider style={ { height: 2 } }/> + </div> + ); + } +} export default class AutoComplete extends Component { static propTypes = { From 0f6681d3e819aa1498d28be4c24313c369754f02 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 19:15:45 +0100 Subject: [PATCH 56/74] Linting issue --- js/src/ui/Form/AutoComplete/autocomplete.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/ui/Form/AutoComplete/autocomplete.js b/js/src/ui/Form/AutoComplete/autocomplete.js index f78ab41dc..d11ae7cc5 100644 --- a/js/src/ui/Form/AutoComplete/autocomplete.js +++ b/js/src/ui/Form/AutoComplete/autocomplete.js @@ -28,7 +28,7 @@ class Divider extends Component { render () { return ( <div style={ { margin: '0.25em 0' } }> - <MUIDivider style={ { height: 2 } }/> + <MUIDivider style={ { height: 2 } } /> </div> ); } From 08a47ea2d48c2de543658a50d74dab39caa10b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= <tomusdrw@users.noreply.github.com> Date: Sat, 10 Dec 2016 20:18:42 +0100 Subject: [PATCH 57/74] Allow modifications of gas when confirming in signer (#3798) --- rpc/src/v1/impls/signer.rs | 10 ++++++---- rpc/src/v1/tests/mocked/signer.rs | 4 ++-- rpc/src/v1/types/confirmations.rs | 12 +++++++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index 66f46ba01..f13a3d037 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -89,11 +89,13 @@ impl<C: 'static, M: 'static> Signer for SignerClient<C, M> where C: MiningBlockC signer.peek(&id).map(|confirmation| { let mut payload = confirmation.payload.clone(); // Modify payload - match (&mut payload, modification.gas_price) { - (&mut ConfirmationPayload::SendTransaction(ref mut request), Some(gas_price)) => { + if let ConfirmationPayload::SendTransaction(ref mut request) = payload { + if let Some(gas_price) = modification.gas_price { request.gas_price = gas_price.into(); - }, - _ => {}, + } + if let Some(gas) = modification.gas { + request.gas = gas.into(); + } } // Execute let result = dispatch::execute(&*client, &*miner, &*accounts, payload, Some(pass)); diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index ea89e5876..c87abb7eb 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -183,7 +183,7 @@ fn should_confirm_transaction_and_dispatch() { let t = Transaction { nonce: U256::zero(), gas_price: U256::from(0x1000), - gas: U256::from(10_000_000), + gas: U256::from(0x50505), action: Action::Call(recipient), value: U256::from(0x1), data: vec![] @@ -198,7 +198,7 @@ fn should_confirm_transaction_and_dispatch() { let request = r#"{ "jsonrpc":"2.0", "method":"signer_confirmRequest", - "params":["0x1", {"gasPrice":"0x1000"}, "test"], + "params":["0x1", {"gasPrice":"0x1000","gas":"0x50505"}, "test"], "id":1 }"#; let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index d8cfa14d6..f69018422 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -142,6 +142,8 @@ pub struct TransactionModification { /// Modified gas price #[serde(rename="gasPrice")] pub gas_price: Option<U256>, + /// Modified gas + pub gas: Option<U256>, } /// Represents two possible return values. @@ -275,18 +277,26 @@ mod tests { let s1 = r#"{ "gasPrice":"0xba43b7400" }"#; - let s2 = r#"{}"#; + let s2 = r#"{"gas": "0x1233"}"#; + let s3 = r#"{}"#; // when let res1: TransactionModification = serde_json::from_str(s1).unwrap(); let res2: TransactionModification = serde_json::from_str(s2).unwrap(); + let res3: TransactionModification = serde_json::from_str(s3).unwrap(); // then assert_eq!(res1, TransactionModification { gas_price: Some(U256::from_str("0ba43b7400").unwrap()), + gas: None, }); assert_eq!(res2, TransactionModification { gas_price: None, + gas: Some(U256::from_str("1233").unwrap()), + }); + assert_eq!(res3, TransactionModification { + gas_price: None, + gas: None, }); } } From 70eab0da0331c7867c619aa12a6f6a86831aea3f Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 20:29:22 +0100 Subject: [PATCH 58/74] PR grumbles --- .../DeployContract/DetailsStep/detailsStep.js | 22 +++++++++---------- .../DetailsStep/detailsStep.js | 6 ++--- js/src/ui/Form/AddressSelect/addressSelect.js | 3 +-- js/src/views/Addresses/addresses.js | 2 +- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 9db223793..3de7a8a44 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -28,27 +28,25 @@ export default class DetailsStep extends Component { static propTypes = { accounts: PropTypes.object.isRequired, - - onFromAddressChange: PropTypes.func.isRequired, - onNameChange: PropTypes.func.isRequired, - onDescriptionChange: PropTypes.func.isRequired, onAbiChange: PropTypes.func.isRequired, onCodeChange: PropTypes.func.isRequired, - onParamsChange: PropTypes.func.isRequired, + onDescriptionChange: PropTypes.func.isRequired, + onFromAddressChange: PropTypes.func.isRequired, onInputsChange: PropTypes.func.isRequired, + onNameChange: PropTypes.func.isRequired, + onParamsChange: PropTypes.func.isRequired, + abi: PropTypes.string, + abiError: PropTypes.string, balances: PropTypes.object, + code: PropTypes.string, + codeError: PropTypes.string, + description: PropTypes.string, + descriptionError: PropTypes.string, fromAddress: PropTypes.string, fromAddressError: PropTypes.string, name: PropTypes.string, nameError: PropTypes.string, - description: PropTypes.string, - descriptionError: PropTypes.string, - abi: PropTypes.string, - abiError: PropTypes.string, - code: PropTypes.string, - codeError: PropTypes.string, - readOnly: PropTypes.bool }; diff --git a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js index 7bbe7be84..fde7fa1b2 100644 --- a/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js +++ b/js/src/modals/ExecuteContract/DetailsStep/detailsStep.js @@ -32,20 +32,20 @@ export default class DetailsStep extends Component { static propTypes = { accounts: PropTypes.object.isRequired, contract: PropTypes.object.isRequired, - amount: PropTypes.string, - amountError: PropTypes.string, onAmountChange: PropTypes.func.isRequired, onFromAddressChange: PropTypes.func.isRequired, onValueChange: PropTypes.func.isRequired, values: PropTypes.array.isRequired, valuesError: PropTypes.array.isRequired, + amount: PropTypes.string, + amountError: PropTypes.string, balances: PropTypes.object, fromAddress: PropTypes.string, fromAddressError: PropTypes.string, - gasEdit: PropTypes.bool, func: PropTypes.object, funcError: PropTypes.string, + gasEdit: PropTypes.bool, onFuncChange: PropTypes.func, onGasEditClick: PropTypes.func, warning: PropTypes.string diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js index 4bd93caa9..0cd92c5c8 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.js +++ b/js/src/ui/Form/AddressSelect/addressSelect.js @@ -158,7 +158,7 @@ export default class AddressSelect extends Component { return null; } - const ethToken = balance.tokens.find((t) => t.token.tag.toLowerCase() === 'eth'); + const ethToken = balance.tokens.find((tok) => tok.token && tok.token.tag && tok.token.tag.toLowerCase() === 'eth'); if (!ethToken) { return null; @@ -187,7 +187,6 @@ export default class AddressSelect extends Component { <IdentityName className={ styles.name } address={ address } /> - { balance } </div> ); diff --git a/js/src/views/Addresses/addresses.js b/js/src/views/Addresses/addresses.js index fd26d94e5..609f029c7 100644 --- a/js/src/views/Addresses/addresses.js +++ b/js/src/views/Addresses/addresses.js @@ -89,7 +89,7 @@ class Addresses extends Component { if (hasContacts && Object.keys(balances).length === 0) { return ( - <Loading size={ 3 } /> + <Loading /> ); } From 7401358543f8e4b18f55cebc4cc1d73ae5337ab6 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 22:15:56 +0100 Subject: [PATCH 59/74] PR grumbles --- .../DeployContract/DetailsStep/detailsStep.js | 23 +++++++++++-------- js/src/views/Contracts/contracts.js | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/js/src/modals/DeployContract/DetailsStep/detailsStep.js b/js/src/modals/DeployContract/DetailsStep/detailsStep.js index 3de7a8a44..51c5d3cfb 100644 --- a/js/src/modals/DeployContract/DetailsStep/detailsStep.js +++ b/js/src/modals/DeployContract/DetailsStep/detailsStep.js @@ -94,25 +94,28 @@ export default class DetailsStep extends Component { <AddressSelect label='from account (contract owner)' hint='the owner account for this contract' - value={ fromAddress } - error={ fromAddressError } accounts={ accounts } balances={ balances } - onChange={ this.onFromAddressChange } /> + error={ fromAddressError } + onChange={ this.onFromAddressChange } + value={ fromAddress } + /> <Input label='contract name' hint='a name for the deployed contract' error={ nameError } + onChange={ this.onNameChange } value={ name || '' } - onChange={ this.onNameChange } /> + /> <Input label='contract description (optional)' hint='a description for the contract' error={ descriptionError } + onChange={ this.onDescriptionChange } value={ description } - onChange={ this.onDescriptionChange } /> + /> { this.renderContractSelect() } @@ -120,17 +123,19 @@ export default class DetailsStep extends Component { label='abi / solc combined-output' hint='the abi of the contract to deploy or solc combined-output' error={ abiError } - value={ solcOutput } onChange={ this.onSolcChange } onSubmit={ this.onSolcSubmit } - readOnly={ readOnly } /> + readOnly={ readOnly } + value={ solcOutput } + /> <Input label='code' hint='the compiled code of the contract to deploy' error={ codeError } - value={ code } onSubmit={ this.onCodeChange } - readOnly={ readOnly || solc } /> + readOnly={ readOnly || solc } + value={ code } + /> </Form> ); diff --git a/js/src/views/Contracts/contracts.js b/js/src/views/Contracts/contracts.js index 524954f80..7b74654da 100644 --- a/js/src/views/Contracts/contracts.js +++ b/js/src/views/Contracts/contracts.js @@ -147,7 +147,7 @@ class Contracts extends Component { > <Button icon={ <FileIcon /> } - label='write contract' + label='develop contract' /> </Link>, From 173a88804a6dff251c5362f06e68d5c68801fbd5 Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jacogr@gmail.com> Date: Sat, 10 Dec 2016 22:21:32 +0100 Subject: [PATCH 60/74] Status page updates (#3774) * Allow Page to create optional Actionbar * Typo * Display last block.timestamp * Remove unnneeded console.log * Re-do git mv * git mv * Force build changes * Resolving case sensitivity issues * Swapped to margin at bottom --- js/src/redux/providers/status.js | 5 +++- js/src/redux/providers/statusReducer.js | 1 + js/src/ui/Actionbar/actionbar.js | 4 ++- js/src/ui/Page/page.css | 2 +- js/src/ui/Page/page.js | 25 ++++++++++++++--- js/src/views/Signer/signer.js | 3 +-- js/src/views/Signer/store.js | 12 ++++----- .../components/Debug/{Debug.css => debug.css} | 0 .../components/Debug/{Debug.js => debug.js} | 2 +- js/src/views/Status/components/Debug/index.js | 2 +- .../Status/components/MiningSettings/index.js | 2 +- .../{MiningSettings.js => miningSettings.js} | 0 .../views/Status/components/Status/index.js | 2 +- .../Status/{Status.css => status.css} | 15 ++++++++--- .../Status/{Status.js => status.js} | 27 +++++++++++-------- .../Status/containers/StatusPage/index.js | 2 +- .../StatusPage/statusPage.css} | 6 ++++- .../{StatusPage.js => statusPage.js} | 4 ++- js/src/views/Status/status.js | 14 +++------- 19 files changed, 82 insertions(+), 46 deletions(-) rename js/src/views/Status/components/Debug/{Debug.css => debug.css} (100%) rename js/src/views/Status/components/Debug/{Debug.js => debug.js} (99%) rename js/src/views/Status/components/MiningSettings/{MiningSettings.js => miningSettings.js} (100%) rename js/src/views/Status/components/Status/{Status.css => status.css} (90%) rename js/src/views/Status/components/Status/{Status.js => status.js} (84%) rename js/src/views/Status/{status.css => containers/StatusPage/statusPage.css} (93%) rename js/src/views/Status/containers/StatusPage/{StatusPage.js => statusPage.js} (95%) diff --git a/js/src/redux/providers/status.js b/js/src/redux/providers/status.js index 830192bbe..138479716 100644 --- a/js/src/redux/providers/status.js +++ b/js/src/redux/providers/status.js @@ -54,7 +54,10 @@ export default class Status { this._api.eth .getBlockByNumber(blockNumber) .then((block) => { - this._store.dispatch(statusCollection({ gasLimit: block.gasLimit })); + this._store.dispatch(statusCollection({ + blockTimestamp: block.timestamp, + gasLimit: block.gasLimit + })); }) .catch((error) => { console.warn('status._subscribeBlockNumber', 'getBlockByNumber', error); diff --git a/js/src/redux/providers/statusReducer.js b/js/src/redux/providers/statusReducer.js index 07ba4af5b..279a9da42 100644 --- a/js/src/redux/providers/statusReducer.js +++ b/js/src/redux/providers/statusReducer.js @@ -19,6 +19,7 @@ import { handleActions } from 'redux-actions'; const initialState = { blockNumber: new BigNumber(0), + blockTimestamp: new Date(), devLogs: [], devLogsLevels: null, devLogsEnabled: false, diff --git a/js/src/ui/Actionbar/actionbar.js b/js/src/ui/Actionbar/actionbar.js index c3fbe6a18..f744f5c57 100644 --- a/js/src/ui/Actionbar/actionbar.js +++ b/js/src/ui/Actionbar/actionbar.js @@ -17,11 +17,13 @@ import React, { Component, PropTypes } from 'react'; import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar'; +import { nodeOrStringProptype } from '~/util/proptypes'; + import styles from './actionbar.css'; export default class Actionbar extends Component { static propTypes = { - title: PropTypes.string, + title: nodeOrStringProptype(), buttons: PropTypes.array, children: PropTypes.node, className: PropTypes.string diff --git a/js/src/ui/Page/page.css b/js/src/ui/Page/page.css index 72a78dc22..c09283aec 100644 --- a/js/src/ui/Page/page.css +++ b/js/src/ui/Page/page.css @@ -18,7 +18,7 @@ .layout { padding: 0.25em 0.25em 1em 0.25em; - > * { + &>div { margin-bottom: 0.75em; } } diff --git a/js/src/ui/Page/page.js b/js/src/ui/Page/page.js index 867c3fdf1..9646358b3 100644 --- a/js/src/ui/Page/page.js +++ b/js/src/ui/Page/page.js @@ -16,21 +16,38 @@ import React, { Component, PropTypes } from 'react'; +import Actionbar from '../Actionbar'; +import { nodeOrStringProptype } from '~/util/proptypes'; + import styles from './page.css'; export default class Page extends Component { static propTypes = { + buttons: PropTypes.array, className: PropTypes.string, - children: PropTypes.node + children: PropTypes.node, + title: nodeOrStringProptype() }; render () { - const { className, children } = this.props; + const { buttons, className, children, title } = this.props; const classes = `${styles.layout} ${className}`; + let actionbar = null; + + if (title || buttons) { + actionbar = ( + <Actionbar + buttons={ buttons } + title={ title } /> + ); + } return ( - <div className={ classes }> - { children } + <div> + { actionbar } + <div className={ classes }> + { children } + </div> </div> ); } diff --git a/js/src/views/Signer/signer.js b/js/src/views/Signer/signer.js index 6d68c1dc0..f0b4baed7 100644 --- a/js/src/views/Signer/signer.js +++ b/js/src/views/Signer/signer.js @@ -23,8 +23,7 @@ export default class Signer extends Component { render () { return ( <div> - <Actionbar - title='Trusted Signer' /> + <Actionbar title='Trusted Signer' /> <RequestsPage /> </div> ); diff --git a/js/src/views/Signer/store.js b/js/src/views/Signer/store.js index 0eeb99861..bcc85a431 100644 --- a/js/src/views/Signer/store.js +++ b/js/src/views/Signer/store.js @@ -30,12 +30,6 @@ export default class Store { } } - @action unsubscribe () { - if (this._timeoutId) { - clearTimeout(this._timeoutId); - } - } - @action setBalance = (address, balance) => { this.setBalances({ [address]: balance }); } @@ -50,6 +44,12 @@ export default class Store { } } + @action unsubscribe () { + if (this._timeoutId) { + clearTimeout(this._timeoutId); + } + } + fetchBalance (address) { this._api.eth .getBalance(address) diff --git a/js/src/views/Status/components/Debug/Debug.css b/js/src/views/Status/components/Debug/debug.css similarity index 100% rename from js/src/views/Status/components/Debug/Debug.css rename to js/src/views/Status/components/Debug/debug.css diff --git a/js/src/views/Status/components/Debug/Debug.js b/js/src/views/Status/components/Debug/debug.js similarity index 99% rename from js/src/views/Status/components/Debug/Debug.js rename to js/src/views/Status/components/Debug/debug.js index 84ce1bb87..438d208f9 100644 --- a/js/src/views/Status/components/Debug/Debug.js +++ b/js/src/views/Status/components/Debug/debug.js @@ -22,7 +22,7 @@ import ReorderIcon from 'material-ui/svg-icons/action/reorder'; import { Container } from '~/ui'; -import styles from './Debug.css'; +import styles from './debug.css'; export default class Debug extends Component { static propTypes = { diff --git a/js/src/views/Status/components/Debug/index.js b/js/src/views/Status/components/Debug/index.js index 9e8eae219..c6b522c46 100644 --- a/js/src/views/Status/components/Debug/index.js +++ b/js/src/views/Status/components/Debug/index.js @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -export default from './Debug'; +export default from './debug'; diff --git a/js/src/views/Status/components/MiningSettings/index.js b/js/src/views/Status/components/MiningSettings/index.js index 3eee1e051..7853e0d33 100644 --- a/js/src/views/Status/components/MiningSettings/index.js +++ b/js/src/views/Status/components/MiningSettings/index.js @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -export default from './MiningSettings'; +export default from './miningSettings'; diff --git a/js/src/views/Status/components/MiningSettings/MiningSettings.js b/js/src/views/Status/components/MiningSettings/miningSettings.js similarity index 100% rename from js/src/views/Status/components/MiningSettings/MiningSettings.js rename to js/src/views/Status/components/MiningSettings/miningSettings.js diff --git a/js/src/views/Status/components/Status/index.js b/js/src/views/Status/components/Status/index.js index 8885a04c6..44079b224 100644 --- a/js/src/views/Status/components/Status/index.js +++ b/js/src/views/Status/components/Status/index.js @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -export default from './Status'; +export default from './status'; diff --git a/js/src/views/Status/components/Status/Status.css b/js/src/views/Status/components/Status/status.css similarity index 90% rename from js/src/views/Status/components/Status/Status.css rename to js/src/views/Status/components/Status/status.css index d79d41062..229e72ce1 100644 --- a/js/src/views/Status/components/Status/Status.css +++ b/js/src/views/Status/components/Status/status.css @@ -28,10 +28,19 @@ content: ''; } -.blockinfo { - font-size: 24px; +.blockInfo { color: #aaa; - line-height: 24px; + font-size: 1.5em; + line-height: 1.5em; +} + +.blockByline { + color: #aaa; + font-size: 0.75em; +} + +.padBottom { + padding-bottom: 1.25em !important; } .col, diff --git a/js/src/views/Status/components/Status/Status.js b/js/src/views/Status/components/Status/status.js similarity index 84% rename from js/src/views/Status/components/Status/Status.js rename to js/src/views/Status/components/Status/status.js index e2d65cb9a..891ec6632 100644 --- a/js/src/views/Status/components/Status/Status.js +++ b/js/src/views/Status/components/Status/status.js @@ -14,14 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -import React, { Component, PropTypes } from 'react'; import bytes from 'bytes'; +import moment from 'moment'; +import React, { Component, PropTypes } from 'react'; import { Container, ContainerTitle, Input } from '~/ui'; -import styles from './Status.css'; import MiningSettings from '../MiningSettings'; +import styles from './status.css'; + export default class Status extends Component { static propTypes = { nodeStatus: PropTypes.object.isRequired, @@ -44,23 +46,26 @@ export default class Status extends Component { <div className={ styles.container }> <div className={ styles.row }> <div className={ styles.col3 }> - <div className={ styles.col12 }> + <div className={ `${styles.col12} ${styles.padBottom}` }> <ContainerTitle title='best block' /> - <h2 { ...this._test('best-block') } className={ styles.blockinfo }> + <div { ...this._test('best-block') } className={ styles.blockInfo }> #{ nodeStatus.blockNumber.toFormat() } - </h2> + </div> + <div className={ styles.blockByline }> + { moment().calendar(nodeStatus.blockTimestamp) } + </div> </div> - <div className={ styles.col12 }> + <div className={ `${styles.col12} ${styles.padBottom}` }> <ContainerTitle title='peers' /> - <h2 { ...this._test('peers') } className={ styles.blockinfo }> + <div { ...this._test('peers') } className={ styles.blockInfo }> { peers } - </h2> + </div> </div> - <div className={ styles.col12 }> + <div className={ `${styles.col12} ${styles.padBottom}` }> <ContainerTitle title='hash rate' /> - <h2 { ...this._test('hashrate') } className={ styles.blockinfo }> + <div { ...this._test('hashrate') } className={ styles.blockInfo }> { `${hashrate} H/s` } - </h2> + </div> </div> </div> <div className={ styles.col5 }> diff --git a/js/src/views/Status/containers/StatusPage/index.js b/js/src/views/Status/containers/StatusPage/index.js index e36bc949c..0e141a16d 100644 --- a/js/src/views/Status/containers/StatusPage/index.js +++ b/js/src/views/Status/containers/StatusPage/index.js @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -export default from './StatusPage'; +export default from './statusPage'; diff --git a/js/src/views/Status/status.css b/js/src/views/Status/containers/StatusPage/statusPage.css similarity index 93% rename from js/src/views/Status/status.css rename to js/src/views/Status/containers/StatusPage/statusPage.css index 0ed554ae6..bda975813 100644 --- a/js/src/views/Status/status.css +++ b/js/src/views/Status/containers/StatusPage/statusPage.css @@ -14,5 +14,9 @@ /* You should have received a copy of the GNU General Public License /* along with Parity. If not, see <http://www.gnu.org/licenses/>. */ -.container { + +.body { + &>div { + margin-bottom: 0.25em; + } } diff --git a/js/src/views/Status/containers/StatusPage/StatusPage.js b/js/src/views/Status/containers/StatusPage/statusPage.js similarity index 95% rename from js/src/views/Status/containers/StatusPage/StatusPage.js rename to js/src/views/Status/containers/StatusPage/statusPage.js index c7ab56c5e..286e2c20e 100644 --- a/js/src/views/Status/containers/StatusPage/StatusPage.js +++ b/js/src/views/Status/containers/StatusPage/statusPage.js @@ -23,6 +23,8 @@ import { clearStatusLogs, toggleStatusLogs, toggleStatusRefresh } from '~/redux/ import Debug from '../../components/Debug'; import Status from '../../components/Status'; +import styles from './statusPage.css'; + class StatusPage extends Component { static propTypes = { nodeStatus: PropTypes.object.isRequired, @@ -39,7 +41,7 @@ class StatusPage extends Component { render () { return ( - <div> + <div className={ styles.body }> <Status { ...this.props } /> <Debug { ...this.props } /> </div> diff --git a/js/src/views/Status/status.js b/js/src/views/Status/status.js index ff13f1a07..805c96850 100644 --- a/js/src/views/Status/status.js +++ b/js/src/views/Status/status.js @@ -16,22 +16,16 @@ import React, { Component } from 'react'; -import { Actionbar, Page } from '~/ui'; +import { Page } from '~/ui'; import StatusPage from './containers/StatusPage'; -import styles from './status.css'; - export default class Status extends Component { render () { return ( - <div className={ styles.container }> - <Actionbar - title='status' /> - <Page> - <StatusPage /> - </Page> - </div> + <Page title='status'> + <StatusPage /> + </Page> ); } } From 9dbccfda2f52394056d9f5ea0c412543c71b3988 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 22:22:14 +0100 Subject: [PATCH 61/74] Fix padding bottom // Status bar fixed --- js/src/ui/Page/page.css | 2 +- js/src/views/Application/application.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/ui/Page/page.css b/js/src/ui/Page/page.css index 72a78dc22..ace070528 100644 --- a/js/src/ui/Page/page.css +++ b/js/src/ui/Page/page.css @@ -16,7 +16,7 @@ */ .layout { - padding: 0.25em 0.25em 1em 0.25em; + padding: 0.25em; > * { margin-bottom: 0.75em; diff --git a/js/src/views/Application/application.css b/js/src/views/Application/application.css index 1b26ed071..4ce748a30 100644 --- a/js/src/views/Application/application.css +++ b/js/src/views/Application/application.css @@ -19,4 +19,5 @@ display: flex; flex-direction: column; min-height: 100vh; + padding-bottom: 1em; } From 5ec1ff43ea79d1d029e0160c4efed125d1523776 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot <jaco+gitlab@ethcore.io> Date: Sat, 10 Dec 2016 21:29:53 +0000 Subject: [PATCH 62/74] [ci skip] js-precompiled 20161210-212741 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e970051c..6ff92e827 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,7 +1290,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#3d3b2f9e8e8b0fd62c172240bfd001a317cf2979" +source = "git+https://github.com/ethcore/js-precompiled.git#74aca23de55f84b2fcf6fe80d30277fa6449f645" 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 524c280dc..0618f9d2d 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.105", + "version": "0.2.106", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team <admin@parity.io>", From e5c73b29d857e8d7ef8d9113498bafdad386054d Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 22:42:49 +0100 Subject: [PATCH 63/74] 'contract/write' to 'contract/develop' --- js/src/main.js | 2 +- js/src/views/Contracts/contracts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/main.js b/js/src/main.js index c1dda9d57..f61e3d563 100644 --- a/js/src/main.js +++ b/js/src/main.js @@ -74,7 +74,7 @@ export default class MainApplication extends Component { <Route path='contracts'> <IndexRoute component={ Contracts } /> - <Route path='write' component={ WriteContract } /> + <Route path='develop' component={ WriteContract } /> <Route path=':address' component={ Contract } /> </Route> diff --git a/js/src/views/Contracts/contracts.js b/js/src/views/Contracts/contracts.js index 7b74654da..1e15c3947 100644 --- a/js/src/views/Contracts/contracts.js +++ b/js/src/views/Contracts/contracts.js @@ -142,7 +142,7 @@ class Contracts extends Component { label='deploy contract' onClick={ this.onDeployContract } />, <Link - to='/contracts/write' + to='/contracts/develop' key='writeContract' > <Button From b0f1665f11120183fb21bdaf31231d077ad96cbb Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 23:55:36 +0100 Subject: [PATCH 64/74] Notify user on transaction received (#3782) * Notify user on new transaction #2556 * Add routing to account on notification click * Timeout of notif set to 20s --- js/package.json | 1 + js/src/index.js | 2 +- js/src/redux/middleware.js | 6 +- js/src/redux/providers/balancesActions.js | 95 +++++++++++++++++++---- js/src/redux/providers/balancesReducer.js | 35 +-------- js/src/redux/providers/index.js | 6 +- js/src/redux/reducers.js | 7 +- js/src/redux/store.js | 4 +- js/src/util/notifications.js | 45 +++++++++++ 9 files changed, 142 insertions(+), 59 deletions(-) create mode 100644 js/src/util/notifications.js diff --git a/js/package.json b/js/package.json index 0618f9d2d..011ecc897 100644 --- a/js/package.json +++ b/js/package.json @@ -146,6 +146,7 @@ "mobx-react-devtools": "4.2.10", "moment": "2.17.0", "phoneformat.js": "1.0.3", + "push.js": "0.0.11", "qs": "6.3.0", "react": "15.4.1", "react-ace": "4.1.0", diff --git a/js/src/index.js b/js/src/index.js index 6938a46f8..46d6c9c74 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -67,7 +67,7 @@ if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) { const api = new SecureApi(`ws://${parityUrl}`, token); ContractInstances.create(api); -const store = initStore(api); +const store = initStore(api, hashHistory); store.dispatch({ type: 'initAll', api }); store.dispatch(setApi(api)); diff --git a/js/src/redux/middleware.js b/js/src/redux/middleware.js index bb11cf32f..14bc9b0a6 100644 --- a/js/src/redux/middleware.js +++ b/js/src/redux/middleware.js @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. import thunk from 'redux-thunk'; +import { routerMiddleware } from 'react-router-redux'; import ErrorsMiddleware from '~/ui/Errors/middleware'; import SettingsMiddleware from '~/views/Settings/middleware'; @@ -22,12 +23,13 @@ import SignerMiddleware from './providers/signerMiddleware'; import statusMiddleware from '~/views/Status/middleware'; import CertificationsMiddleware from './providers/certifications/middleware'; -export default function (api) { +export default function (api, browserHistory) { const errors = new ErrorsMiddleware(); const signer = new SignerMiddleware(api); const settings = new SettingsMiddleware(); const status = statusMiddleware(); const certifications = new CertificationsMiddleware(); + const routeMiddleware = routerMiddleware(browserHistory); const middleware = [ settings.toMiddleware(), @@ -36,5 +38,5 @@ export default function (api) { certifications.toMiddleware() ]; - return middleware.concat(status, thunk); + return middleware.concat(status, routeMiddleware, thunk); } diff --git a/js/src/redux/providers/balancesActions.js b/js/src/redux/providers/balancesActions.js index 65f831008..f8cfb2c1e 100644 --- a/js/src/redux/providers/balancesActions.js +++ b/js/src/redux/providers/balancesActions.js @@ -15,11 +15,14 @@ // along with Parity. If not, see <http://www.gnu.org/licenses/>. import { range, uniq, isEqual } from 'lodash'; +import BigNumber from 'bignumber.js'; +import { push } from 'react-router-redux'; import { hashToImageUrl } from './imagesReducer'; import { setAddressImage } from './imagesActions'; import * as ABIS from '~/contracts/abi'; +import { notifyTransaction } from '~/util/notifications'; import imagesEthereum from '../../../assets/images/contracts/ethereum-black-64x64.png'; const ETH = { @@ -28,7 +31,64 @@ const ETH = { image: imagesEthereum }; -export function setBalances (balances) { +function setBalances (_balances) { + return (dispatch, getState) => { + const state = getState(); + + const accounts = state.personal.accounts; + const nextBalances = _balances; + const prevBalances = state.balances.balances; + const balances = { ...prevBalances }; + + Object.keys(nextBalances).forEach((address) => { + if (!balances[address]) { + balances[address] = Object.assign({}, nextBalances[address]); + return; + } + + const balance = Object.assign({}, balances[address]); + const { tokens, txCount = balance.txCount } = nextBalances[address]; + const nextTokens = [].concat(balance.tokens); + + tokens.forEach((t) => { + const { token, value } = t; + const { tag } = token; + + const tokenIndex = nextTokens.findIndex((tok) => tok.token.tag === tag); + + if (tokenIndex === -1) { + nextTokens.push({ + token, + value + }); + } else { + const oldValue = nextTokens[tokenIndex].value; + + // If received a token/eth (old value < new value), notify + if (oldValue.lt(value) && accounts[address]) { + const account = accounts[address]; + const txValue = value.minus(oldValue); + + const redirectToAccount = () => { + const route = `/account/${account.address}`; + dispatch(push(route)); + }; + + notifyTransaction(account, token, txValue, redirectToAccount); + } + + nextTokens[tokenIndex] = { token, value }; + } + }); + + balances[address] = { txCount: txCount || new BigNumber(0), tokens: nextTokens }; + }); + + dispatch(_setBalances(balances)); + }; +} + +function _setBalances (balances) { return { type: 'setBalances', balances @@ -123,14 +183,14 @@ export function fetchBalances (_addresses) { const fullFetch = addresses.length === 1; - const fetchedAddresses = uniq(addresses.concat(Object.keys(accounts))); + const addressesToFetch = uniq(addresses.concat(Object.keys(accounts))); return Promise - .all(fetchedAddresses.map((addr) => fetchAccount(addr, api, fullFetch))) + .all(addressesToFetch.map((addr) => fetchAccount(addr, api, fullFetch))) .then((accountsBalances) => { const balances = {}; - fetchedAddresses.forEach((addr, idx) => { + addressesToFetch.forEach((addr, idx) => { balances[addr] = accountsBalances[idx]; }); @@ -146,10 +206,12 @@ export function fetchBalances (_addresses) { export function updateTokensFilter (_addresses, _tokens) { return (dispatch, getState) => { const { api, balances, personal } = getState(); - const { visibleAccounts } = personal; + const { visibleAccounts, accounts } = personal; const { tokensFilter } = balances; - const addresses = uniq(_addresses || visibleAccounts || []).sort(); + const addressesToFetch = uniq(visibleAccounts.concat(Object.keys(accounts))); + const addresses = uniq(_addresses || addressesToFetch || []).sort(); + const tokens = _tokens || Object.values(balances.tokens) || []; const tokenAddresses = tokens.map((t) => t.address).sort(); @@ -221,8 +283,10 @@ export function updateTokensFilter (_addresses, _tokens) { export function queryTokensFilter (tokensFilter) { return (dispatch, getState) => { const { api, personal, balances } = getState(); - const { visibleAccounts } = personal; + const { visibleAccounts, accounts } = personal; + const visibleAddresses = visibleAccounts.map((a) => a.toLowerCase()); + const addressesToFetch = uniq(visibleAddresses.concat(Object.keys(accounts))); Promise .all([ @@ -237,18 +301,16 @@ export function queryTokensFilter (tokensFilter) { .concat(logsTo) .forEach((log) => { const tokenAddress = log.address; + const fromAddress = '0x' + log.topics[1].slice(-40); const toAddress = '0x' + log.topics[2].slice(-40); - const fromIdx = visibleAddresses.indexOf(fromAddress); - const toIdx = visibleAddresses.indexOf(toAddress); - - if (fromIdx > -1) { - addresses.push(visibleAccounts[fromIdx]); + if (addressesToFetch.includes(fromAddress)) { + addresses.push(fromAddress); } - if (toIdx > -1) { - addresses.push(visibleAccounts[toIdx]); + if (addressesToFetch.includes(toAddress)) { + addresses.push(toAddress); } tokenAddresses.push(tokenAddress); @@ -269,9 +331,10 @@ export function queryTokensFilter (tokensFilter) { export function fetchTokensBalances (_addresses = null, _tokens = null) { return (dispatch, getState) => { const { api, personal, balances } = getState(); - const { visibleAccounts } = personal; + const { visibleAccounts, accounts } = personal; - const addresses = _addresses || visibleAccounts; + const addressesToFetch = uniq(visibleAccounts.concat(Object.keys(accounts))); + const addresses = _addresses || addressesToFetch; const tokens = _tokens || Object.values(balances.tokens); if (addresses.length === 0) { diff --git a/js/src/redux/providers/balancesReducer.js b/js/src/redux/providers/balancesReducer.js index 01923a4f4..4b6950498 100644 --- a/js/src/redux/providers/balancesReducer.js +++ b/js/src/redux/providers/balancesReducer.js @@ -15,7 +15,6 @@ // along with Parity. If not, see <http://www.gnu.org/licenses/>. import { handleActions } from 'redux-actions'; -import BigNumber from 'bignumber.js'; const initialState = { balances: {}, @@ -26,39 +25,7 @@ const initialState = { export default handleActions({ setBalances (state, action) { - const nextBalances = action.balances; - const prevBalances = state.balances; - const balances = { ...prevBalances }; - - Object.keys(nextBalances).forEach((address) => { - if (!balances[address]) { - balances[address] = Object.assign({}, nextBalances[address]); - return; - } - - const balance = Object.assign({}, balances[address]); - const { tokens, txCount = balance.txCount } = nextBalances[address]; - const nextTokens = [].concat(balance.tokens); - - tokens.forEach((t) => { - const { token, value } = t; - const { tag } = token; - - const tokenIndex = nextTokens.findIndex((tok) => tok.token.tag === tag); - - if (tokenIndex === -1) { - nextTokens.push({ - token, - value - }); - } else { - nextTokens[tokenIndex] = { token, value }; - } - }); - - balances[address] = Object.assign({}, { txCount: txCount || new BigNumber(0), tokens: nextTokens }); - }); - + const { balances } = action; return Object.assign({}, state, { balances }); }, diff --git a/js/src/redux/providers/index.js b/js/src/redux/providers/index.js index 563378caa..a90d8b62c 100644 --- a/js/src/redux/providers/index.js +++ b/js/src/redux/providers/index.js @@ -21,11 +21,11 @@ export Status from './status'; export apiReducer from './apiReducer'; export balancesReducer from './balancesReducer'; +export blockchainReducer from './blockchainReducer'; +export compilerReducer from './compilerReducer'; export imagesReducer from './imagesReducer'; export personalReducer from './personalReducer'; export signerReducer from './signerReducer'; -export statusReducer from './statusReducer'; -export blockchainReducer from './blockchainReducer'; -export compilerReducer from './compilerReducer'; export snackbarReducer from './snackbarReducer'; +export statusReducer from './statusReducer'; export walletReducer from './walletReducer'; diff --git a/js/src/redux/reducers.js b/js/src/redux/reducers.js index 92388df65..642dfe403 100644 --- a/js/src/redux/reducers.js +++ b/js/src/redux/reducers.js @@ -17,7 +17,12 @@ import { combineReducers } from 'redux'; import { routerReducer } from 'react-router-redux'; -import { apiReducer, balancesReducer, blockchainReducer, compilerReducer, imagesReducer, personalReducer, signerReducer, statusReducer as nodeStatusReducer, snackbarReducer, walletReducer } from './providers'; +import { + apiReducer, balancesReducer, blockchainReducer, + compilerReducer, imagesReducer, personalReducer, + signerReducer, statusReducer as nodeStatusReducer, + snackbarReducer, walletReducer +} from './providers'; import certificationsReducer from './providers/certifications/reducer'; import errorReducer from '~/ui/Errors/reducers'; diff --git a/js/src/redux/store.js b/js/src/redux/store.js index 2ff50ea53..1d62f9ea5 100644 --- a/js/src/redux/store.js +++ b/js/src/redux/store.js @@ -32,9 +32,9 @@ const storeCreation = window.devToolsExtension ? window.devToolsExtension()(createStore) : createStore; -export default function (api) { +export default function (api, browserHistory) { const reducers = initReducers(); - const middleware = initMiddleware(api); + const middleware = initMiddleware(api, browserHistory); const store = applyMiddleware(...middleware)(storeCreation)(reducers); new BalancesProvider(store, api).start(); diff --git a/js/src/util/notifications.js b/js/src/util/notifications.js new file mode 100644 index 000000000..479448234 --- /dev/null +++ b/js/src/util/notifications.js @@ -0,0 +1,45 @@ +// 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 <http://www.gnu.org/licenses/>. + +import Push from 'push.js'; +import BigNumber from 'bignumber.js'; +import { noop } from 'lodash'; + +import { fromWei } from '~/api/util/wei'; + +import ethereumIcon from '~/../assets/images/contracts/ethereum-black-64x64.png'; +import unkownIcon from '~/../assets/images/contracts/unknown-64x64.png'; + +export function notifyTransaction (account, token, _value, onClick) { + const name = account.name || account.address; + const value = token.tag.toLowerCase() === 'eth' + ? fromWei(_value) + : _value.div(new BigNumber(token.format || 1)); + + const icon = token.tag.toLowerCase() === 'eth' + ? ethereumIcon + : (token.image || unkownIcon); + + Push.create(`${name}`, { + body: `You just received ${value.toFormat()} ${token.tag.toUpperCase()}`, + icon: { + x16: icon, + x32: icon + }, + timeout: 20000, + onClick: onClick || noop + }); +} From b44e7bb292fe8b0f3431062a9735638d52718b74 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac <ngotchac@gmail.com> Date: Sat, 10 Dec 2016 23:55:57 +0100 Subject: [PATCH 65/74] Extract CSS to file in production builds (#3783) * Extract CSS to file if production build * Use DAPPS_URL for testing purposes + better assets in Webpack * Delete comments --- js/src/views/Dapp/dapp.js | 13 ++++++++++--- js/webpack/app.js | 31 +++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/js/src/views/Dapp/dapp.js b/js/src/views/Dapp/dapp.js index 9094e4dc3..3e9218914 100644 --- a/js/src/views/Dapp/dapp.js +++ b/js/src/views/Dapp/dapp.js @@ -51,9 +51,16 @@ export default class Dapp extends Component { src = `${dappsUrl}/${app.contentHash}/`; break; default: - const dapphost = process.env.NODE_ENV === 'production' && !app.secure - ? `${dappsUrl}/ui` - : ''; + let dapphost = process.env.DAPPS_URL || ( + process.env.NODE_ENV === 'production' && !app.secure + ? `${dappsUrl}/ui` + : '' + ); + + if (dapphost === '/') { + dapphost = ''; + } + src = `${dapphost}/${app.url}.html`; break; } diff --git a/js/webpack/app.js b/js/webpack/app.js index 5998cf30b..a7b086b96 100644 --- a/js/webpack/app.js +++ b/js/webpack/app.js @@ -20,6 +20,7 @@ const path = require('path'); const WebpackErrorNotificationPlugin = require('webpack-error-notification'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); const Shared = require('./shared'); const DAPPS = require('../src/dapps'); @@ -41,7 +42,7 @@ module.exports = { output: { publicPath: '/', path: path.join(__dirname, '../', DEST), - filename: '[name].[hash].js' + filename: '[name].[hash:10].js' }, module: { @@ -85,13 +86,20 @@ module.exports = { { test: /\.css$/, include: [ /src/ ], + // exclude: [ /src\/dapps/ ], + loader: isProd ? ExtractTextPlugin.extract([ + // 'style-loader', + 'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]', + 'postcss-loader' + ]) : undefined, // use: [ 'happypack/loader?id=css' ] - use: [ + use: isProd ? undefined : [ 'style-loader', 'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]', 'postcss-loader' ] }, + { test: /\.css$/, exclude: [ /src/ ], @@ -99,11 +107,15 @@ module.exports = { }, { test: /\.(png|jpg)$/, - use: [ 'file-loader?name=[name].[hash].[ext]' ] + use: [ 'file-loader?&name=assets/[name].[hash:10].[ext]' ] }, { - test: /\.(woff(2)|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/, - use: [ 'file-loader' ] + test: /\.(woff(2)|ttf|eot|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/, + use: [ 'file-loader?name=fonts/[name][hash:10].[ext]' ] + }, + { + test: /\.svg(\?v=[0-9]\.[0-9]\.[0-9])?$/, + use: [ 'file-loader?name=assets/[name].[hash:10].[ext]' ] } ], noParse: [ @@ -153,13 +165,20 @@ module.exports = { if (!isProd) { plugins.push( new webpack.optimize.CommonsChunkPlugin({ - filename: 'commons.[hash].js', + filename: 'commons.[hash:10].js', name: 'commons', minChunks: Infinity }) ); } + if (isProd) { + plugins.push(new ExtractTextPlugin({ + filename: 'styles/[name].[hash:10].css', + allChunks: true + })); + } + return plugins; }()) }; From 854eb1080cbbb4611049b31a62ad0cc7af996512 Mon Sep 17 00:00:00 2001 From: Gav Wood <gavin@ethcore.io> Date: Sat, 10 Dec 2016 23:59:50 +0100 Subject: [PATCH 66/74] Remove "s --- .gitlab-ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d27b58f9a..6dbe688f6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -425,11 +425,11 @@ test-rust-stable: - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v ^js/ | wc -l) - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - echo "rust/js modified: $RUST_FILES_MODIFIED / $JS_FILES_MODIFIED" - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi + - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - export RUST_BACKTRACE=1 - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi - - if [ "$RUST_FILES_MODIFIED" = 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi + - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi + - if [ $RUST_FILES_MODIFIED = 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi tags: - rust - rust-stable @@ -440,11 +440,11 @@ js-test: - git submodule update --init --recursive - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi + - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - export RUST_BACKTRACE=1 - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi + - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi tags: - rust - rust-stable @@ -487,9 +487,9 @@ js-release: before_script: - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi + - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - echo $JS_FILES_MODIFIED - - if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi + - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi tags: - javascript From 62492a915ed70b3a2b036e0f1b99615e8370c050 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot <jaco+gitlab@ethcore.io> Date: Sat, 10 Dec 2016 23:07:55 +0000 Subject: [PATCH 67/74] [ci skip] js-precompiled 20161210-230517 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ff92e827..7d485651c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,7 +1290,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#74aca23de55f84b2fcf6fe80d30277fa6449f645" +source = "git+https://github.com/ethcore/js-precompiled.git#f982c84ac216cc4f99d056c912e205bcf9341602" 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 011ecc897..29d7464be 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.106", + "version": "0.2.107", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team <admin@parity.io>", From c14d8da3505822509a5a7b3d2948dc0d70bdaafd Mon Sep 17 00:00:00 2001 From: Gav Wood <gavin@ethcore.io> Date: Sun, 11 Dec 2016 00:09:04 +0100 Subject: [PATCH 68/74] Use real arithmetic operator rather than '=' --- .gitlab-ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9d6f3eb69..365ea547a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -425,7 +425,7 @@ test-rust-stable: - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v ^js/ | wc -l) script: - export RUST_BACKTRACE=1 - - if [ $RUST_FILES_MODIFIED = 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi + - if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi tags: - rust - rust-stable @@ -435,9 +435,9 @@ js-test: before_script: - git submodule update --init --recursive - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi + - if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi + - if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS lint since no JS files modified."; else ./js/scripts/lint.sh && ./js/scripts/test.sh && ./js/scripts/build.sh; fi tags: - rust - rust-stable @@ -480,9 +480,9 @@ js-release: before_script: - export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep ^js/ | wc -l) - echo $JS_FILES_MODIFIED - - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi + - if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS deps install since no JS files modified."; else ./js/scripts/install-deps.sh;fi script: - echo $JS_FILES_MODIFIED - - if [ $JS_FILES_MODIFIED = 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi + - if [ $JS_FILES_MODIFIED -eq 0 ]; then echo "Skipping JS rebuild since no JS files modified."; else ./js/scripts/build.sh && ./js/scripts/release.sh; fi tags: - javascript From 7c2c9c89a47ab5fefd0e022dbcb2ff5d00d5dceb Mon Sep 17 00:00:00 2001 From: Gav Wood <gavin@ethcore.io> Date: Sun, 11 Dec 2016 00:20:19 +0100 Subject: [PATCH 69/74] Stricter search for rust changes. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 365ea547a..84d10e1bc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -422,7 +422,7 @@ test-rust-stable: image: ethcore/rust:stable before_script: - git submodule update --init --recursive - - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v ^js/ | wc -l) + - export RUST_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep -v -e ^js -e ^\\. -e ^LICENSE -e ^README.md -e ^appveyor.yml -e ^test.sh -e ^windows/ -e ^scripts/ -e^mac/ -e ^nsis/ | wc -l) script: - export RUST_BACKTRACE=1 - if [ $RUST_FILES_MODIFIED -eq 0 ]; then echo "Skipping Rust tests since no Rust files modified."; else ./test.sh $CARGOFLAGS; fi From bfb684a641de74c6da84050c7749a734bfb35ca0 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot <jaco+gitlab@ethcore.io> Date: Sun, 11 Dec 2016 01:28:52 +0000 Subject: [PATCH 70/74] [ci skip] js-precompiled 20161211-012647 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d485651c..cef7a4a24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,7 +1290,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#f982c84ac216cc4f99d056c912e205bcf9341602" +source = "git+https://github.com/ethcore/js-precompiled.git#45b91accfc947de126e00472874554f04739ac43" 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 29d7464be..f0e925fe4 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.107", + "version": "0.2.108", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team <admin@parity.io>", From 36b8e4b6a88344cdb121834c6319cfe2979f443a Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jacogr@gmail.com> Date: Sun, 11 Dec 2016 10:13:34 +0100 Subject: [PATCH 71/74] Extended publishing of libraries to npm (#3786) * Extended publishing of libraries to npm * Update source links * Add some tests before publishing NPM library * Fix Shapeshift tests --- js/npm/etherscan/README.md | 34 +++++++++++ js/npm/etherscan/package.json | 33 +++++++++++ js/{parity.md => npm/parity/README.md} | 4 +- .../parity/package.json} | 8 +-- js/npm/parity/test/smoke.spec.js | 26 +++++++++ js/npm/shapeshift/README.md | 34 +++++++++++ js/npm/shapeshift/package.json | 31 ++++++++++ js/npm/test/mocha.config.js | 29 ++++++++++ js/npm/test/mocha.opts | 1 + js/package.json | 2 +- js/scripts/dryrun-npm.sh | 33 +++++++++++ js/scripts/release.sh | 22 +++++--- js/src/3rdparty/etherscan/account.spec.js | 6 +- js/src/3rdparty/etherscan/stats.spec.js | 6 +- js/src/3rdparty/shapeshift/helpers.spec.js | 17 ++---- js/src/3rdparty/shapeshift/rpc.spec.js | 7 ++- js/src/3rdparty/shapeshift/shapeshift.js | 5 ++ js/src/3rdparty/shapeshift/shapeshift.spec.js | 6 +- js/src/library.etherscan.js | 34 +++++++++++ js/src/{library.js => library.parity.js} | 0 js/src/library.shapeshift.js | 34 +++++++++++ js/test/{npmLibrary.js => npmParity.js} | 4 +- js/webpack/npm.js | 56 +++++++++++++++++-- 23 files changed, 394 insertions(+), 38 deletions(-) create mode 100644 js/npm/etherscan/README.md create mode 100644 js/npm/etherscan/package.json rename js/{parity.md => npm/parity/README.md} (92%) rename js/{parity.package.json => npm/parity/package.json} (72%) create mode 100644 js/npm/parity/test/smoke.spec.js create mode 100644 js/npm/shapeshift/README.md create mode 100644 js/npm/shapeshift/package.json create mode 100644 js/npm/test/mocha.config.js create mode 100644 js/npm/test/mocha.opts create mode 100755 js/scripts/dryrun-npm.sh create mode 100644 js/src/library.etherscan.js rename js/src/{library.js => library.parity.js} (100%) create mode 100644 js/src/library.shapeshift.js rename js/test/{npmLibrary.js => npmParity.js} (92%) diff --git a/js/npm/etherscan/README.md b/js/npm/etherscan/README.md new file mode 100644 index 000000000..39c86d62b --- /dev/null +++ b/js/npm/etherscan/README.md @@ -0,0 +1,34 @@ +# @parity/Etherscan + +A thin, lightweight promise wrapper for the api.etherscan.io/apis service, exposing a common endpoint for use in JavaScript applications. + +[https://github.com/ethcore/parity/tree/master/js/src/3rdparty/etherscan](https://github.com/ethcore/parity/tree/master/js/src/3rdparty/etherscan) + +## usage + +installation - + +``` +npm install --save @parity/Etherscan +``` + +Usage - + +``` +const etherscan = require('@parity/Etherscan'); + +// api calls goes here +``` + +## api + +account (exposed on etherscan.account) - + +- `balance(address)` +- `balances(addresses)` (array or addresses) +- `transactions(address, page)` (page offset starts at 0, returns 25) + +stats (exposed on etherscan.stats) - + +- `price()` +- `supply()` diff --git a/js/npm/etherscan/package.json b/js/npm/etherscan/package.json new file mode 100644 index 000000000..80b1a38f8 --- /dev/null +++ b/js/npm/etherscan/package.json @@ -0,0 +1,33 @@ +{ + "name": "@parity/Etherscan", + "description": "The Parity Promise-based library for interfacing with Etherscan over HTTP", + "version": "0.0.0", + "main": "library.js", + "author": "Parity Team <admin@parity.io>", + "maintainers": [ + "Jaco Greeff" + ], + "contributors": [], + "license": "GPL-3.0", + "repository": { + "type": "git", + "url": "git+https://github.com/ethcore/parity.git" + }, + "keywords": [ + "Ethereum", + "ABI", + "API", + "RPC", + "Parity", + "Promise" + ], + "scripts": { + }, + "devDependencies": { + "chai": "3.5.0", + "mocha": "3.2.0" + }, + "dependencies": { + "node-fetch": "~1.6.3" + } +} diff --git a/js/parity.md b/js/npm/parity/README.md similarity index 92% rename from js/parity.md rename to js/npm/parity/README.md index 3e42f5c8d..30efd3b94 100644 --- a/js/parity.md +++ b/js/npm/parity/README.md @@ -1,7 +1,9 @@ -# parity.js +# @parity/parity.js Parity.js is a thin, fast, Promise-based wrapper around the Ethereum APIs. +[https://github.com/ethcore/parity/tree/master/js/src/api](https://github.com/ethcore/parity/tree/master/js/src/api) + ## installation Install the package with `npm install --save @parity/parity.js` diff --git a/js/parity.package.json b/js/npm/parity/package.json similarity index 72% rename from js/parity.package.json rename to js/npm/parity/package.json index 0974e072f..7b1fcebda 100644 --- a/js/parity.package.json +++ b/js/npm/parity/package.json @@ -1,6 +1,6 @@ { "name": "@parity/parity.js", - "description": "The Parity Promise-base API & ABI library for interfacing with Ethereum over RPC", + "description": "The Parity Promise-based API & ABI library for interfacing with Ethereum over RPC", "version": "0.0.0", "main": "library.js", "author": "Parity Team <admin@parity.io>", @@ -26,8 +26,8 @@ "devDependencies": { }, "dependencies": { - "bignumber.js": "^2.3.0", - "js-sha3": "^0.5.2", - "node-fetch": "^1.6.3" + "bignumber.js": "~2.3.0", + "js-sha3": "~0.5.2", + "node-fetch": "~1.6.3" } } diff --git a/js/npm/parity/test/smoke.spec.js b/js/npm/parity/test/smoke.spec.js new file mode 100644 index 000000000..9920b10d2 --- /dev/null +++ b/js/npm/parity/test/smoke.spec.js @@ -0,0 +1,26 @@ +// 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 <http://www.gnu.org/licenses/>. + +const parity = require('../'); + +describe('load the Parity library', function () { + it('should no throw any error', () => { + expect(parity).to.be.ok; + + expect(parity.Api).to.be.ok; + expect(parity.Abi).to.be.ok; + }); +}); diff --git a/js/npm/shapeshift/README.md b/js/npm/shapeshift/README.md new file mode 100644 index 000000000..e12b7b7ef --- /dev/null +++ b/js/npm/shapeshift/README.md @@ -0,0 +1,34 @@ +# @parity/ShapeShift + +A thin ES6 promise wrapper around the shapeshift.io APIs as documented at https://shapeshift.io/api + +[https://github.com/ethcore/parity/tree/master/js/src/3rdparty/shapeshift](https://github.com/ethcore/parity/tree/master/js/src/3rdparty/shapeshift) + +## usage + +installation - + +``` +npm install --save @parity/ShapeShift +``` + +Usage - + +``` +const APIKEY = 'private affiliate key or undefined'; +const shapeshift = require('@parity/ShapeShift')(APIKEY); + +// api calls goes here +``` + +## api + +queries - + +- `getCoins()` [https://shapeshift.io/api#api-104](https://shapeshift.io/api#api-104) +- `getMarketInfo(pair)` [https://shapeshift.io/api#api-103](https://shapeshift.io/api#api-103) +- `getStatus(depositAddress)` [https://shapeshift.io/api#api-5](https://shapeshift.io/api#api-5) + +transactions - + +- `shift(toAddress, returnAddress, pair)` [https://shapeshift.io/api#api-7](https://shapeshift.io/api#api-7) diff --git a/js/npm/shapeshift/package.json b/js/npm/shapeshift/package.json new file mode 100644 index 000000000..c163b0e5f --- /dev/null +++ b/js/npm/shapeshift/package.json @@ -0,0 +1,31 @@ +{ + "name": "@parity/ShapeShift", + "description": "The Parity Promise-based library for interfacing with ShapeShift over HTTP", + "version": "0.0.0", + "main": "library.js", + "author": "Parity Team <admin@parity.io>", + "maintainers": [ + "Jaco Greeff" + ], + "contributors": [], + "license": "GPL-3.0", + "repository": { + "type": "git", + "url": "git+https://github.com/ethcore/parity.git" + }, + "keywords": [ + "Ethereum", + "ABI", + "API", + "RPC", + "Parity", + "Promise" + ], + "scripts": { + }, + "devDependencies": { + }, + "dependencies": { + "node-fetch": "~1.6.3" + } +} diff --git a/js/npm/test/mocha.config.js b/js/npm/test/mocha.config.js new file mode 100644 index 000000000..2b871f518 --- /dev/null +++ b/js/npm/test/mocha.config.js @@ -0,0 +1,29 @@ +// 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 <http://www.gnu.org/licenses/>. + +const chai = require('chai'); +// const chaiAsPromised from 'chai-as-promised'; +// const chaiEnzyme from 'chai-enzyme'; +// const sinonChai from 'sinon-chai'; + +// chai.use(chaiAsPromised); +// chai.use(chaiEnzyme()); +// chai.use(sinonChai); + +// expose expect to global so we won't have to manually import & define it in every test +global.expect = chai.expect; + +module.exports = {}; diff --git a/js/npm/test/mocha.opts b/js/npm/test/mocha.opts new file mode 100644 index 000000000..0ed8269b4 --- /dev/null +++ b/js/npm/test/mocha.opts @@ -0,0 +1 @@ +-r ./test/mocha.config diff --git a/js/package.json b/js/package.json index f0e925fe4..4a15e9fee 100644 --- a/js/package.json +++ b/js/package.json @@ -43,7 +43,7 @@ "test": "NODE_ENV=test mocha 'src/**/*.spec.js'", "test:coverage": "NODE_ENV=test istanbul cover _mocha -- 'src/**/*.spec.js'", "test:e2e": "NODE_ENV=test mocha 'src/**/*.e2e.js'", - "test:npm": "(cd .npmjs && npm i) && node test/npmLibrary && (rm -rf .npmjs/node_modules)", + "test:npm": "(cd .npmjs && npm i) && node test/npmParity && (rm -rf .npmjs/node_modules)", "prepush": "npm run lint:cached" }, "devDependencies": { diff --git a/js/scripts/dryrun-npm.sh b/js/scripts/dryrun-npm.sh new file mode 100755 index 000000000..21e00f157 --- /dev/null +++ b/js/scripts/dryrun-npm.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -e + +# variables +PACKAGES=( "Parity" "Etherscan" "ShapeShift" ) + +# change into the build directory +BASEDIR=`dirname $0` +cd $BASEDIR/.. + +# build all packages +echo "*** Building packages for npmjs" +echo "$NPM_TOKEN" >> ~/.npmrc + +for PACKAGE in ${PACKAGES[@]} +do + echo "*** Building $PACKAGE" + LIBRARY=$PACKAGE npm run ci:build:npm + DIRECTORY=.npmjs/$(echo $PACKAGE | tr '[:upper:]' '[:lower:]') + + cd $DIRECTORY + echo "*** Executing $PACKAGE tests from $DIRECTORY" + npm test + + echo "*** Publishing $PACKAGE from $DIRECTORY" + echo "npm publish --access public || true" + cd ../.. + +done +cd .. + +# exit with exit code +exit 0 diff --git a/js/scripts/release.sh b/js/scripts/release.sh index 1cf3095ef..e120aaf95 100755 --- a/js/scripts/release.sh +++ b/js/scripts/release.sh @@ -3,7 +3,7 @@ set -e # variables UTCDATE=`date -u "+%Y%m%d-%H%M%S"` -PACKAGES=( "parity.js" ) +PACKAGES=( "Parity" "Etherscan" "ShapeShift" ) BRANCH=$CI_BUILD_REF_NAME GIT_JS_PRECOMPILED="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/js-precompiled.git" GIT_PARITY="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/parity.git" @@ -59,19 +59,27 @@ git reset --hard origin/$BRANCH 2>$GITLOG if [ "$BRANCH" == "master" ]; then cd js + echo "*** Bumping package.json patch version" npm --no-git-tag-version version npm version patch echo "*** Building packages for npmjs" - # echo -e "$NPM_USERNAME\n$NPM_PASSWORD\n$NPM_EMAIL" | npm login echo "$NPM_TOKEN" >> ~/.npmrc - npm run ci:build:npm - echo "*** Publishing $PACKAGE to npmjs" - cd .npmjs - npm publish --access public || true - cd ../.. + for PACKAGE in ${PACKAGES[@]} + do + echo "*** Building $PACKAGE" + LIBRARY=$PACKAGE npm run ci:build:npm + DIRECTORY=.npmjs/$(echo $PACKAGE | tr '[:upper:]' '[:lower:]') + + echo "*** Publishing $PACKAGE from $DIRECTORY" + cd $DIRECTORY + npm publish --access public || true + cd ../.. + done + + cd .. fi echo "*** Updating cargo parity-ui-precompiled#$PRECOMPILED_HASH" diff --git a/js/src/3rdparty/etherscan/account.spec.js b/js/src/3rdparty/etherscan/account.spec.js index 283fab3d2..9dc36c254 100644 --- a/js/src/3rdparty/etherscan/account.spec.js +++ b/js/src/3rdparty/etherscan/account.spec.js @@ -14,11 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -import etherscan from './'; +const etherscan = require('./'); const TESTADDR = '0xbf885e2b55c6bcc84556a3c5f07d3040833c8d00'; -describe.skip('etherscan/account', () => { +describe.skip('etherscan/account', function () { + this.timeout(60 * 1000); + const checkBalance = function (balance, addr) { expect(balance).to.be.ok; expect(balance.account).to.equal(addr); diff --git a/js/src/3rdparty/etherscan/stats.spec.js b/js/src/3rdparty/etherscan/stats.spec.js index fde2b035c..62152b6be 100644 --- a/js/src/3rdparty/etherscan/stats.spec.js +++ b/js/src/3rdparty/etherscan/stats.spec.js @@ -14,9 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -import etherscan from './'; +const etherscan = require('./'); + +describe.skip('etherscan/stats', function () { + this.timeout(60 * 1000); -describe.skip('etherscan/stats', () => { it('retrieves the latest price', () => { return etherscan.stats .price() diff --git a/js/src/3rdparty/shapeshift/helpers.spec.js b/js/src/3rdparty/shapeshift/helpers.spec.js index a82b2f6c3..f2ea0f3f9 100644 --- a/js/src/3rdparty/shapeshift/helpers.spec.js +++ b/js/src/3rdparty/shapeshift/helpers.spec.js @@ -14,22 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -import chai from 'chai'; -import nock from 'nock'; +const nock = require('nock'); -global.expect = chai.expect; // eslint-disable-line no-undef - -import 'isomorphic-fetch'; -import es6Promise from 'es6-promise'; -es6Promise.polyfill(); - -import initShapeshift from './'; -import initRpc from './rpc'; +const ShapeShift = require('./'); +const initShapeshift = (ShapeShift.default || ShapeShift); const APIKEY = '0x123454321'; const shapeshift = initShapeshift(APIKEY); -const rpc = initRpc(APIKEY); +const rpc = shapeshift.getRpc(); function mockget (requests) { let scope = nock(rpc.ENDPOINT); @@ -62,7 +55,7 @@ function mockpost (requests) { return scope; } -export { +module.exports = { APIKEY, mockget, mockpost, diff --git a/js/src/3rdparty/shapeshift/rpc.spec.js b/js/src/3rdparty/shapeshift/rpc.spec.js index 8de9e8641..47d4e0052 100644 --- a/js/src/3rdparty/shapeshift/rpc.spec.js +++ b/js/src/3rdparty/shapeshift/rpc.spec.js @@ -14,7 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -import { APIKEY, mockget, mockpost, rpc } from './helpers.spec.js'; +const helpers = require('./helpers.spec.js'); + +const APIKEY = helpers.APIKEY; +const mockget = helpers.mockget; +const mockpost = helpers.mockpost; +const rpc = helpers.rpc; describe('shapeshift/rpc', () => { describe('GET', () => { diff --git a/js/src/3rdparty/shapeshift/shapeshift.js b/js/src/3rdparty/shapeshift/shapeshift.js index 8f388d0a7..5743fcac1 100644 --- a/js/src/3rdparty/shapeshift/shapeshift.js +++ b/js/src/3rdparty/shapeshift/shapeshift.js @@ -26,6 +26,10 @@ export default function (rpc) { return rpc.get(`marketinfo/${pair}`); } + function getRpc () { + return rpc; + } + function getStatus (depositAddress) { return rpc.get(`txStat/${depositAddress}`); } @@ -103,6 +107,7 @@ export default function (rpc) { return { getCoins, getMarketInfo, + getRpc, getStatus, shift, subscribe, diff --git a/js/src/3rdparty/shapeshift/shapeshift.spec.js b/js/src/3rdparty/shapeshift/shapeshift.spec.js index 36b1506a2..01180e130 100644 --- a/js/src/3rdparty/shapeshift/shapeshift.spec.js +++ b/js/src/3rdparty/shapeshift/shapeshift.spec.js @@ -14,7 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see <http://www.gnu.org/licenses/>. -import { mockget, mockpost, shapeshift } from './helpers.spec.js'; +const helpers = require('./helpers.spec.js'); + +const mockget = helpers.mockget; +const mockpost = helpers.mockpost; +const shapeshift = helpers.shapeshift; describe('shapeshift/calls', () => { describe('getCoins', () => { diff --git a/js/src/library.etherscan.js b/js/src/library.etherscan.js new file mode 100644 index 000000000..59cb861d3 --- /dev/null +++ b/js/src/library.etherscan.js @@ -0,0 +1,34 @@ +// 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 <http://www.gnu.org/licenses/>. + +import 'babel-polyfill/dist/polyfill.js'; +import es6Promise from 'es6-promise'; +es6Promise.polyfill(); + +const isNode = typeof global !== 'undefined' && typeof global !== 'undefined'; +const isBrowser = typeof self !== 'undefined' && typeof self.window !== 'undefined'; + +if (isBrowser) { + require('whatwg-fetch'); +} + +if (isNode) { + global.fetch = require('node-fetch'); +} + +import Etherscan from './3rdparty/etherscan'; + +module.exports = Etherscan; diff --git a/js/src/library.js b/js/src/library.parity.js similarity index 100% rename from js/src/library.js rename to js/src/library.parity.js diff --git a/js/src/library.shapeshift.js b/js/src/library.shapeshift.js new file mode 100644 index 000000000..3f24f216b --- /dev/null +++ b/js/src/library.shapeshift.js @@ -0,0 +1,34 @@ +// 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 <http://www.gnu.org/licenses/>. + +import 'babel-polyfill/dist/polyfill.js'; +import es6Promise from 'es6-promise'; +es6Promise.polyfill(); + +const isNode = typeof global !== 'undefined' && typeof global !== 'undefined'; +const isBrowser = typeof self !== 'undefined' && typeof self.window !== 'undefined'; + +if (isBrowser) { + require('whatwg-fetch'); +} + +if (isNode) { + global.fetch = require('node-fetch'); +} + +import ShapeShift from './3rdparty/shapeshift'; + +module.exports = ShapeShift; diff --git a/js/test/npmLibrary.js b/js/test/npmParity.js similarity index 92% rename from js/test/npmLibrary.js rename to js/test/npmParity.js index 63d8f9515..6e125e9e2 100644 --- a/js/test/npmLibrary.js +++ b/js/test/npmParity.js @@ -15,8 +15,8 @@ // along with Parity. If not, see <http://www.gnu.org/licenses/>. try { - var Api = require('../.npmjs/library.js').Api; - var Abi = require('../.npmjs/library.js').Abi; + var Api = require('../.npmjs/parity/library.js').Api; + var Abi = require('../.npmjs/parity/library.js').Abi; if (typeof Api !== 'function') { throw new Error('No Api'); diff --git a/js/webpack/npm.js b/js/webpack/npm.js index 7353efe55..a1bbaeda9 100644 --- a/js/webpack/npm.js +++ b/js/webpack/npm.js @@ -23,14 +23,27 @@ const Shared = require('./shared'); const ENV = process.env.NODE_ENV || 'development'; const isProd = ENV === 'production'; +const LIBRARY = process.env.LIBRARY; +if (!LIBRARY) { + process.exit(-1); +} +const SRC = LIBRARY.toLowerCase(); +const OUTPUT_PATH = path.join(__dirname, '../.npmjs', SRC); + +const TEST_CONTEXT = SRC === 'parity' + ? '../npm/parity/test/' + : `../src/3rdparty/${SRC}/`; + +console.log(`Building ${LIBRARY} from library.${SRC}.js to .npmjs/${SRC}`); + module.exports = { context: path.join(__dirname, '../src'), target: 'node', - entry: 'library.js', + entry: `library.${SRC}.js`, output: { - path: path.join(__dirname, '../.npmjs'), + path: OUTPUT_PATH, filename: 'library.js', - library: 'Parity', + library: LIBRARY, libraryTarget: 'umd', umdNamedDefine: true }, @@ -66,19 +79,52 @@ module.exports = { plugins: Shared.getPlugins().concat([ new CopyWebpackPlugin([ { - from: '../parity.package.json', + from: `../npm/${SRC}/package.json`, to: 'package.json', transform: function (content, path) { const json = JSON.parse(content.toString()); json.version = packageJson.version; + + // Add tests dependencies to Dev Deps + json.devDependencies.chai = packageJson.devDependencies.chai; + json.devDependencies.mocha = packageJson.devDependencies.mocha; + json.devDependencies.nock = packageJson.devDependencies.nock; + + // Add test script + json.scripts.test = 'mocha \'test/*.spec.js\''; + return new Buffer(JSON.stringify(json, null, ' '), 'utf-8'); } }, { from: '../LICENSE' }, + + // Copy the base test config { - from: '../parity.md', + from: '../npm/test', + to: 'test' + }, + + // Copy the actual tests + { + context: TEST_CONTEXT, + from: '**/*.spec.js', + to: 'test', + transform: function (content, path) { + let output = content.toString(); + + // Don't skip tests + output = output.replace(/describe\.skip/, 'describe'); + + // Require parent library + output = output.replace('require(\'./\')', 'require(\'../\')'); + + return new Buffer(output, 'utf-8'); + } + }, + { + from: `../npm/${SRC}/README.md`, to: 'README.md' } ], { copyUnmodified: true }) From 17dee5f1a2595b51da6fc18f6a9f86dee98b18de Mon Sep 17 00:00:00 2001 From: GitLab Build Bot <jaco+gitlab@ethcore.io> Date: Sun, 11 Dec 2016 09:22:07 +0000 Subject: [PATCH 72/74] [ci skip] js-precompiled 20161211-091947 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cef7a4a24..653a51224 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,7 +1290,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#45b91accfc947de126e00472874554f04739ac43" +source = "git+https://github.com/ethcore/js-precompiled.git#52ce19ee74fdea46aead753065908afce57297af" 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 4a15e9fee..5d23998b0 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.108", + "version": "0.2.109", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team <admin@parity.io>", From 0a37288aa4412087aae4126984775c44736d8e38 Mon Sep 17 00:00:00 2001 From: Jaco Greeff <jacogr@gmail.com> Date: Sun, 11 Dec 2016 10:30:30 +0100 Subject: [PATCH 73/74] Lowercase npm packages (#3807) --- js/npm/etherscan/README.md | 6 +++--- js/npm/etherscan/package.json | 2 +- js/npm/shapeshift/README.md | 6 +++--- js/npm/shapeshift/package.json | 2 +- js/scripts/dryrun-npm.sh | 4 ++-- js/scripts/release.sh | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/js/npm/etherscan/README.md b/js/npm/etherscan/README.md index 39c86d62b..8130db3c6 100644 --- a/js/npm/etherscan/README.md +++ b/js/npm/etherscan/README.md @@ -1,4 +1,4 @@ -# @parity/Etherscan +# @parity/etherscan A thin, lightweight promise wrapper for the api.etherscan.io/apis service, exposing a common endpoint for use in JavaScript applications. @@ -9,13 +9,13 @@ A thin, lightweight promise wrapper for the api.etherscan.io/apis service, expos installation - ``` -npm install --save @parity/Etherscan +npm install --save @parity/etherscan ``` Usage - ``` -const etherscan = require('@parity/Etherscan'); +const etherscan = require('@parity/etherscan'); // api calls goes here ``` diff --git a/js/npm/etherscan/package.json b/js/npm/etherscan/package.json index 80b1a38f8..0cfdf83e1 100644 --- a/js/npm/etherscan/package.json +++ b/js/npm/etherscan/package.json @@ -1,5 +1,5 @@ { - "name": "@parity/Etherscan", + "name": "@parity/etherscan", "description": "The Parity Promise-based library for interfacing with Etherscan over HTTP", "version": "0.0.0", "main": "library.js", diff --git a/js/npm/shapeshift/README.md b/js/npm/shapeshift/README.md index e12b7b7ef..0544b6f99 100644 --- a/js/npm/shapeshift/README.md +++ b/js/npm/shapeshift/README.md @@ -1,4 +1,4 @@ -# @parity/ShapeShift +# @parity/shapeshift A thin ES6 promise wrapper around the shapeshift.io APIs as documented at https://shapeshift.io/api @@ -9,14 +9,14 @@ A thin ES6 promise wrapper around the shapeshift.io APIs as documented at https: installation - ``` -npm install --save @parity/ShapeShift +npm install --save @parity/shapeshift ``` Usage - ``` const APIKEY = 'private affiliate key or undefined'; -const shapeshift = require('@parity/ShapeShift')(APIKEY); +const shapeshift = require('@parity/shapeshift')(APIKEY); // api calls goes here ``` diff --git a/js/npm/shapeshift/package.json b/js/npm/shapeshift/package.json index c163b0e5f..b0a2c460a 100644 --- a/js/npm/shapeshift/package.json +++ b/js/npm/shapeshift/package.json @@ -1,5 +1,5 @@ { - "name": "@parity/ShapeShift", + "name": "@parity/shapeshift", "description": "The Parity Promise-based library for interfacing with ShapeShift over HTTP", "version": "0.0.0", "main": "library.js", diff --git a/js/scripts/dryrun-npm.sh b/js/scripts/dryrun-npm.sh index 21e00f157..6d9412f62 100755 --- a/js/scripts/dryrun-npm.sh +++ b/js/scripts/dryrun-npm.sh @@ -2,7 +2,7 @@ set -e # variables -PACKAGES=( "Parity" "Etherscan" "ShapeShift" ) +PACKAGES=( "parity" "etherscan" "shapeshift" ) # change into the build directory BASEDIR=`dirname $0` @@ -16,7 +16,7 @@ for PACKAGE in ${PACKAGES[@]} do echo "*** Building $PACKAGE" LIBRARY=$PACKAGE npm run ci:build:npm - DIRECTORY=.npmjs/$(echo $PACKAGE | tr '[:upper:]' '[:lower:]') + DIRECTORY=.npmjs/$PACKAGE cd $DIRECTORY echo "*** Executing $PACKAGE tests from $DIRECTORY" diff --git a/js/scripts/release.sh b/js/scripts/release.sh index e120aaf95..88422df0a 100755 --- a/js/scripts/release.sh +++ b/js/scripts/release.sh @@ -3,7 +3,7 @@ set -e # variables UTCDATE=`date -u "+%Y%m%d-%H%M%S"` -PACKAGES=( "Parity" "Etherscan" "ShapeShift" ) +PACKAGES=( "parity" "etherscan" "shapeshift" ) BRANCH=$CI_BUILD_REF_NAME GIT_JS_PRECOMPILED="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/js-precompiled.git" GIT_PARITY="https://${GITHUB_JS_PRECOMPILED}:@github.com/ethcore/parity.git" @@ -71,7 +71,7 @@ if [ "$BRANCH" == "master" ]; then do echo "*** Building $PACKAGE" LIBRARY=$PACKAGE npm run ci:build:npm - DIRECTORY=.npmjs/$(echo $PACKAGE | tr '[:upper:]' '[:lower:]') + DIRECTORY=.npmjs/$PACKAGE echo "*** Publishing $PACKAGE from $DIRECTORY" cd $DIRECTORY From 95af942fc9f5931e7c0784de460041cbefccd784 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot <jaco+gitlab@ethcore.io> Date: Sun, 11 Dec 2016 09:40:44 +0000 Subject: [PATCH 74/74] [ci skip] js-precompiled 20161211-093638 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 653a51224..c15b82879 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,7 +1290,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#52ce19ee74fdea46aead753065908afce57297af" +source = "git+https://github.com/ethcore/js-precompiled.git#4465ba44b8f698640f2355508245f7f9e370343b" 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 5d23998b0..a8b156dd2 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.2.109", + "version": "0.2.110", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team <admin@parity.io>",