From d7e225c0afdd986ee4fa7c1cf3e7812066117c9d Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Wed, 6 Jul 2016 19:14:53 +0200 Subject: [PATCH 1/3] Kill timers when removing IO handler (#1554) --- util/src/io/service.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/src/io/service.rs b/util/src/io/service.rs index 07e60f766..1e2169808 100644 --- a/util/src/io/service.rs +++ b/util/src/io/service.rs @@ -248,6 +248,13 @@ impl Handler for IoManager where Message: Send + Clone + Sync IoMessage::RemoveHandler { handler_id } => { // TODO: flush event loop self.handlers.remove(handler_id); + // unregister timers + let mut timers = self.timers.write().unwrap(); + let to_remove: Vec<_> = timers.keys().cloned().filter(|timer_id| timer_id / TOKENS_PER_HANDLER == handler_id).collect(); + for timer_id in to_remove { + let timer = timers.remove(&timer_id).expect("to_remove only contains keys from timers; qed"); + event_loop.clear_timeout(timer.timeout); + } }, IoMessage::AddTimer { handler_id, token, delay } => { let timer_id = token + handler_id * TOKENS_PER_HANDLER; From 8282c7dd50dff7e9c6fd54ad4293643af2cf1e6b Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 7 Jul 2016 11:39:32 +0400 Subject: [PATCH 2/3] Client IPC Interface (#1493) * btree map serializer * serde tests * state diff serialization * basic layout * more missing serializaers * uncle returns rlp * block queue info * sorting with transaction result * sorting out util imports * transaction import result sorting also * sorting filters & ranges * error sorting out * deriving ipc service compiling * rpc & sync recompile * sorting rpc using uncles * fix compilation * fix merging bugs * fix unused imports * fix all warnings * tests stub * some merge bugs * ethcore compilation * fix rpc compilation * deriving attribute * tests (and fixes) * rpc test working * fix warnings again * rs.in -> rs * missing attribute * refactored tree changes * paste reformat mess fix * pub mod actually * intendation fix --- Cargo.lock | 3 ++ ethcore/Cargo.toml | 2 + ethcore/build.rs | 14 ++++++ ethcore/src/block_queue.rs | 18 +------ ethcore/src/blockchain/block_info.rs | 42 ---------------- ethcore/src/client/client.rs | 69 ++++++++++++++++++-------- ethcore/src/client/mod.rs | 21 ++++---- ethcore/src/client/test_client.rs | 2 +- ethcore/src/client/trace.rs | 15 +----- ethcore/src/error.rs | 3 +- ethcore/src/lib.rs | 3 +- ethcore/src/miner/transaction_queue.rs | 1 + ethcore/src/pod_state.rs | 22 ++++---- ethcore/src/tests/mod.rs | 3 +- ethcore/src/tests/rpc.rs | 57 +++++++++++++++++++++ ethcore/src/types/account_diff.rs | 24 ++++++--- ethcore/src/types/block_queue_info.rs | 38 ++++++++++++++ ethcore/src/types/call_analytics.rs | 32 ++++++++++++ ethcore/src/types/executed.rs | 2 +- ethcore/src/{ => types}/filter.rs | 45 +++++++++++------ ethcore/src/types/ids.rs | 1 + ethcore/src/types/mod.rs.in | 4 ++ ethcore/src/types/state_diff.rs | 20 +++++--- ethcore/src/types/trace_filter.rs | 35 +++++++++++++ ethcore/src/types/tree_route.rs | 5 +- ipc/rpc/src/binary.rs | 27 +++++++++- ipc/rpc/src/interface.rs | 2 +- rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/impls/eth.rs | 2 +- rpc/src/v1/types/filter.rs | 2 +- rpc/src/v1/types/trace.rs | 4 +- 32 files changed, 362 insertions(+), 158 deletions(-) create mode 100644 ethcore/src/tests/rpc.rs create mode 100644 ethcore/src/types/block_queue_info.rs create mode 100644 ethcore/src/types/call_analytics.rs rename ethcore/src/{ => types}/filter.rs (93%) create mode 100644 ethcore/src/types/trace_filter.rs diff --git a/Cargo.lock b/Cargo.lock index a17bc4413..e3dc752f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -245,6 +245,7 @@ dependencies = [ "ethcore-devtools 1.3.0", "ethcore-ipc 1.3.0", "ethcore-ipc-codegen 1.3.0", + "ethcore-ipc-nano 1.3.0", "ethcore-util 1.3.0", "ethjson 0.1.0", "ethstore 0.1.0", @@ -256,6 +257,7 @@ dependencies = [ "rayon 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -330,6 +332,7 @@ dependencies = [ "ethash 1.3.0", "ethcore 1.3.0", "ethcore-devtools 1.3.0", + "ethcore-ipc 1.3.0", "ethcore-util 1.3.0", "ethjson 0.1.0", "ethsync 1.3.0", diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index acdcd667a..810fd6c51 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -31,6 +31,8 @@ bloomchain = "0.1" "ethcore-ipc" = { path = "../ipc/rpc" } rayon = "0.3.1" ethstore = { path = "../ethstore" } +semver = "0.2" +ethcore-ipc-nano = { path = "../ipc/nano" } [dependencies.hyper] git = "https://github.com/ethcore/hyper" diff --git a/ethcore/build.rs b/ethcore/build.rs index dadcce13a..9f61851d4 100644 --- a/ethcore/build.rs +++ b/ethcore/build.rs @@ -30,4 +30,18 @@ fn main() { codegen::register(&mut registry); registry.expand("", &src, &dst).unwrap(); } + + // client interface + { + let src = Path::new("src/client/client.rs"); + let intermediate = Path::new(&out_dir).join("client.intermediate.rs.in"); + let mut registry = syntex::Registry::new(); + codegen::register(&mut registry); + registry.expand("", &src, &intermediate).unwrap(); + + let dst = Path::new(&out_dir).join("client.ipc.rs"); + let mut registry = syntex::Registry::new(); + codegen::register(&mut registry); + registry.expand("", &intermediate, &dst).unwrap(); + } } diff --git a/ethcore/src/block_queue.rs b/ethcore/src/block_queue.rs index ce99dcccd..2288c1509 100644 --- a/ethcore/src/block_queue.rs +++ b/ethcore/src/block_queue.rs @@ -28,6 +28,8 @@ use service::*; use client::BlockStatus; use util::panics::*; +pub use types::block_queue_info::BlockQueueInfo; + known_heap_size!(0, UnverifiedBlock, VerifyingBlock, PreverifiedBlock); const MIN_MEM_LIMIT: usize = 16384; @@ -53,22 +55,6 @@ impl Default for BlockQueueConfig { } } -/// Block queue status -#[derive(Debug)] -pub struct BlockQueueInfo { - /// Number of queued blocks pending verification - pub unverified_queue_size: usize, - /// Number of verified queued blocks pending import - pub verified_queue_size: usize, - /// Number of blocks being verified - pub verifying_queue_size: usize, - /// Configured maximum number of blocks in the queue - pub max_queue_size: usize, - /// Configured maximum number of bytes to use - pub max_mem_use: usize, - /// Heap memory used in bytes - pub mem_used: usize, -} impl BlockQueueInfo { /// The total size of the queues. diff --git a/ethcore/src/blockchain/block_info.rs b/ethcore/src/blockchain/block_info.rs index eb3677c25..42f1bd439 100644 --- a/ethcore/src/blockchain/block_info.rs +++ b/ethcore/src/blockchain/block_info.rs @@ -17,8 +17,6 @@ use util::numbers::{U256,H256}; use header::BlockNumber; -use util::bytes::{FromRawBytesVariable, FromBytesError, ToBytesWithMap}; - /// Brief info about inserted block. #[derive(Clone)] pub struct BlockInfo { @@ -54,43 +52,3 @@ pub struct BranchBecomingCanonChainData { /// Hashes of the blocks which were invalidated. pub retracted: Vec, } - -impl FromRawBytesVariable for BranchBecomingCanonChainData { - fn from_bytes_variable(bytes: &[u8]) -> Result { - type Tuple = (Vec, Vec, H256); - let (enacted, retracted, ancestor) = try!(Tuple::from_bytes_variable(bytes)); - Ok(BranchBecomingCanonChainData { ancestor: ancestor, enacted: enacted, retracted: retracted }) - } -} - -impl FromRawBytesVariable for BlockLocation { - fn from_bytes_variable(bytes: &[u8]) -> Result { - match bytes[0] { - 0 => Ok(BlockLocation::CanonChain), - 1 => Ok(BlockLocation::Branch), - 2 => Ok(BlockLocation::BranchBecomingCanonChain( - try!(BranchBecomingCanonChainData::from_bytes_variable(&bytes[1..bytes.len()])))), - _ => Err(FromBytesError::UnknownMarker) - } - } -} - -impl ToBytesWithMap for BranchBecomingCanonChainData { - fn to_bytes_map(&self) -> Vec { - (&self.enacted, &self.retracted, &self.ancestor).to_bytes_map() - } -} - -impl ToBytesWithMap for BlockLocation { - fn to_bytes_map(&self) -> Vec { - match *self { - BlockLocation::CanonChain => vec![0u8], - BlockLocation::Branch => vec![1u8], - BlockLocation::BranchBecomingCanonChain(ref data) => { - let mut bytes = (&data.enacted, &data.retracted, &data.ancestor).to_bytes_map(); - bytes.insert(0, 2u8); - bytes - } - } - } -} diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index fffef2bb2..a23e47929 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -14,16 +14,35 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! Blockchain database client. - use std::path::PathBuf; +use std::collections::{HashSet, HashMap}; +use std::ops::Deref; +use std::mem; +use std::collections::VecDeque; +use std::sync::*; +use std::path::Path; +use std::fmt; +use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::time::Instant; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}; -use util::*; + +// util +use util::numbers::*; use util::panics::*; +use util::network::*; +use util::io::*; +use util::rlp; +use util::sha3::*; +use util::{Bytes}; +use util::rlp::{RlpStream, Rlp, UntrustedRlp}; +use util::journaldb; +use util::journaldb::JournalDB; +use util::kvdb::*; +use util::{Applyable, Stream, View, PerfTimer, Itertools, Colour}; + +// other use views::BlockView; use error::{ImportError, ExecutionError, BlockError, ImportResult}; -use header::{BlockNumber}; +use header::BlockNumber; use state::State; use spec::Spec; use engine::Engine; @@ -35,24 +54,29 @@ use verification::{PreverifiedBlock, Verifier}; use block::*; use transaction::{LocalizedTransaction, SignedTransaction, Action}; use blockchain::extras::TransactionAddress; -use filter::Filter; +use types::filter::Filter; use log_entry::LocalizedLogEntry; use block_queue::{BlockQueue, BlockQueueInfo}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; -use client::{BlockID, TransactionID, UncleID, TraceId, Mode, ClientConfig, DatabaseCompactionProfile, - BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, - BlockImportError}; +use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, + DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient, + TraceFilter, CallAnalytics, BlockImportError, Mode}; use client::Error as ClientError; use env_info::EnvInfo; use executive::{Executive, Executed, TransactOptions, contract_address}; use receipt::LocalizedReceipt; -pub use blockchain::CacheSize as BlockChainCacheSize; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use trace; -pub use types::blockchain_info::BlockChainInfo; -pub use types::block_status::BlockStatus; use evm::Factory as EvmFactory; use miner::{Miner, MinerService}; +use util::TrieFactory; +use ipc::IpcConfig; +use ipc::binary::{BinaryConvertError}; + +// re-export +pub use types::blockchain_info::BlockChainInfo; +pub use types::block_status::BlockStatus; +pub use blockchain::CacheSize as BlockChainCacheSize; const MAX_TX_QUEUE_SIZE: usize = 4096; const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2; @@ -452,7 +476,7 @@ impl Client { HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce(), self.trie_factory.clone()) - .expect("State root of best block header always valid.") + .expect("State root of best block header always valid.") } /// Get info on the cache. @@ -472,12 +496,12 @@ impl Client { pub fn tick(&self) { self.chain.collect_garbage(); self.block_queue.collect_garbage(); - + match self.mode { Mode::Dark(timeout) => { let mut ss = self.sleep_state.lock().unwrap(); if let Some(t) = ss.last_activity { - if Instant::now() > t + timeout { + if Instant::now() > t + timeout { self.sleep(); ss.last_activity = None; } @@ -487,14 +511,14 @@ impl Client { let mut ss = self.sleep_state.lock().unwrap(); let now = Instant::now(); if let Some(t) = ss.last_activity { - if now > t + timeout { + if now > t + timeout { self.sleep(); ss.last_activity = None; ss.last_autosleep = Some(now); } } - if let Some(t) = ss.last_autosleep { - if now > t + wakeup_after { + if let Some(t) = ss.last_autosleep { + if now > t + wakeup_after { self.wake_up(); ss.last_activity = Some(now); ss.last_autosleep = None; @@ -575,6 +599,8 @@ impl Client { } } +#[derive(Ipc)] +#[ipc(client_ident="RemoteClient")] impl BlockChainClient for Client { fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result { let header = self.block_header(BlockID::Latest).unwrap(); @@ -811,8 +837,8 @@ impl BlockChainClient for Client { receipt.logs.into_iter() .enumerate() .filter(|tuple| filter.matches(&tuple.1)) - .map(|(i, log)| LocalizedLogEntry { - entry: log, + .map(|(i, log)| LocalizedLogEntry { + entry: log, block_hash: hash.clone(), block_number: number, transaction_hash: hashes.get(index).cloned().unwrap_or_else(H256::new), @@ -822,7 +848,6 @@ impl BlockChainClient for Client { .collect::>() }) .collect::>() - }) .collect() } @@ -966,3 +991,5 @@ impl MayPanic for Client { self.panic_handler.on_panic(closure); } } + +impl IpcConfig for Client { } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 0e7298b74..7a24dc94b 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -16,7 +16,6 @@ //! Blockchain database client. -mod client; mod config; mod error; mod test_client; @@ -27,7 +26,7 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueue pub use self::error::Error; pub use types::ids::*; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; -pub use self::trace::Filter as TraceFilter; +pub use types::trace_filter::Filter as TraceFilter; pub use executive::{Executed, Executive, TransactOptions}; pub use env_info::{LastHashes, EnvInfo}; @@ -47,23 +46,21 @@ use error::{ImportResult, ExecutionError}; use receipt::LocalizedReceipt; use trace::LocalizedTrace; use evm::Factory as EvmFactory; +pub use types::call_analytics::CallAnalytics; pub use block_import_error::BlockImportError; pub use transaction_import::TransactionImportResult; +pub use transaction_import::TransactionImportError; -/// Options concerning what analytics we run on the call. -#[derive(Eq, PartialEq, Default, Clone, Copy, Debug)] -pub struct CallAnalytics { - /// Make a transaction trace. - pub transaction_tracing: bool, - /// Make a VM trace. - pub vm_tracing: bool, - /// Make a diff. - pub state_diffing: bool, +pub mod client { + //! Blockchain database client. + + #![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues + include!(concat!(env!("OUT_DIR"), "/client.ipc.rs")); } /// Blockchain database client. Owns and manages a blockchain and a block queue. pub trait BlockChainClient : Sync + Send { - + /// Should be called by any external-facing interface when actively using the client. /// To minimise chatter, there's no need to call more than once every 30s. fn keep_alive(&self) {} diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 16adfe7c3..0f615832e 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -36,7 +36,7 @@ use spec::Spec; use block_queue::BlockQueueInfo; use block::{OpenBlock, SealedBlock}; use executive::Executed; -use error::{ExecutionError}; +use error::ExecutionError; use trace::LocalizedTrace; /// Test client. diff --git a/ethcore/src/client/trace.rs b/ethcore/src/client/trace.rs index 15920dea9..3ab01757e 100644 --- a/ethcore/src/client/trace.rs +++ b/ethcore/src/client/trace.rs @@ -1,13 +1,12 @@ //! Bridge between Tracedb and Blockchain. -use std::ops::Range; -use util::{Address, H256}; +use util::{H256}; use header::BlockNumber; use trace::DatabaseExtras as TraceDatabaseExtras; use blockchain::{BlockChain, BlockProvider}; use blockchain::extras::TransactionAddress; -use super::BlockID; +pub use types::trace_filter::Filter; impl TraceDatabaseExtras for BlockChain { fn block_hash(&self, block_number: BlockNumber) -> Option { @@ -26,13 +25,3 @@ impl TraceDatabaseExtras for BlockChain { .map(|tx| tx.hash()) } } - -/// Easy to use trace filter. -pub struct Filter { - /// Range of filtering. - pub range: Range, - /// From address. - pub from_address: Vec
, - /// To address. - pub to_address: Vec
, -} diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 8c37e98ef..10786cf91 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -20,9 +20,9 @@ use util::*; use header::BlockNumber; use basic_types::LogBloom; use client::Error as ClientError; -pub use types::executed::ExecutionError; use ipc::binary::{BinaryConvertError, BinaryConvertable}; use types::block_import_error::BlockImportError; +pub use types::executed::ExecutionError; #[derive(Debug, PartialEq, Clone)] /// Errors concerning transaction processing. @@ -327,7 +327,6 @@ binary_fixed_size!(BlockError); binary_fixed_size!(ImportError); binary_fixed_size!(TransactionError); - // TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted. /*#![feature(concat_idents)] macro_rules! assimilate { diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 183a3c251..f2269501c 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -95,6 +95,8 @@ extern crate rayon; extern crate hyper; extern crate ethash; pub extern crate ethstore; +extern crate semver; +extern crate ethcore_ipc_nano as nanoipc; #[cfg(test)] extern crate ethcore_devtools as devtools; #[cfg(feature = "jit" )] extern crate evmjit; @@ -106,7 +108,6 @@ pub mod block_queue; pub mod client; pub mod error; pub mod ethereum; -pub mod filter; pub mod header; pub mod service; pub mod trace; diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index bdce9f504..f86bf3564 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -198,6 +198,7 @@ struct VerifiedTransaction { /// transaction origin origin: TransactionOrigin, } + impl VerifiedTransaction { fn new(transaction: SignedTransaction, origin: TransactionOrigin) -> Result { try!(transaction.sender()); diff --git a/ethcore/src/pod_state.rs b/ethcore/src/pod_state.rs index 76dac214b..9a11fb33f 100644 --- a/ethcore/src/pod_state.rs +++ b/ethcore/src/pod_state.rs @@ -72,7 +72,7 @@ impl fmt::Display for PodState { /// Calculate and return diff between `pre` state and `post` state. pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { - StateDiff(pre.get().keys().merge(post.get().keys()).filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect()) + StateDiff { raw: pre.get().keys().merge(post.get().keys()).filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d|(acc.clone(), d))).collect() } } #[cfg(test)] @@ -86,22 +86,22 @@ mod test { #[test] fn create_delete() { let a = PodState::from(map![ 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); - assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff(map![ + assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff { raw: map![ 1.into() => AccountDiff{ balance: Diff::Died(69.into()), nonce: Diff::Died(0.into()), code: Diff::Died(vec![]), storage: map![], } - ])); - assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff(map![ + ]}); + assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff{ raw: map![ 1.into() => AccountDiff{ balance: Diff::Born(69.into()), nonce: Diff::Born(0.into()), code: Diff::Born(vec![]), storage: map![], } - ])); + ]}); } #[test] @@ -111,22 +111,22 @@ mod test { 1.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]), 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); - assert_eq!(super::diff_pod(&a, &b), StateDiff(map![ + assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ 2.into() => AccountDiff{ balance: Diff::Born(69.into()), nonce: Diff::Born(0.into()), code: Diff::Born(vec![]), storage: map![], } - ])); - assert_eq!(super::diff_pod(&b, &a), StateDiff(map![ + ]}); + assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![ 2.into() => AccountDiff{ balance: Diff::Died(69.into()), nonce: Diff::Died(0.into()), code: Diff::Died(vec![]), storage: map![], } - ])); + ]}); } #[test] @@ -139,14 +139,14 @@ mod test { 1.into() => PodAccount::new(69.into(), 1.into(), vec![], map![]), 2.into() => PodAccount::new(69.into(), 0.into(), vec![], map![]) ]); - assert_eq!(super::diff_pod(&a, &b), StateDiff(map![ + assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ 1.into() => AccountDiff{ balance: Diff::Same, nonce: Diff::Changed(0.into(), 1.into()), code: Diff::Same, storage: map![], } - ])); + ]}); } } diff --git a/ethcore/src/tests/mod.rs b/ethcore/src/tests/mod.rs index 28c1b3b5b..db36a3762 100644 --- a/ethcore/src/tests/mod.rs +++ b/ethcore/src/tests/mod.rs @@ -15,4 +15,5 @@ // along with Parity. If not, see . pub mod helpers; -mod client; \ No newline at end of file +mod client; +mod rpc; diff --git a/ethcore/src/tests/rpc.rs b/ethcore/src/tests/rpc.rs new file mode 100644 index 000000000..a25928cf8 --- /dev/null +++ b/ethcore/src/tests/rpc.rs @@ -0,0 +1,57 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Client RPC tests + +use nanoipc; +use std::sync::Arc; +use std::sync::atomic::{Ordering, AtomicBool}; +use client::{Client, ClientConfig, RemoteClient}; +use tests::helpers::*; +use devtools::*; +use miner::Miner; +use crossbeam; +use common::IoChannel; + +pub fn run_test_worker(scope: &crossbeam::Scope, stop: Arc, socket_path: &str) { + let socket_path = socket_path.to_owned(); + scope.spawn(move || { + let temp = RandomTempPath::create_dir(); + let client = Client::new( + ClientConfig::default(), + get_test_spec(), + temp.as_path(), + Arc::new(Miner::with_spec(get_test_spec())), + IoChannel::disconnected()).unwrap(); + let mut worker = nanoipc::Worker::new(&client); + worker.add_reqrep(&socket_path).unwrap(); + while !stop.load(Ordering::Relaxed) { + worker.poll(); + } + }); +} + +#[test] +fn can_handshake() { + crossbeam::scope(|scope| { + let stop_guard = StopGuard::new(); + let socket_path = "ipc:///tmp/parity-client-rpc-10.ipc"; + run_test_worker(scope, stop_guard.share(), socket_path); + let remote_client = nanoipc::init_client::>(socket_path).unwrap(); + + assert!(remote_client.handshake().is_ok()); + }) +} diff --git a/ethcore/src/types/account_diff.rs b/ethcore/src/types/account_diff.rs index 49fc51110..5071c2f7e 100644 --- a/ethcore/src/types/account_diff.rs +++ b/ethcore/src/types/account_diff.rs @@ -16,11 +16,17 @@ //! Diff between two accounts. -use util::*; +use util::numbers::*; +use std::cmp::*; +use std::fmt; +use ipc::binary::{BinaryConvertError, BinaryConvertable}; +use util::Bytes; +use std::collections::{VecDeque, BTreeMap}; +use std::mem; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Binary)] /// Diff type for specifying a change (or not). -pub enum Diff where T: Eq { +pub enum Diff where T: Eq + BinaryConvertable { /// Both sides are the same. Same, /// Left (pre, source) side doesn't include value, right side (post, destination) does. @@ -31,7 +37,7 @@ pub enum Diff where T: Eq { Died(T), } -impl Diff where T: Eq { +impl Diff where T: Eq + BinaryConvertable { /// Construct new object with given `pre` and `post`. pub fn new(pre: T, post: T) -> Self { if pre == post { Diff::Same } else { Diff::Changed(pre, post) } } @@ -45,7 +51,7 @@ impl Diff where T: Eq { pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false }} } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Binary)] /// Account diff. pub struct AccountDiff { /// Change in balance, allowed to be `Diff::Same`. @@ -58,8 +64,8 @@ pub struct AccountDiff { pub storage: BTreeMap>, } -#[derive(Debug, PartialEq, Eq, Clone)] -/// Change in existance type. +#[derive(Debug, PartialEq, Eq, Clone, Binary)] +/// Change in existance type. // TODO: include other types of change. pub enum Existance { /// Item came into existance. @@ -94,6 +100,8 @@ impl AccountDiff { // TODO: refactor into something nicer. fn interpreted_hash(u: &H256) -> String { + use util::bytes::*; + if u <= &H256::from(0xffffffff) { format!("{} = 0x{:x}", U256::from(u.as_slice()).low_u32(), U256::from(u.as_slice()).low_u32()) } else if u <= &H256::from(u64::max_value()) { @@ -107,6 +115,8 @@ fn interpreted_hash(u: &H256) -> String { impl fmt::Display for AccountDiff { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use util::bytes::*; + match self.nonce { Diff::Born(ref x) => try!(write!(f, " non {}", x)), Diff::Changed(ref pre, ref post) => try!(write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))), diff --git a/ethcore/src/types/block_queue_info.rs b/ethcore/src/types/block_queue_info.rs new file mode 100644 index 000000000..714f84ece --- /dev/null +++ b/ethcore/src/types/block_queue_info.rs @@ -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 . + +//! Block queue info types + +use std::mem; +use ipc::binary::BinaryConvertError; +use std::collections::VecDeque; + +/// Block queue status +#[derive(Debug, Binary)] +pub struct BlockQueueInfo { + /// Number of queued blocks pending verification + pub unverified_queue_size: usize, + /// Number of verified queued blocks pending import + pub verified_queue_size: usize, + /// Number of blocks being verified + pub verifying_queue_size: usize, + /// Configured maximum number of blocks in the queue + pub max_queue_size: usize, + /// Configured maximum number of bytes to use + pub max_mem_use: usize, + /// Heap memory used in bytes + pub mem_used: usize, +} diff --git a/ethcore/src/types/call_analytics.rs b/ethcore/src/types/call_analytics.rs new file mode 100644 index 000000000..c738c15bd --- /dev/null +++ b/ethcore/src/types/call_analytics.rs @@ -0,0 +1,32 @@ +// 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 . + +//! Call analytics related types + +use std::mem; +use ipc::binary::{BinaryConvertError}; +use std::collections::VecDeque; + +/// Options concerning what analytics we run on the call. +#[derive(Eq, PartialEq, Default, Clone, Copy, Debug, Binary)] +pub struct CallAnalytics { + /// Make a transaction trace. + pub transaction_tracing: bool, + /// Make a VM trace. + pub vm_tracing: bool, + /// Make a diff. + pub state_diffing: bool, +} diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index 4d31b9fe5..293a427f7 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -27,7 +27,7 @@ use std::mem; use std::collections::VecDeque; /// Transaction execution receipt. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Binary)] pub struct Executed { /// Gas paid up front for execution of transaction. pub gas: U256, diff --git a/ethcore/src/filter.rs b/ethcore/src/types/filter.rs similarity index 93% rename from ethcore/src/filter.rs rename to ethcore/src/types/filter.rs index d99f80050..d8d5d03bd 100644 --- a/ethcore/src/filter.rs +++ b/ethcore/src/types/filter.rs @@ -20,8 +20,12 @@ use util::hash::*; use util::sha3::*; use client::BlockID; use log_entry::LogEntry; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::collections::VecDeque; /// Blockchain Filter. +#[derive(Binary)] pub struct Filter { /// Blockchain will be searched from this block. pub from_block: BlockID, @@ -29,22 +33,27 @@ pub struct Filter { /// Till this block. pub to_block: BlockID, - /// Search addresses. - /// + /// Search addresses. + /// /// If None, match all. /// If specified, log must be produced by one of these addresses. pub address: Option>, /// Search topics. - /// + /// /// If None, match all. /// If specified, log must contain one of these topics. - pub topics: [Option>; 4], + pub topics: Vec>>, } impl Clone for Filter { fn clone(&self) -> Self { - let mut topics = [None, None, None, None]; + let mut topics = [ + None, + None, + None, + None, + ]; for i in 0..4 { topics[i] = self.topics[i].clone(); } @@ -53,13 +62,13 @@ impl Clone for Filter { from_block: self.from_block.clone(), to_block: self.to_block.clone(), address: self.address.clone(), - topics: topics + topics: topics[..].to_vec() } } } impl Filter { - /// Returns combinations of each address and topic. + /// Returns combinations of each address and topic. pub fn bloom_possibilities(&self) -> Vec { let blooms = match self.address { Some(ref addresses) if !addresses.is_empty() => @@ -71,7 +80,7 @@ impl Filter { _ => vec![H2048::new()] }; - self.topics.iter().fold(blooms, | bs, topic | match *topic { + self.topics.iter().fold(blooms, |bs, topic| match *topic { None => bs, Some(ref topics) => bs.into_iter().flat_map(|bloom| { topics.into_iter().map(|topic| { @@ -111,7 +120,7 @@ mod tests { from_block: BlockID::Earliest, to_block: BlockID::Latest, address: None, - topics: [None, None, None, None] + topics: vec![None, None, None, None], }; let possibilities = none_filter.bloom_possibilities(); @@ -126,9 +135,11 @@ mod tests { from_block: BlockID::Earliest, to_block: BlockID::Latest, address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]), - topics: [ + topics: vec![ Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]), - None, None, None + None, + None, + None, ] }; @@ -142,10 +153,11 @@ mod tests { from_block: BlockID::Earliest, to_block: BlockID::Latest, address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]), - topics: [ + topics: vec![ Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]), Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]), - None, None + None, + None, ] }; @@ -162,7 +174,7 @@ mod tests { Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(), Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(), ]), - topics: [ + topics: vec![ Some(vec![ H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(), H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap() @@ -188,10 +200,11 @@ mod tests { from_block: BlockID::Earliest, to_block: BlockID::Latest, address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]), - topics: [ + topics: vec![ Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]), Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap()]), - None, None + None, + None, ] }; diff --git a/ethcore/src/types/ids.rs b/ethcore/src/types/ids.rs index 99dadc4ea..c08ab4116 100644 --- a/ethcore/src/types/ids.rs +++ b/ethcore/src/types/ids.rs @@ -47,6 +47,7 @@ pub enum TransactionID { } /// Uniquely identifies Trace. +#[derive(Binary)] pub struct TraceId { /// Transaction pub transaction: TransactionID, diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in index 97579da8a..e7731d1cc 100644 --- a/ethcore/src/types/mod.rs.in +++ b/ethcore/src/types/mod.rs.in @@ -25,5 +25,9 @@ pub mod executed; pub mod block_status; pub mod account_diff; pub mod state_diff; +pub mod block_queue_info; +pub mod filter; +pub mod trace_filter; +pub mod call_analytics; pub mod transaction_import; pub mod block_import_error; diff --git a/ethcore/src/types/state_diff.rs b/ethcore/src/types/state_diff.rs index 4257d5b07..e341b8436 100644 --- a/ethcore/src/types/state_diff.rs +++ b/ethcore/src/types/state_diff.rs @@ -16,24 +16,32 @@ //! State diff module. -use util::*; +use util::numbers::*; use account_diff::*; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::fmt; +use std::ops::*; +use std::collections::{VecDeque, BTreeMap}; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Binary)] /// Expression for the delta between two system states. Encoded the /// delta of every altered account. -pub struct StateDiff (pub BTreeMap); +pub struct StateDiff { + /// Raw diff key-value + pub raw: BTreeMap +} impl StateDiff { /// Get the actual data. pub fn get(&self) -> &BTreeMap { - &self.0 + &self.raw } } impl fmt::Display for StateDiff { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for (add, acc) in &self.0 { + for (add, acc) in &self.raw { try!(write!(f, "{} {}: {}", acc.existance(), add, acc)); } Ok(()) @@ -44,6 +52,6 @@ impl Deref for StateDiff { type Target = BTreeMap; fn deref(&self) -> &Self::Target { - &self.0 + &self.raw } } diff --git a/ethcore/src/types/trace_filter.rs b/ethcore/src/types/trace_filter.rs new file mode 100644 index 000000000..89f886af4 --- /dev/null +++ b/ethcore/src/types/trace_filter.rs @@ -0,0 +1,35 @@ +// 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 . + +//! Trace filter related types + +use std::mem; +use ipc::binary::{BinaryConvertError}; +use std::collections::VecDeque; +use std::ops::Range; +use util::{Address}; +use types::ids::BlockID; + +/// Easy to use trace filter. +#[derive(Binary)] +pub struct Filter { + /// Range of filtering. + pub range: Range, + /// From address. + pub from_address: Vec
, + /// To address. + pub to_address: Vec
, +} diff --git a/ethcore/src/types/tree_route.rs b/ethcore/src/types/tree_route.rs index 2ad0aa240..37413be57 100644 --- a/ethcore/src/types/tree_route.rs +++ b/ethcore/src/types/tree_route.rs @@ -17,9 +17,12 @@ //! Tree route info type definition use util::numbers::H256; +use ipc::BinaryConvertError; +use std::collections::VecDeque; +use std::mem; /// Represents a tree route between `from` block and `to` block: -#[derive(Debug)] +#[derive(Debug, Binary)] pub struct TreeRoute { /// A vector of hashes of all blocks, ordered from `from` to `to`. pub blocks: Vec, diff --git a/ipc/rpc/src/binary.rs b/ipc/rpc/src/binary.rs index f33d640f4..cfc7420f2 100644 --- a/ipc/rpc/src/binary.rs +++ b/ipc/rpc/src/binary.rs @@ -315,6 +315,31 @@ impl BinaryConvertable for String { } } +impl BinaryConvertable for Range where T: BinaryConvertable { + fn size(&self) -> usize { + mem::size_of::() * 2 + } + + fn from_empty_bytes() -> Result { + Err(BinaryConvertError) + } + + fn to_bytes(&self, buffer: &mut[u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> { + try!(self.start.to_bytes(&mut buffer[..mem::size_of::()], length_stack)); + try!(self.end.to_bytes(&mut buffer[mem::size_of::() + 1..], length_stack)); + Ok(()) + } + + fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result { + Ok(try!(T::from_bytes(&buffer[..mem::size_of::()], length_stack))..try!(T::from_bytes(&buffer[mem::size_of::()+1..], length_stack))) + } + + fn len_params() -> usize { + assert_eq!(0, T::len_params()); + 0 + } +} + impl BinaryConvertable for ::std::cell::RefCell where T: BinaryConvertable { fn size(&self) -> usize { self.borrow().size() @@ -539,8 +564,6 @@ binary_fixed_size!(U512); binary_fixed_size!(H256); binary_fixed_size!(H2048); binary_fixed_size!(Address); -binary_fixed_size!(Range); -binary_fixed_size!(Range); #[test] fn vec_serialize() { diff --git a/ipc/rpc/src/interface.rs b/ipc/rpc/src/interface.rs index 820994a2b..d4514467f 100644 --- a/ipc/rpc/src/interface.rs +++ b/ipc/rpc/src/interface.rs @@ -91,7 +91,7 @@ pub fn invoke(method_num: u16, params: &Option>, w: &mut W) where W: } /// IpcSocket, read/write generalization -pub trait IpcSocket: Read + Write + Sync { +pub trait IpcSocket: Read + Write + Sync + Send { } /// Basically something that needs only socket to be spawned diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 7550adb84..ed9bfd347 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -25,6 +25,7 @@ transient-hashmap = "0.1" serde_macros = { version = "0.7.0", optional = true } clippy = { version = "0.0.78", optional = true} json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" } +ethcore-ipc = { path = "../ipc/rpc" } [build-dependencies] serde_codegen = { version = "0.7.0", optional = true } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 43c120e40..73a769b13 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -32,6 +32,7 @@ extern crate ethcore; extern crate ethsync; extern crate transient_hashmap; extern crate json_ipc_server as ipc; +extern crate ethcore_ipc; #[cfg(test)] extern crate ethjson; diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 13f54feea..dd29b425e 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -30,6 +30,7 @@ use util::sha3::*; use util::rlp::{encode, decode, UntrustedRlp, View}; use ethcore::account_provider::AccountProvider; use ethcore::client::{MiningBlockChainClient, BlockID, TransactionID, UncleID}; +use ethcore::header::Header as BlockHeader; use ethcore::block::IsBlock; use ethcore::views::*; use ethcore::ethereum::Ethash; @@ -42,7 +43,6 @@ use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncIn use v1::helpers::CallRequest as CRequest; use v1::impls::{default_gas_price, dispatch_transaction, error_codes}; use serde; -use ethcore::header::Header as BlockHeader; /// Eth rpc implementation. pub struct EthClient where diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index a34bb9ae9..e07845211 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -84,7 +84,7 @@ impl Into for Filter { VariadicValue::Single(t) => Some(vec![t.into()]), VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect()) }).filter_map(|m| m).collect()).into_iter(); - [iter.next(), iter.next(), iter.next(), iter.next()] + vec![iter.next(), iter.next(), iter.next(), iter.next()] } } } diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index cc193c9fc..b9ce8a3ea 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -162,7 +162,7 @@ pub enum Diff where T: Serialize { Changed(ChangedType), } -impl From> for Diff where T: Eq, U: Serialize + From { +impl From> for Diff where T: Eq + ::ethcore_ipc::BinaryConvertable, U: Serialize + From { fn from(c: account_diff::Diff) -> Self { match c { account_diff::Diff::Same => Diff::Same, @@ -205,7 +205,7 @@ impl Serialize for StateDiff { impl From for StateDiff { fn from(c: state_diff::StateDiff) -> Self { - StateDiff(c.0.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) + StateDiff(c.raw.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) } } From 7af366c5b195fe7e7631a45406323feacdbd1114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 7 Jul 2016 03:42:49 -0400 Subject: [PATCH 3/3] Supporting /api/ping for dapps server (#1543) * Refactoring dapps to support API endpoints. * Using ContentHandler for unauthorized requests * Extracting url stuff * Adding ping endpoint * CORS support for ping request * Fixing url.is_none() * minor formatting fix [ci:skip] --- Cargo.lock | 1 + dapps/Cargo.toml | 1 + dapps/src/api/api.rs | 38 +++++++-- dapps/src/api/response.rs | 10 ++- dapps/src/api/types.rs | 2 - dapps/src/api/types.rs.in | 1 - dapps/src/handlers/echo.rs | 148 ++++++++++++++++++++++++++++++++++ dapps/src/handlers/mod.rs | 32 ++++++++ dapps/src/lib.rs | 6 +- dapps/src/router/mod.rs | 36 +-------- dapps/src/{router => }/url.rs | 10 +-- 11 files changed, 233 insertions(+), 52 deletions(-) create mode 100644 dapps/src/handlers/echo.rs rename dapps/src/{router => }/url.rs (96%) diff --git a/Cargo.lock b/Cargo.lock index e3dc752f2..eae4823f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -283,6 +283,7 @@ dependencies = [ "serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntex 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 94af56ca3..c42ed8eff 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -13,6 +13,7 @@ log = "0.3" jsonrpc-core = "2.0" jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc-http-server.git" } hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } +unicase = "1.3" url = "1.0" rustc-serialize = "0.3" serde = "0.7.0" diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs index ab3632074..84db93f63 100644 --- a/dapps/src/api/api.rs +++ b/dapps/src/api/api.rs @@ -15,20 +15,23 @@ // along with Parity. If not, see . use std::sync::Arc; -use endpoint::{Endpoint, Endpoints, Handler, EndpointPath}; -use api::types::{App, ApiError}; -use api::response::{as_json, as_json_error}; use hyper::{server, net, Decoder, Encoder, Next}; +use api::types::{App, ApiError}; +use api::response::{as_json, as_json_error, ping_response}; +use handlers::extract_url; +use endpoint::{Endpoint, Endpoints, Handler, EndpointPath}; #[derive(Clone)] pub struct RestApi { + local_domain: String, endpoints: Arc, } impl RestApi { - pub fn new(endpoints: Arc) -> Box { + pub fn new(local_domain: String, endpoints: Arc) -> Box { Box::new(RestApi { - endpoints: endpoints + local_domain: local_domain, + endpoints: endpoints, }) } @@ -59,9 +62,28 @@ struct RestApiRouter { impl server::Handler for RestApiRouter { - fn on_request(&mut self, _request: server::Request) -> Next { - self.handler = as_json(&self.api.list_apps()); - Next::write() + fn on_request(&mut self, request: server::Request) -> Next { + let url = extract_url(&request); + if url.is_none() { + // Just return 404 if we can't parse URL + return Next::write(); + } + + let url = url.expect("Check for None is above; qed"); + let endpoint = url.path.get(1).map(|v| v.as_str()); + + let handler = endpoint.and_then(|v| match v { + "apps" => Some(as_json(&self.api.list_apps())), + "ping" => Some(ping_response(&self.api.local_domain)), + _ => None, + }); + + // Overwrite default + if let Some(h) = handler { + self.handler = h; + } + + self.handler.on_request(request) } fn on_request_readable(&mut self, decoder: &mut Decoder) -> Next { diff --git a/dapps/src/api/response.rs b/dapps/src/api/response.rs index ba0922e7a..ce6eb2921 100644 --- a/dapps/src/api/response.rs +++ b/dapps/src/api/response.rs @@ -17,7 +17,7 @@ use serde::Serialize; use serde_json; use endpoint::Handler; -use handlers::ContentHandler; +use handlers::{ContentHandler, EchoHandler}; pub fn as_json(val: &T) -> Box { Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned())) @@ -26,3 +26,11 @@ pub fn as_json(val: &T) -> Box { pub fn as_json_error(val: &T) -> Box { Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned())) } + +pub fn ping_response(local_domain: &str) -> Box { + Box::new(EchoHandler::cors(vec![ + format!("http://{}", local_domain), + // Allow CORS calls also for localhost + format!("http://{}", local_domain.replace("127.0.0.1", "localhost")), + ])) +} diff --git a/dapps/src/api/types.rs b/dapps/src/api/types.rs index d99d767eb..64697123b 100644 --- a/dapps/src/api/types.rs +++ b/dapps/src/api/types.rs @@ -19,5 +19,3 @@ include!("types.rs.in"); #[cfg(not(feature = "serde_macros"))] include!(concat!(env!("OUT_DIR"), "/types.rs")); - - diff --git a/dapps/src/api/types.rs.in b/dapps/src/api/types.rs.in index 72a8cd221..a7961a144 100644 --- a/dapps/src/api/types.rs.in +++ b/dapps/src/api/types.rs.in @@ -48,4 +48,3 @@ pub struct ApiError { pub detail: String, } - diff --git a/dapps/src/handlers/echo.rs b/dapps/src/handlers/echo.rs new file mode 100644 index 000000000..b2ea8f580 --- /dev/null +++ b/dapps/src/handlers/echo.rs @@ -0,0 +1,148 @@ +// 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 . + +//! Echo Handler + +use std::io::Read; +use hyper::{header, server, Decoder, Encoder, Next}; +use hyper::method::Method; +use hyper::net::HttpStream; +use unicase::UniCase; +use super::ContentHandler; + +#[derive(Debug, PartialEq)] +/// Type of Cross-Origin request +enum Cors { + /// Not a Cross-Origin request - no headers needed + No, + /// Cross-Origin request with valid Origin + Allowed(String), + /// Cross-Origin request with invalid Origin + Forbidden, +} + +pub struct EchoHandler { + safe_origins: Vec, + content: String, + cors: Cors, + handler: Option, +} + +impl EchoHandler { + + pub fn cors(safe_origins: Vec) -> Self { + EchoHandler { + safe_origins: safe_origins, + content: String::new(), + cors: Cors::Forbidden, + handler: None, + } + } + + fn cors_header(&self, origin: Option) -> Cors { + fn origin_is_allowed(origin: &str, safe_origins: &[String]) -> bool { + for safe in safe_origins { + if origin.starts_with(safe) { + return true; + } + } + false + } + + match origin { + Some(ref origin) if origin_is_allowed(origin, &self.safe_origins) => { + Cors::Allowed(origin.clone()) + }, + None => Cors::No, + _ => Cors::Forbidden, + } + } +} + +impl server::Handler for EchoHandler { + fn on_request(&mut self, request: server::Request) -> Next { + let origin = request.headers().get_raw("origin") + .and_then(|list| list.get(0)) + .and_then(|origin| String::from_utf8(origin.clone()).ok()); + + self.cors = self.cors_header(origin); + + // Don't even read the payload if origin is forbidden! + if let Cors::Forbidden = self.cors { + self.handler = Some(ContentHandler::ok(String::new(), "text/plain".into())); + Next::write() + } else { + Next::read() + } + } + + fn on_request_readable(&mut self, decoder: &mut Decoder) -> Next { + match decoder.read_to_string(&mut self.content) { + Ok(0) => { + self.handler = Some(ContentHandler::ok(self.content.clone(), "application/json".into())); + Next::write() + }, + Ok(_) => Next::read(), + Err(e) => match e.kind() { + ::std::io::ErrorKind::WouldBlock => Next::read(), + _ => Next::end(), + } + } + } + + fn on_response(&mut self, res: &mut server::Response) -> Next { + if let Cors::Allowed(ref domain) = self.cors { + let mut headers = res.headers_mut(); + headers.set(header::Allow(vec![Method::Options, Method::Post, Method::Get])); + headers.set(header::AccessControlAllowHeaders(vec![ + UniCase("origin".to_owned()), + UniCase("content-type".to_owned()), + UniCase("accept".to_owned()), + ])); + headers.set(header::AccessControlAllowOrigin::Value(domain.clone())); + } + self.handler.as_mut().unwrap().on_response(res) + } + + fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { + self.handler.as_mut().unwrap().on_response_writable(encoder) + } +} + +#[test] +fn should_return_correct_cors_value() { + // given + let safe_origins = vec!["chrome-extension://".to_owned(), "http://localhost:8080".to_owned()]; + let cut = EchoHandler { + safe_origins: safe_origins, + content: String::new(), + cors: Cors::No, + handler: None, + }; + + // when + let res1 = cut.cors_header(Some("http://ethcore.io".into())); + let res2 = cut.cors_header(Some("http://localhost:8080".into())); + let res3 = cut.cors_header(Some("chrome-extension://deadbeefcafe".into())); + let res4 = cut.cors_header(None); + + + // then + assert_eq!(res1, Cors::Forbidden); + assert_eq!(res2, Cors::Allowed("http://localhost:8080".into())); + assert_eq!(res3, Cors::Allowed("chrome-extension://deadbeefcafe".into())); + assert_eq!(res4, Cors::No); +} diff --git a/dapps/src/handlers/mod.rs b/dapps/src/handlers/mod.rs index 933538655..5fa9fda95 100644 --- a/dapps/src/handlers/mod.rs +++ b/dapps/src/handlers/mod.rs @@ -17,9 +17,41 @@ //! Hyper handlers implementations. mod auth; +mod echo; mod content; mod redirect; pub use self::auth::AuthRequiredHandler; +pub use self::echo::EchoHandler; pub use self::content::ContentHandler; pub use self::redirect::Redirection; + +use url::Url; +use hyper::{server, header, net, uri}; + +pub fn extract_url(req: &server::Request) -> Option { + match *req.uri() { + uri::RequestUri::AbsoluteUri(ref url) => { + match Url::from_generic_url(url.clone()) { + Ok(url) => Some(url), + _ => None, + } + }, + uri::RequestUri::AbsolutePath(ref path) => { + // Attempt to prepend the Host header (mandatory in HTTP/1.1) + let url_string = match req.headers().get::() { + Some(ref host) => { + format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path) + }, + None => return None, + }; + + match Url::parse(&url_string) { + Ok(url) => Some(url), + _ => None, + } + }, + _ => None, + } +} + diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 1c550fb07..ccdf172ce 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -45,8 +45,9 @@ #[macro_use] extern crate log; -extern crate url; +extern crate url as url_lib; extern crate hyper; +extern crate unicase; extern crate serde; extern crate serde_json; extern crate jsonrpc_core; @@ -63,6 +64,7 @@ mod handlers; mod rpc; mod api; mod proxypac; +mod url; use std::sync::{Arc, Mutex}; use std::net::SocketAddr; @@ -121,7 +123,7 @@ impl Server { let special = Arc::new({ let mut special = HashMap::new(); special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, panic_handler.clone())); - special.insert(router::SpecialEndpoint::Api, api::RestApi::new(endpoints.clone())); + special.insert(router::SpecialEndpoint::Api, api::RestApi::new(format!("{}", addr), endpoints.clone())); special.insert(router::SpecialEndpoint::Utils, apps::utils()); special }); diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index f04c8b614..8fbb37cf3 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -17,22 +17,18 @@ //! Router implementation //! Processes request handling authorization and dispatching it to proper application. -mod url; pub mod auth; use DAPPS_DOMAIN; use std::sync::Arc; use std::collections::HashMap; -use url::Host; -use hyper; -use hyper::{server, uri, header}; -use hyper::{Next, Encoder, Decoder}; +use url::{Url, Host}; +use hyper::{self, server, Next, Encoder, Decoder}; use hyper::net::HttpStream; use apps; use endpoint::{Endpoint, Endpoints, EndpointPath}; -use self::url::Url; +use handlers::{Redirection, extract_url}; use self::auth::{Authorization, Authorized}; -use handlers::Redirection; /// Special endpoints are accessible on every domain (every dapp) #[derive(Debug, PartialEq, Hash, Eq)] @@ -123,32 +119,6 @@ impl Router { } } -fn extract_url(req: &server::Request) -> Option { - match *req.uri() { - uri::RequestUri::AbsoluteUri(ref url) => { - match Url::from_generic_url(url.clone()) { - Ok(url) => Some(url), - _ => None, - } - }, - uri::RequestUri::AbsolutePath(ref path) => { - // Attempt to prepend the Host header (mandatory in HTTP/1.1) - let url_string = match req.headers().get::() { - Some(ref host) => { - format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path) - }, - None => return None, - }; - - match Url::parse(&url_string) { - Ok(url) => Some(url), - _ => None, - } - }, - _ => None, - } -} - fn extract_endpoint(url: &Option) -> (Option, SpecialEndpoint) { fn special_endpoint(url: &Url) -> SpecialEndpoint { if url.path.len() <= 1 { diff --git a/dapps/src/router/url.rs b/dapps/src/url.rs similarity index 96% rename from dapps/src/router/url.rs rename to dapps/src/url.rs index b96168239..c9eb2f78f 100644 --- a/dapps/src/router/url.rs +++ b/dapps/src/url.rs @@ -16,14 +16,14 @@ //! HTTP/HTTPS URL type. Based on URL type from Iron library. -use url::Host; -use url::{self}; +use url_lib::{self}; +pub use url_lib::Host; /// HTTP/HTTPS URL type for Iron. #[derive(PartialEq, Eq, Clone, Debug)] pub struct Url { /// Raw url of url - pub raw: url::Url, + pub raw: url_lib::Url, /// The host field of the URL, probably a domain. pub host: Host, @@ -62,14 +62,14 @@ impl Url { /// See: http://url.spec.whatwg.org/#special-scheme pub fn parse(input: &str) -> Result { // Parse the string using rust-url, then convert. - match url::Url::parse(input) { + match url_lib::Url::parse(input) { Ok(raw_url) => Url::from_generic_url(raw_url), Err(e) => Err(format!("{}", e)) } } /// Create a `Url` from a `rust-url` `Url`. - pub fn from_generic_url(raw_url: url::Url) -> Result { + pub fn from_generic_url(raw_url: url_lib::Url) -> Result { // Map empty usernames to None. let username = match raw_url.username() { "" => None,