diff --git a/Cargo.lock b/Cargo.lock index c409cf58a..b0c9e5eb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ name = "parity" version = "1.2.0" dependencies = [ - "clippy 0.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.67 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)", "daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)", @@ -119,7 +119,7 @@ dependencies = [ [[package]] name = "clippy" -version = "0.0.64" +version = "0.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quine-mc_cluskey 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -147,11 +147,6 @@ dependencies = [ "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "crossbeam" version = "0.2.9" @@ -235,11 +230,13 @@ name = "ethcore" version = "1.2.0" dependencies = [ "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.64 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.67 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethash 1.2.0", "ethcore-devtools 1.2.0", + "ethcore-ipc 1.2.0", + "ethcore-ipc-codegen 1.2.0", "ethcore-util 1.2.0", "ethjson 0.1.0", "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -248,6 +245,7 @@ dependencies = [ "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -293,7 +291,7 @@ dependencies = [ name = "ethcore-rpc" version = "1.2.0" dependencies = [ - "clippy 0.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.67 (registry+https://github.com/rust-lang/crates.io-index)", "ethash 1.2.0", "ethcore 1.2.0", "ethcore-util 1.2.0", @@ -318,7 +316,7 @@ dependencies = [ "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "bigint 0.1.0", "chrono 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.67 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -351,15 +349,16 @@ dependencies = [ name = "ethcore-webapp" version = "1.2.0" dependencies = [ - "clippy 0.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.67 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-rpc 1.2.0", "ethcore-util 1.2.0", "hyper 0.9.3 (git+https://github.com/ethcore/hyper)", "jsonrpc-core 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-http-server 5.1.0 (git+https://github.com/ethcore/jsonrpc-http-server.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-status 0.3.7 (git+https://github.com/ethcore/parity-status.git)", - "parity-wallet 0.2.0 (git+https://github.com/ethcore/parity-wallet.git)", + "parity-idmanager 0.1.3 (git+https://github.com/ethcore/parity-idmanager-rs.git)", + "parity-status 0.4.1 (git+https://github.com/ethcore/parity-status.git)", + "parity-wallet 0.3.0 (git+https://github.com/ethcore/parity-wallet.git)", "parity-webapp 0.1.0 (git+https://github.com/ethcore/parity-webapp.git)", "url 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -380,7 +379,7 @@ dependencies = [ name = "ethminer" version = "1.2.0" dependencies = [ - "clippy 0.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.67 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.2.0", "ethcore-util 1.2.0", @@ -394,7 +393,7 @@ dependencies = [ name = "ethsync" version = "1.2.0" dependencies = [ - "clippy 0.0.64 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.67 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.2.0", "ethcore-util 1.2.0", @@ -830,18 +829,26 @@ name = "odds" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "parity-idmanager" +version = "0.1.3" +source = "git+https://github.com/ethcore/parity-idmanager-rs.git#efb69592b87854f41d8882de75982c8f1e748666" +dependencies = [ + "parity-webapp 0.1.0 (git+https://github.com/ethcore/parity-webapp.git)", +] + [[package]] name = "parity-status" -version = "0.3.7" -source = "git+https://github.com/ethcore/parity-status.git#b0ae32a7fe2f843e4e22dc38903fd2c3e7fb0763" +version = "0.4.1" +source = "git+https://github.com/ethcore/parity-status.git#f121ebd1f49986545d9fc262ba210cdf07039e6d" dependencies = [ "parity-webapp 0.1.0 (git+https://github.com/ethcore/parity-webapp.git)", ] [[package]] name = "parity-wallet" -version = "0.2.0" -source = "git+https://github.com/ethcore/parity-wallet.git#18a602fd25f3e9bcdbc5528bf61ba627665d962c" +version = "0.3.0" +source = "git+https://github.com/ethcore/parity-wallet.git#664fd2b85dd94ca184868bd3965e14a4ba68c03f" dependencies = [ "parity-webapp 0.1.0 (git+https://github.com/ethcore/parity-webapp.git)", ] diff --git a/Cargo.toml b/Cargo.toml index af97737e5..6d3ddf357 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ daemonize = "0.2" num_cpus = "0.2" number_prefix = "0.2" rpassword = "0.2.1" -clippy = { version = "0.0.64", optional = true} +clippy = { version = "0.0.67", optional = true} ethcore = { path = "ethcore" } ethcore-util = { path = "util" } ethsync = { path = "sync" } diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index b5f50b207..795eaf90f 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -5,6 +5,11 @@ license = "GPL-3.0" name = "ethcore" version = "1.2.0" authors = ["Ethcore "] +build = "build.rs" + +[build-dependencies] +syntex = "*" +"ethcore-ipc-codegen" = { path = "../ipc/codegen" } [dependencies] log = "0.3" @@ -17,12 +22,13 @@ ethcore-util = { path = "../util" } evmjit = { path = "../evmjit", optional = true } ethash = { path = "../ethash" } num_cpus = "0.2" -clippy = { version = "0.0.64", optional = true} -crossbeam = "0.1.5" +clippy = { version = "0.0.67", optional = true} +crossbeam = "0.2.9" lazy_static = "0.1" ethcore-devtools = { path = "../devtools" } ethjson = { path = "../json" } bloomchain = "0.1" +"ethcore-ipc" = { path = "../ipc/rpc" } [features] jit = ["evmjit"] diff --git a/ethcore/build.rs b/ethcore/build.rs new file mode 100644 index 000000000..dadcce13a --- /dev/null +++ b/ethcore/build.rs @@ -0,0 +1,33 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +extern crate syntex; +extern crate ethcore_ipc_codegen as codegen; + +use std::env; +use std::path::Path; + +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + // serialization pass + { + let src = Path::new("src/types/mod.rs.in"); + let dst = Path::new(&out_dir).join("types.rs"); + let mut registry = syntex::Registry::new(); + codegen::register(&mut registry); + registry.expand("", &src, &dst).unwrap(); + } +} diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 7e5f896fd..d95152341 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -27,7 +27,7 @@ use chainfilter::{ChainFilter, BloomIndex, FilterDataSource}; use blockchain::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}; use blockchain::best_block::BestBlock; use blockchain::bloom_indexer::BloomIndexer; -use blockchain::tree_route::TreeRoute; +use types::tree_route::TreeRoute; use blockchain::update::ExtrasUpdate; use blockchain::{CacheSize, ImportRoute}; use db::{Writable, Readable, Key, CacheUpdatePolicy}; diff --git a/ethcore/src/blockchain/mod.rs b/ethcore/src/blockchain/mod.rs index 29a4ee684..62196825f 100644 --- a/ethcore/src/blockchain/mod.rs +++ b/ethcore/src/blockchain/mod.rs @@ -21,7 +21,6 @@ mod best_block; mod block_info; mod bloom_indexer; mod cache; -mod tree_route; mod update; mod import_route; #[cfg(test)] @@ -29,5 +28,5 @@ mod generator; pub use self::blockchain::{BlockProvider, BlockChain, BlockChainConfig}; pub use self::cache::CacheSize; -pub use self::tree_route::TreeRoute; +pub use types::tree_route::TreeRoute; pub use self::import_route::ImportRoute; diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index e385f39a8..0d61325d9 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -44,34 +44,8 @@ use receipt::LocalizedReceipt; pub use blockchain::CacheSize as BlockChainCacheSize; use trace::{TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; use trace; - -/// General block status -#[derive(Debug, Eq, PartialEq)] -pub enum BlockStatus { - /// Part of the blockchain. - InChain, - /// Queued for import. - Queued, - /// Known as bad. - Bad, - /// Unknown. - Unknown, -} - -/// Information about the blockchain gathered together. -#[derive(Debug)] -pub struct BlockChainInfo { - /// Blockchain difficulty. - pub total_difficulty: U256, - /// Block queue difficulty. - pub pending_total_difficulty: U256, - /// Genesis block hash. - pub genesis_hash: H256, - /// Best blockchain block hash. - pub best_block_hash: H256, - /// Best blockchain block number. - pub best_block_number: BlockNumber -} +pub use types::blockchain_info::BlockChainInfo; +pub use types::block_status::BlockStatus; impl fmt::Display for BlockChainInfo { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 9dddeceb7..a748ff900 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -18,13 +18,12 @@ mod client; mod config; -mod ids; mod test_client; mod trace; pub use self::client::*; pub use self::config::{ClientConfig, BlockQueueConfig, BlockChainConfig, Switch}; -pub use self::ids::{BlockId, TransactionId, UncleId, TraceId}; +pub use types::ids::*; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::trace::Filter as TraceFilter; pub use executive::{Executed, Executive, TransactOptions}; diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 2e007703b..edec7c959 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -20,49 +20,7 @@ use util::*; use header::BlockNumber; use basic_types::LogBloom; -/// Result of executing the transaction. -#[derive(PartialEq, Debug)] -pub enum ExecutionError { - /// Returned when there gas paid for transaction execution is - /// lower than base gas required. - NotEnoughBaseGas { - /// Absolute minimum gas required. - required: U256, - /// Gas provided. - got: U256 - }, - /// Returned when block (gas_used + gas) > gas_limit. - /// - /// If gas =< gas_limit, upstream may try to execute the transaction - /// in next block. - BlockGasLimitReached { - /// Gas limit of block for transaction. - gas_limit: U256, - /// Gas used in block prior to transaction. - gas_used: U256, - /// Amount of gas in block. - gas: U256 - }, - /// Returned when transaction nonce does not match state nonce. - InvalidNonce { - /// Nonce expected. - expected: U256, - /// Nonce found. - got: U256 - }, - /// Returned when cost of transaction (value + gas_price * gas) exceeds - /// current sender balance. - NotEnoughCash { - /// Minimum required balance. - required: U512, - /// Actual balance. - got: U512 - }, - /// Returned when internal evm error occurs. - Internal, - /// Returned when generic transaction occurs - TransactionMalformed(String), -} +pub use types::executed::ExecutionError; #[derive(Debug, PartialEq)] /// Errors concerning transaction processing. diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 179195078..74e0499c1 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -24,6 +24,8 @@ use substate::*; use trace::{Trace, Tracer, NoopTracer, ExecutiveTracer}; use crossbeam; +pub use types::executed::{Executed, ExecutionResult}; + /// Max depth to avoid stack overflow (when it's reached we start a new thread with VM) /// TODO [todr] We probably need some more sophisticated calculations here (limit on my machine 132) /// Maybe something like here: `https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp` @@ -45,45 +47,6 @@ pub struct TransactOptions { pub check_nonce: bool, } -/// Transaction execution receipt. -#[derive(Debug, PartialEq, Clone)] -pub struct Executed { - /// Gas paid up front for execution of transaction. - pub gas: U256, - - /// Gas used during execution of transaction. - pub gas_used: U256, - - /// Gas refunded after the execution of transaction. - /// To get gas that was required up front, add `refunded` and `gas_used`. - pub refunded: U256, - - /// Cumulative gas used in current block so far. - /// - /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` - /// - /// where `tn` is current transaction. - pub cumulative_gas_used: U256, - - /// Vector of logs generated by transaction. - pub logs: Vec, - - /// Addresses of contracts created during execution of transaction. - /// Ordered from earliest creation. - /// - /// eg. sender creates contract A and A in constructor creates contract B - /// - /// B creation ends first, and it will be the first element of the vector. - pub contracts_created: Vec
, - /// Transaction output. - pub output: Bytes, - /// The trace of this transaction. - pub trace: Option, -} - -/// Transaction execution result. -pub type ExecutionResult = Result; - /// Transaction executor. pub struct Executive<'a> { state: &'a mut State, diff --git a/ethcore/src/header.rs b/ethcore/src/header.rs index 1e1a54d57..0941c9ca6 100644 --- a/ethcore/src/header.rs +++ b/ethcore/src/header.rs @@ -61,7 +61,7 @@ pub struct Header { /// Block difficulty. pub difficulty: U256, - /// Block seal. + /// Vector of post-RLP-encoded fields. pub seal: Vec, /// The memoized hash of the RLP representation *including* the seal fields. @@ -201,7 +201,7 @@ impl Header { *self.bare_hash.borrow_mut() = None; } - // TODO: make these functions traity + // TODO: make these functions traity /// Place this header into an RLP stream `s`, optionally `with_seal`. pub fn stream_rlp(&self, s: &mut RlpStream, with_seal: Seal) { s.begin_list(13 + match with_seal { Seal::With => self.seal.len(), _ => 0 }); @@ -219,8 +219,8 @@ impl Header { s.append(&self.timestamp); s.append(&self.extra_data); if let Seal::With = with_seal { - for b in &self.seal { - s.append_raw(&b, 1); + for b in &self.seal { + s.append_raw(&b, 1); } } } @@ -275,4 +275,32 @@ impl Encodable for Header { #[cfg(test)] mod tests { + use rustc_serialize::hex::FromHex; + use util::rlp::{decode, encode}; + use super::Header; + + #[test] + fn test_header_seal_fields() { + // that's rlp of block header created with ethash engine. + let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); + let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); + + let header: Header = decode(&header_rlp); + let seal_fields = header.seal; + assert_eq!(seal_fields.len(), 2); + assert_eq!(seal_fields[0], mix_hash); + assert_eq!(seal_fields[1], nonce); + } + + #[test] + fn decode_and_encode_header() { + // that's rlp of block header created with ethash engine. + let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + + let header: Header = decode(&header_rlp); + let encoded_header = encode(&header).to_vec(); + + assert_eq!(header_rlp, encoded_header); + } } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 43b4c5099..f443647bd 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -27,6 +27,10 @@ #![cfg_attr(feature="dev", allow(clone_on_copy))] // In most cases it expresses function flow better #![cfg_attr(feature="dev", allow(if_not_else))] +// TODO [todr] a lot of warnings to be fixed +#![cfg_attr(feature="dev", allow(needless_borrow))] +#![cfg_attr(feature="dev", allow(assign_op_pattern))] + //! Ethcore library //! @@ -85,6 +89,7 @@ extern crate num_cpus; extern crate crossbeam; extern crate ethjson; extern crate bloomchain; +#[macro_use] extern crate ethcore_ipc as ipc; #[cfg(test)] extern crate ethcore_devtools as devtools; #[cfg(feature = "jit" )] extern crate evmjit; @@ -98,12 +103,9 @@ pub mod ethereum; pub mod filter; pub mod header; pub mod service; -pub mod log_entry; pub mod trace; pub mod spec; -pub mod transaction; pub mod views; -pub mod receipt; pub mod pod_state; mod db; @@ -128,9 +130,12 @@ mod executive; mod externalities; mod verification; mod blockchain; +mod types; #[cfg(test)] mod tests; #[cfg(test)] #[cfg(feature="json-tests")] mod json_tests; + +pub use types::*; diff --git a/ethcore/src/trace/flat.rs b/ethcore/src/trace/flat.rs index ae3f22050..f9d3b0831 100644 --- a/ethcore/src/trace/flat.rs +++ b/ethcore/src/trace/flat.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Flat trace module + use trace::BlockTraces; use super::trace::{Trace, Action, Res}; diff --git a/ethcore/src/trace/mod.rs b/ethcore/src/trace/mod.rs index 831a55cd5..01763a167 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/src/trace/mod.rs @@ -21,20 +21,18 @@ mod bloom; mod config; mod db; mod executive_tracer; -mod filter; -mod flat; +pub mod flat; mod import; -mod localized; mod noop_tracer; -pub mod trace; +pub use types::trace_types::*; pub use self::block::BlockTraces; pub use self::config::{Config, Switch}; pub use self::db::TraceDB; -pub use self::trace::Trace; +pub use types::trace_types::trace::Trace; pub use self::noop_tracer::NoopTracer; pub use self::executive_tracer::ExecutiveTracer; -pub use self::filter::{Filter, AddressesFilter}; +pub use types::trace_types::filter::{Filter, AddressesFilter}; pub use self::import::ImportRequest; pub use self::localized::LocalizedTrace; use util::{Bytes, Address, U256, H256}; diff --git a/ethcore/src/types/block_status.rs b/ethcore/src/types/block_status.rs new file mode 100644 index 000000000..ca0f47227 --- /dev/null +++ b/ethcore/src/types/block_status.rs @@ -0,0 +1,33 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Block status description module + +use ipc::binary::BinaryConvertError; +use std::collections::VecDeque; + +/// General block status +#[derive(Debug, Eq, PartialEq, Binary)] +pub enum BlockStatus { + /// Part of the blockchain. + InChain, + /// Queued for import. + Queued, + /// Known as bad. + Bad, + /// Unknown. + Unknown, +} diff --git a/ethcore/src/types/blockchain_info.rs b/ethcore/src/types/blockchain_info.rs new file mode 100644 index 000000000..4e8634dba --- /dev/null +++ b/ethcore/src/types/blockchain_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 . + +//! Blockhain info type definition + +use util::numbers::*; +use header::BlockNumber; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::collections::VecDeque; + +/// Information about the blockchain gathered together. +#[derive(Debug, Binary)] +pub struct BlockChainInfo { + /// Blockchain difficulty. + pub total_difficulty: U256, + /// Block queue difficulty. + pub pending_total_difficulty: U256, + /// Genesis block hash. + pub genesis_hash: H256, + /// Best blockchain block hash. + pub best_block_hash: H256, + /// Best blockchain block number. + pub best_block_number: BlockNumber +} diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs new file mode 100644 index 000000000..f03a1c26b --- /dev/null +++ b/ethcore/src/types/executed.rs @@ -0,0 +1,109 @@ +// 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 . + +//! Transaction execution format module. + +use util::numbers::*; +use util::Bytes; +use trace::Trace; +use types::log_entry::LogEntry; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::collections::VecDeque; + +/// Transaction execution receipt. +#[derive(Debug, PartialEq, Clone, Binary)] +pub struct Executed { + /// Gas paid up front for execution of transaction. + pub gas: U256, + + /// Gas used during execution of transaction. + pub gas_used: U256, + + /// Gas refunded after the execution of transaction. + /// To get gas that was required up front, add `refunded` and `gas_used`. + pub refunded: U256, + + /// Cumulative gas used in current block so far. + /// + /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` + /// + /// where `tn` is current transaction. + pub cumulative_gas_used: U256, + + /// Vector of logs generated by transaction. + pub logs: Vec, + + /// Addresses of contracts created during execution of transaction. + /// Ordered from earliest creation. + /// + /// eg. sender creates contract A and A in constructor creates contract B + /// + /// B creation ends first, and it will be the first element of the vector. + pub contracts_created: Vec
, + /// Transaction output. + pub output: Bytes, + /// The trace of this transaction. + pub trace: Option, +} + +/// Result of executing the transaction. +#[derive(PartialEq, Debug, Binary)] +pub enum ExecutionError { + /// Returned when there gas paid for transaction execution is + /// lower than base gas required. + NotEnoughBaseGas { + /// Absolute minimum gas required. + required: U256, + /// Gas provided. + got: U256 + }, + /// Returned when block (gas_used + gas) > gas_limit. + /// + /// If gas =< gas_limit, upstream may try to execute the transaction + /// in next block. + BlockGasLimitReached { + /// Gas limit of block for transaction. + gas_limit: U256, + /// Gas used in block prior to transaction. + gas_used: U256, + /// Amount of gas in block. + gas: U256 + }, + /// Returned when transaction nonce does not match state nonce. + InvalidNonce { + /// Nonce expected. + expected: U256, + /// Nonce found. + got: U256 + }, + /// Returned when cost of transaction (value + gas_price * gas) exceeds + /// current sender balance. + NotEnoughCash { + /// Minimum required balance. + required: U512, + /// Actual balance. + got: U512 + }, + /// Returned when internal evm error occurs. + Internal, + /// Returned when generic transaction occurs + TransactionMalformed(String), +} + + +/// Transaction execution result. +pub type ExecutionResult = Result; diff --git a/ethcore/src/client/ids.rs b/ethcore/src/types/ids.rs similarity index 85% rename from ethcore/src/client/ids.rs rename to ethcore/src/types/ids.rs index 39a19c82d..8fffcb8f7 100644 --- a/ethcore/src/client/ids.rs +++ b/ethcore/src/types/ids.rs @@ -18,10 +18,13 @@ use util::hash::H256; use header::BlockNumber; -use util::bytes::{FromRawBytes, FromBytesError, ToBytesWithMap, Populatable}; +use ipc::binary::BinaryConvertError; +use ipc::binary::BinaryConvertable; +use std::mem; +use std::collections::VecDeque; /// Uniquely identifies block. -#[derive(Debug, PartialEq, Clone, Hash, Eq)] +#[derive(Debug, PartialEq, Clone, Hash, Eq, Binary)] pub enum BlockId { /// Block's sha3. /// Querying by hash is always faster. @@ -35,7 +38,7 @@ pub enum BlockId { } /// Uniquely identifies transaction. -#[derive(Debug, PartialEq, Clone, Hash, Eq)] +#[derive(Debug, PartialEq, Clone, Hash, Eq, Binary)] pub enum TransactionId { /// Transaction's sha3. Hash(H256), @@ -53,13 +56,10 @@ pub struct TraceId { } /// Uniquely identifies Uncle. +#[derive(Debug)] pub struct UncleId ( /// Block id. pub BlockId, /// Position in block. pub usize ); - -sized_binary_map!(TransactionId); -sized_binary_map!(UncleId); -sized_binary_map!(BlockId); diff --git a/ethcore/src/log_entry.rs b/ethcore/src/types/log_entry.rs similarity index 91% rename from ethcore/src/log_entry.rs rename to ethcore/src/types/log_entry.rs index 2a7ca080f..7d8dccc6c 100644 --- a/ethcore/src/log_entry.rs +++ b/ethcore/src/types/log_entry.rs @@ -14,15 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! Block log. +//! Log entry type definition. -use util::*; +use util::numbers::*; +use std::ops::Deref; +use util::rlp::*; +use util::Bytes; +use util::HeapSizeOf; +use util::sha3::*; use basic_types::LogBloom; use header::BlockNumber; use ethjson; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::collections::VecDeque; /// A record of execution for a `LOG` operation. -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Binary)] pub struct LogEntry { /// The address of the contract executing at the point of the `LOG` operation. pub address: Address, @@ -77,7 +85,7 @@ impl From for LogEntry { } /// Log localized in a blockchain. -#[derive(Default, Debug, PartialEq, Clone)] +#[derive(Default, Debug, PartialEq, Clone, Binary)] pub struct LocalizedLogEntry { /// Plain log entry. pub entry: LogEntry, diff --git a/ethcore/src/types/mod.rs b/ethcore/src/types/mod.rs new file mode 100644 index 000000000..112f79c32 --- /dev/null +++ b/ethcore/src/types/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Types used in the public api + +#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues +include!(concat!(env!("OUT_DIR"), "/types.rs")); diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in new file mode 100644 index 000000000..1f67a9184 --- /dev/null +++ b/ethcore/src/types/mod.rs.in @@ -0,0 +1,25 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +pub mod transaction; +pub mod ids; +pub mod receipt; +pub mod tree_route; +pub mod blockchain_info; +pub mod log_entry; +pub mod trace_types; +pub mod executed; +pub mod block_status; diff --git a/ethcore/src/receipt.rs b/ethcore/src/types/receipt.rs similarity index 80% rename from ethcore/src/receipt.rs rename to ethcore/src/types/receipt.rs index 2fbd3f14c..cc83e2f97 100644 --- a/ethcore/src/receipt.rs +++ b/ethcore/src/types/receipt.rs @@ -16,13 +16,18 @@ //! Receipt -use util::*; +use util::numbers::*; +use util::rlp::*; +use util::HeapSizeOf; use basic_types::LogBloom; use header::BlockNumber; use log_entry::{LogEntry, LocalizedLogEntry}; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::collections::VecDeque; /// Information describing execution of a transaction. -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, Binary)] pub struct Receipt { /// The state root after executing the transaction. pub state_root: H256, @@ -76,7 +81,7 @@ impl HeapSizeOf for Receipt { } /// Receipt with additional info. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Binary)] pub struct LocalizedReceipt { /// Transaction hash. pub transaction_hash: H256, @@ -98,7 +103,7 @@ pub struct LocalizedReceipt { #[test] fn test_basic() { - let expected = FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let expected = ::rustc_serialize::hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( x!("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee"), x!(0x40cae), diff --git a/ethcore/src/trace/filter.rs b/ethcore/src/types/trace_types/filter.rs similarity index 93% rename from ethcore/src/trace/filter.rs rename to ethcore/src/types/trace_types/filter.rs index d1f699c52..c02a15c03 100644 --- a/ethcore/src/trace/filter.rs +++ b/ethcore/src/types/trace_types/filter.rs @@ -14,41 +14,49 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Trace filters type definitions + use std::ops::Range; use bloomchain::{Filter as BloomFilter, Bloom, Number}; use util::{Address, FixedHash}; use util::sha3::Hashable; use basic_types::LogBloom; -use super::flat::FlatTrace; -use super::trace::Action; +use trace::flat::FlatTrace; +use types::trace_types::trace::Action; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::collections::VecDeque; /// Addresses filter. /// /// Used to create bloom possibilities and match filters. -pub struct AddressesFilter(Vec
); +#[derive(Binary)] +pub struct AddressesFilter { + list: Vec
+} impl From> for AddressesFilter { fn from(addresses: Vec
) -> Self { - AddressesFilter(addresses) + AddressesFilter { list: addresses } } } impl AddressesFilter { /// Returns true if address matches one of the searched addresses. pub fn matches(&self, address: &Address) -> bool { - self.matches_all() || self.0.contains(address) + self.matches_all() || self.list.contains(address) } /// Returns true if this address filter matches everything. pub fn matches_all(&self) -> bool { - self.0.is_empty() + self.list.is_empty() } /// Returns blooms of this addresses filter. pub fn blooms(&self) -> Vec { - match self.0.is_empty() { + match self.list.is_empty() { true => vec![LogBloom::new()], - false => self.0.iter() + false => self.list.iter() .map(|address| LogBloom::from_bloomed(&address.sha3())) .collect() } @@ -56,11 +64,11 @@ impl AddressesFilter { /// Returns vector of blooms zipped with blooms of this addresses filter. pub fn with_blooms(&self, blooms: Vec) -> Vec { - match self.0.is_empty() { + match self.list.is_empty() { true => blooms, false => blooms .into_iter() - .flat_map(|bloom| self.0.iter() + .flat_map(|bloom| self.list.iter() .map(|address| bloom.with_bloomed(&address.sha3())) .collect::>()) .collect() @@ -68,6 +76,7 @@ impl AddressesFilter { } } +#[derive(Binary)] /// Traces filter. pub struct Filter { /// Block range. diff --git a/ethcore/src/trace/localized.rs b/ethcore/src/types/trace_types/localized.rs similarity index 89% rename from ethcore/src/trace/localized.rs rename to ethcore/src/types/trace_types/localized.rs index ef18d6898..334d7b518 100644 --- a/ethcore/src/trace/localized.rs +++ b/ethcore/src/types/trace_types/localized.rs @@ -14,12 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Localized traces type definitions + use util::H256; use super::trace::{Action, Res}; use header::BlockNumber; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::collections::VecDeque; /// Localized trace. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Binary)] pub struct LocalizedTrace { /// Type of action performed by a transaction. pub action: Action, diff --git a/ethcore/src/types/trace_types/mod.rs b/ethcore/src/types/trace_types/mod.rs new file mode 100644 index 000000000..db429a8f4 --- /dev/null +++ b/ethcore/src/types/trace_types/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Types used in the public api + +pub mod filter; +pub mod trace; +pub mod localized; diff --git a/ethcore/src/trace/trace.rs b/ethcore/src/types/trace_types/trace.rs similarity index 96% rename from ethcore/src/trace/trace.rs rename to ethcore/src/types/trace_types/trace.rs index f7efe9721..f8fe07360 100644 --- a/ethcore/src/trace/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -15,14 +15,18 @@ // along with Parity. If not, see . //! Tracing datatypes. + use util::{U256, Bytes, Address, FixedHash}; use util::rlp::*; use util::sha3::Hashable; use action_params::ActionParams; use basic_types::LogBloom; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::collections::VecDeque; /// `Call` result. -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq, Default, Binary)] pub struct CallResult { /// Gas used by call. pub gas_used: U256, @@ -51,7 +55,7 @@ impl Decodable for CallResult { } /// `Create` result. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Binary)] pub struct CreateResult { /// Gas used by create. pub gas_used: U256, @@ -84,7 +88,7 @@ impl Decodable for CreateResult { } /// Description of a _call_ action, either a `CALL` operation or a message transction. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Binary)] pub struct Call { /// The sending account. pub from: Address, @@ -146,7 +150,7 @@ impl Call { } /// Description of a _create_ action, either a `CREATE` operation or a create transction. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Binary)] pub struct Create { /// The address of the creator. pub from: Address, @@ -202,7 +206,7 @@ impl Create { } /// Description of an action that we trace; will be either a call or a create. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Binary)] pub enum Action { /// It's a call action. Call(Call), @@ -249,7 +253,7 @@ impl Action { } /// The result of the performed action. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Binary)] pub enum Res { /// Successful call action result. Call(CallResult), @@ -300,7 +304,7 @@ impl Decodable for Res { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Binary)] /// A trace; includes a description of the action being traced and sub traces of each interior action. pub struct Trace { /// The number of EVM execution environments active when this action happened; 0 if it's diff --git a/ethcore/src/transaction.rs b/ethcore/src/types/transaction.rs similarity index 91% rename from ethcore/src/transaction.rs rename to ethcore/src/types/transaction.rs index ca54f26bb..842decd88 100644 --- a/ethcore/src/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -16,13 +16,21 @@ //! Transaction data structure. -use util::*; +use util::numbers::*; +use std::ops::Deref; +use util::rlp::*; +use util::sha3::*; +use util::{UtilError, CryptoError, Bytes, Signature, Secret, ec}; +use std::cell::*; use error::*; use evm::Schedule; use header::BlockNumber; use ethjson; +use ipc::binary::BinaryConvertError; +use std::mem; +use std::collections::VecDeque; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Binary)] /// Transaction action type. pub enum Action { /// Create creates new contract. @@ -48,7 +56,7 @@ impl Decodable for Action { /// A set of information describing an externally-originating message call /// or contract creation operation. -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq, Binary)] pub struct Transaction { /// Nonce. pub nonce: U256, @@ -183,7 +191,7 @@ impl Transaction { } /// Signed transaction information. -#[derive(Debug, Clone, Eq)] +#[derive(Debug, Clone, Eq, Binary)] pub struct SignedTransaction { /// Plain Transaction. unsigned: Transaction, @@ -310,7 +318,7 @@ impl SignedTransaction { } try!(self.sender()); if self.gas < U256::from(self.gas_required(&schedule)) { - Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}))) + Err(From::from(TransactionError::InvalidGasLimit(::util::OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}))) } else { Ok(self) } @@ -318,7 +326,7 @@ impl SignedTransaction { } /// Signed Transaction that is a part of canon blockchain. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Binary)] pub struct LocalizedTransaction { /// Signed part. pub signed: SignedTransaction, @@ -340,7 +348,7 @@ impl Deref for LocalizedTransaction { #[test] fn sender_test() { - let t: SignedTransaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); + let t: SignedTransaction = decode(&::rustc_serialize::hex::FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); assert_eq!(t.data, b""); assert_eq!(t.gas, U256::from(0x5208u64)); assert_eq!(t.gas_price, U256::from(0x01u64)); @@ -354,7 +362,7 @@ fn sender_test() { #[test] fn signing() { - let key = KeyPair::create().unwrap(); + let key = ::util::crypto::KeyPair::create().unwrap(); let t = Transaction { action: Action::Create, nonce: U256::from(42), diff --git a/ethcore/src/blockchain/tree_route.rs b/ethcore/src/types/tree_route.rs similarity index 96% rename from ethcore/src/blockchain/tree_route.rs rename to ethcore/src/types/tree_route.rs index 3c4906449..2ad0aa240 100644 --- a/ethcore/src/blockchain/tree_route.rs +++ b/ethcore/src/types/tree_route.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Tree route info type definition + use util::numbers::H256; /// Represents a tree route between `from` block and `to` block: diff --git a/ethcore/src/views.rs b/ethcore/src/views.rs index 11e26eb5f..d47fec95a 100644 --- a/ethcore/src/views.rs +++ b/ethcore/src/views.rs @@ -301,11 +301,11 @@ impl<'a> HeaderView<'a> { /// Returns block extra data. pub fn extra_data(&self) -> Bytes { self.rlp.val_at(12) } - /// Returns block seal. + /// Returns a vector of post-RLP-encoded seal fields. pub fn seal(&self) -> Vec { let mut seal = vec![]; for i in 13..self.rlp.item_count() { - seal.push(self.rlp.val_at(i)); + seal.push(self.rlp.at(i).as_raw().to_vec()); } seal } @@ -316,3 +316,25 @@ impl<'a> Hashable for HeaderView<'a> { self.rlp.as_raw().sha3() } } + +#[cfg(test)] +mod tests { + use rustc_serialize::hex::FromHex; + use util::rlp::View; + use super::BlockView; + + #[test] + fn test_header_view_seal_fields() { + // that's rlp of block created with ethash engine. + let block_rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); + let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); + let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); + + let block_view = BlockView::new(&block_rlp); + let header_view = block_view.header_view(); + let seal_fields = header_view.seal(); + assert_eq!(seal_fields.len(), 2); + assert_eq!(seal_fields[0], mix_hash); + assert_eq!(seal_fields[1], nonce); + } +} diff --git a/ipc/codegen/src/serialization.rs b/ipc/codegen/src/serialization.rs index 6a9784790..e8f3d2cd8 100644 --- a/ipc/codegen/src/serialization.rs +++ b/ipc/codegen/src/serialization.rs @@ -175,6 +175,11 @@ fn binary_expr_struct( ) -> Result { let size_exprs: Vec> = fields.iter().enumerate().map(|(index, field)| { + + if ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)) == "u8" { + return quote_expr!(cx, 1); + } + let field_type_ident = builder.id( &::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty))); @@ -228,23 +233,34 @@ fn binary_expr_struct( }, }; - write_stmts.push(quote_stmt!(cx, let next_line = offset + match $field_type_ident_qualified::len_params() { - 0 => mem::size_of::<$field_type_ident>(), - _ => { let size = $member_expr .size(); length_stack.push_back(size); size }, - }).unwrap()); - - write_stmts.push(quote_stmt!(cx, - if let Err(e) = $member_expr .to_bytes(&mut buffer[offset..next_line], length_stack) { return Err(e) };).unwrap()); + if ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)) == "u8" { + write_stmts.push(quote_stmt!(cx, let next_line = offset + 1;).unwrap()); + write_stmts.push(quote_stmt!(cx, buffer[offset] = $member_expr; ).unwrap()); + } + else { + write_stmts.push(quote_stmt!(cx, let next_line = offset + match $field_type_ident_qualified::len_params() { + 0 => mem::size_of::<$field_type_ident>(), + _ => { let size = $member_expr .size(); length_stack.push_back(size); size }, + }).unwrap()); + write_stmts.push(quote_stmt!(cx, + if let Err(e) = $member_expr .to_bytes(&mut buffer[offset..next_line], length_stack) { return Err(e) };).unwrap()); + } write_stmts.push(quote_stmt!(cx, offset = next_line; ).unwrap()); let field_index = builder.id(&format!("{}", index)); map_stmts.push(quote_stmt!(cx, map[$field_index] = total;).unwrap()); - map_stmts.push(quote_stmt!(cx, let size = match $field_type_ident_qualified::len_params() { - 0 => mem::size_of::<$field_type_ident>(), - _ => length_stack.pop_front().unwrap(), - }).unwrap()); - map_stmts.push(quote_stmt!(cx, total = total + size;).unwrap()); + + if ::syntax::print::pprust::ty_to_string(&codegen::strip_ptr(&field.ty)) == "u8" { + map_stmts.push(quote_stmt!(cx, total = total + 1;).unwrap()); + } + else { + map_stmts.push(quote_stmt!(cx, let size = match $field_type_ident_qualified::len_params() { + 0 => mem::size_of::<$field_type_ident>(), + _ => length_stack.pop_front().unwrap(), + }).unwrap()); + map_stmts.push(quote_stmt!(cx, total = total + size;).unwrap()); + } }; let read_expr = match fields.iter().any(|f| codegen::has_ptr(&f.ty)) { @@ -366,6 +382,8 @@ fn fields_sequence( use syntax::parse::token; use syntax::ast::TokenTree::Token; + let named_members = fields.iter().any(|f| f.ident.is_some()); + ::quasi::parse_expr_panic(&mut ::syntax::parse::new_parser_from_tts( ext_cx.parse_sess(), ext_cx.cfg(), @@ -373,7 +391,12 @@ fn fields_sequence( let _sp = ext_cx.call_site(); let mut tt = ::std::vec::Vec::new(); tt.push(Token(_sp, token::Ident(variant_ident.clone()))); - tt.push(Token(_sp, token::OpenDelim(token::Paren))); + if named_members { + tt.push(Token(_sp, token::OpenDelim(token::Brace))); + } + else { + tt.push(Token(_sp, token::OpenDelim(token::Paren))); + } for (idx, field) in fields.iter().enumerate() { if field.ident.is_some() { @@ -381,6 +404,21 @@ fn fields_sequence( tt.push(Token(_sp, token::Colon)); } + // special case for u8, it just takes byte form sequence + if ::syntax::print::pprust::ty_to_string(&field.ty) == "u8" { + tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer")))); + + tt.push(Token(_sp, token::OpenDelim(token::Bracket))); + tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map")))); + tt.push(Token(_sp, token::OpenDelim(token::Bracket))); + tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx))))); + tt.push(Token(_sp, token::CloseDelim(token::Bracket))); + tt.push(Token(_sp, token::CloseDelim(token::Bracket))); + + tt.push(Token(_sp, token::Comma)); + continue; + } + tt.push(Token(_sp, token::Ident(ext_cx.ident_of("try!")))); tt.push(Token(_sp, token::OpenDelim(token::Paren))); tt.push( @@ -393,6 +431,7 @@ fn fields_sequence( tt.push(Token(_sp, token::OpenDelim(token::Paren))); tt.push(Token(_sp, token::BinOp(token::And))); + tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer")))); tt.push(Token(_sp, token::OpenDelim(token::Bracket))); @@ -418,8 +457,12 @@ fn fields_sequence( tt.push(Token(_sp, token::CloseDelim(token::Paren))); tt.push(Token(_sp, token::Comma)); } - tt.push(Token(_sp, token::CloseDelim(token::Paren))); - + if named_members { + tt.push(Token(_sp, token::CloseDelim(token::Brace))); + } + else { + tt.push(Token(_sp, token::CloseDelim(token::Paren))); + } tt }) ).unwrap() @@ -455,6 +498,21 @@ fn named_fields_sequence( tt.push(Token(_sp, token::Ident(field.ident.clone().unwrap()))); tt.push(Token(_sp, token::Colon)); + // special case for u8, it just takes byte form sequence + if ::syntax::print::pprust::ty_to_string(&field.ty) == "u8" { + tt.push(Token(_sp, token::Ident(ext_cx.ident_of("buffer")))); + + tt.push(Token(_sp, token::OpenDelim(token::Bracket))); + tt.push(Token(_sp, token::Ident(ext_cx.ident_of("map")))); + tt.push(Token(_sp, token::OpenDelim(token::Bracket))); + tt.push(Token(_sp, token::Ident(ext_cx.ident_of(&format!("{}", idx))))); + tt.push(Token(_sp, token::CloseDelim(token::Bracket))); + tt.push(Token(_sp, token::CloseDelim(token::Bracket))); + + tt.push(Token(_sp, token::Comma)); + continue; + } + tt.push(Token(_sp, token::Ident(ext_cx.ident_of("try!")))); tt.push(Token(_sp, token::OpenDelim(token::Paren))); tt.push(Token( @@ -573,7 +631,6 @@ fn binary_expr_variant( .map(|(id, field)|(field.ident.unwrap(), builder.pat().ref_id(id)))) .build(); - let binary_expr = try!(binary_expr_struct( cx, &builder, @@ -593,7 +650,7 @@ fn binary_expr_variant( let buffer = &mut buffer[1..]; $write_expr }), - read: quote_arm!(cx, $pat => { $read_expr } ), + read: quote_arm!(cx, $variant_index_ident => { $read_expr } ), }) }, } diff --git a/ipc/rpc/src/binary.rs b/ipc/rpc/src/binary.rs index 3ba172c6e..4fb359f7c 100644 --- a/ipc/rpc/src/binary.rs +++ b/ipc/rpc/src/binary.rs @@ -17,9 +17,10 @@ //! Binary representation of types use util::bytes::Populatable; -use util::numbers::{U256, H256, H2048, Address}; +use util::numbers::{U256, U512, H256, H2048, Address}; use std::mem; use std::collections::VecDeque; +use std::ops::Range; #[derive(Debug)] pub struct BinaryConvertError; @@ -232,6 +233,29 @@ impl BinaryConvertable for ::std::cell::RefCell where T: BinaryConvertable } } +impl BinaryConvertable for ::std::cell::Cell where T: BinaryConvertable + Copy { + fn size(&self) -> usize { + self.get().size() + } + + fn from_empty_bytes() -> Result { + Ok(::std::cell::Cell::new(try!(T::from_empty_bytes()))) + } + + fn from_bytes(buffer: &[u8], length_stack: &mut VecDeque) -> Result { + Ok(::std::cell::Cell::new(try!(T::from_bytes(buffer, length_stack)))) + } + + fn to_bytes(&self, buffer: &mut [u8], length_stack: &mut VecDeque) -> Result<(), BinaryConvertError> { + try!(self.get().to_bytes(buffer, length_stack)); + Ok(()) + } + + fn len_params() -> usize { + T::len_params() + } +} + impl BinaryConvertable for Vec { fn size(&self) -> usize { self.len() @@ -365,8 +389,9 @@ pub fn serialize(t: &T) -> Result, BinaryConvertEr Ok(buff.into_inner()) } +#[macro_export] macro_rules! binary_fixed_size { - ($target_ty: ident) => { + ($target_ty: ty) => { impl BinaryConvertable for $target_ty { fn from_bytes(bytes: &[u8], _length_stack: &mut VecDeque) -> Result { match bytes.len().cmp(&::std::mem::size_of::<$target_ty>()) { @@ -398,9 +423,12 @@ binary_fixed_size!(usize); binary_fixed_size!(i32); binary_fixed_size!(bool); binary_fixed_size!(U256); +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/tests/binary.rs.in b/ipc/tests/binary.rs.in index 710752237..74dd39c1b 100644 --- a/ipc/tests/binary.rs.in +++ b/ipc/tests/binary.rs.in @@ -36,3 +36,9 @@ pub struct DoubleRoot { pub struct ReferenceStruct<'a> { pub ref_data: &'a u64, } + +#[derive(Binary, PartialEq, Debug)] +pub enum EnumWithStruct { + Left, + Right { how_much: u64 }, +} diff --git a/ipc/tests/run.rs b/ipc/tests/run.rs index c07145f77..cdda5275b 100644 --- a/ipc/tests/run.rs +++ b/ipc/tests/run.rs @@ -16,9 +16,7 @@ #![allow(dead_code)] -extern crate bincode; extern crate ethcore_ipc as ipc; -extern crate serde; extern crate ethcore_devtools as devtools; extern crate semver; extern crate nanomsg; diff --git a/json/Cargo.toml b/json/Cargo.toml index e6b914f4f..93b86cedb 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -10,7 +10,7 @@ rustc-serialize = "0.3" serde = "0.7.0" serde_json = "0.7.0" serde_macros = { version = "0.7.0", optional = true } -clippy = { version = "0.0.64", optional = true} +clippy = { version = "0.0.67", optional = true} [build-dependencies] serde_codegen = { version = "0.7.0", optional = true } diff --git a/miner/Cargo.toml b/miner/Cargo.toml index cf28fd03e..d42753a39 100644 --- a/miner/Cargo.toml +++ b/miner/Cargo.toml @@ -17,7 +17,7 @@ log = "0.3" env_logger = "0.3" rustc-serialize = "0.3" rayon = "0.3.1" -clippy = { version = "0.0.64", optional = true} +clippy = { version = "0.0.67", optional = true} [features] default = [] diff --git a/parity/cli.rs b/parity/cli.rs index 009f03b89..90d7167dc 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -102,7 +102,9 @@ Sealing/Mining Options: [default: 0.005]. The minimum gas price is set accordingly. --usd-per-eth SOURCE USD value of a single ETH. SOURCE may be either an - amount in USD or a web service [default: etherscan]. + amount in USD, a web service or 'auto' to use each + web service in turn and fallback on the last known + good value [default: auto]. --gas-floor-target GAS Amount of gas per block to target when sealing a new block [default: 4712388]. --author ADDRESS Specify the block author (aka "coinbase") address diff --git a/parity/configuration.rs b/parity/configuration.rs index 96885f83b..ba7789c08 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -37,6 +37,11 @@ pub struct Configuration { pub args: Args } +pub struct Directories { + pub keys: String, + pub db: String, +} + impl Configuration { pub fn parse() -> Self { Configuration { @@ -60,11 +65,6 @@ impl Configuration { self.args.flag_maxpeers.unwrap_or(self.args.flag_peers) as u32 } - pub fn path(&self) -> String { - let d = self.args.flag_datadir.as_ref().unwrap_or(&self.args.flag_db_path); - d.replace("$HOME", env::home_dir().unwrap().to_str().unwrap()) - } - pub fn author(&self) -> Address { let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author); Address::from_str(clean_0x(d)).unwrap_or_else(|_| { @@ -91,11 +91,18 @@ impl Configuration { die!("{}: Invalid basic transaction price given in USD. Must be a decimal number.", self.args.flag_usd_per_tx) }); let usd_per_eth = match self.args.flag_usd_per_eth.as_str() { + "auto" => PriceInfo::get().map_or_else(|| { + let last_known_good = 9.69696; + // TODO: use #1083 to read last known good value. + last_known_good + }, |x| x.ethusd), "etherscan" => PriceInfo::get().map_or_else(|| { die!("Unable to retrieve USD value of ETH from etherscan. Rerun with a different value for --usd-per-eth.") }, |x| x.ethusd), x => FromStr::from_str(x).unwrap_or_else(|_| die!("{}: Invalid ether price given in USD. Must be a decimal number.", x)) }; + // TODO: use #1083 to write last known good value as use_per_eth. + let wei_per_usd: f32 = 1.0e18 / usd_per_eth; let gas_per_tx: f32 = 21000.0; let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; @@ -113,14 +120,6 @@ impl Configuration { } } - pub fn keys_path(&self) -> String { - self.args.flag_keys_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap()) - } - - pub fn keys_iterations(&self) -> u32 { - self.args.flag_keys_iterations - } - pub fn spec(&self) -> Spec { match self.chain().as_str() { "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(), @@ -272,19 +271,18 @@ impl Configuration { cors.map_or_else(Vec::new, |c| c.split(',').map(|s| s.to_owned()).collect()) } - fn geth_ipc_path() -> &'static str { - if cfg!(target_os = "macos") { - "$HOME/Library/Ethereum/geth.ipc" - } else { - "$HOME/.ethereum/geth.ipc" - } + fn geth_ipc_path() -> String { + path::ethereum::with_default("geth.ipc").to_str().unwrap().to_owned() + } + + pub fn keys_iterations(&self) -> u32 { + self.args.flag_keys_iterations } pub fn ipc_settings(&self) -> IpcConfiguration { IpcConfiguration { enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off), - socket_addr: if self.args.flag_geth { Self::geth_ipc_path().to_owned() } else { self.args.flag_ipcpath.clone().unwrap_or(self.args.flag_ipc_path.clone()) } - .replace("$HOME", env::home_dir().unwrap().to_str().unwrap()), + socket_addr: self.ipc_path(), apis: self.args.flag_ipcapi.clone().unwrap_or(self.args.flag_ipc_apis.clone()), } } @@ -301,6 +299,37 @@ impl Configuration { rpc_port: self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port), } } + + pub fn directories(&self) -> Directories { + let db_path = Configuration::replace_home( + &self.args.flag_datadir.as_ref().unwrap_or(&self.args.flag_db_path)); + ::std::fs::create_dir_all(&db_path).unwrap_or_else(|e| die_with_io_error("main", e)); + + let keys_path = Configuration::replace_home(&self.args.flag_keys_path); + ::std::fs::create_dir_all(&db_path).unwrap_or_else(|e| die_with_io_error("main", e)); + + Directories { + keys: keys_path, + db: db_path, + } + } + + pub fn keys_path(&self) -> String { + self.directories().keys + } + + pub fn path(&self) -> String { + self.directories().db + } + + fn replace_home(arg: &str) -> String { + arg.replace("$HOME", env::home_dir().unwrap().to_str().unwrap()) + } + + fn ipc_path(&self) -> String { + if self.args.flag_geth { Self::geth_ipc_path() } + else { Configuration::replace_home(&self.args.flag_ipcpath.clone().unwrap_or(self.args.flag_ipc_path.clone())) } + } } #[cfg(test)] diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index c4de059bf..0f1f6925a 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -22,7 +22,7 @@ ethminer = { path = "../miner" } rustc-serialize = "0.3" transient-hashmap = "0.1" serde_macros = { version = "0.7.0", optional = true } -clippy = { version = "0.0.64", optional = true} +clippy = { version = "0.0.67", optional = true} json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" } [build-dependencies] diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 51946b97b..6fddf7b4f 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -123,8 +123,8 @@ impl EthClient fn uncle(&self, id: UncleId) -> Result { let client = take_weak!(self.client); - match client.uncle(id).and_then(|u| client.block_total_difficulty(BlockId::Hash(u.hash())).map(|diff| (diff, u))) { - Some((difficulty, uncle)) => { + match client.uncle(id).and_then(|u| client.block_total_difficulty(BlockId::Hash(u.parent_hash().clone())).map(|diff| (diff, u))) { + Some((parent_difficulty, uncle)) => { let block = Block { hash: OptionalValue::Value(uncle.hash()), parent_hash: uncle.parent_hash, @@ -139,7 +139,7 @@ impl EthClient logs_bloom: uncle.log_bloom, timestamp: U256::from(uncle.timestamp), difficulty: uncle.difficulty, - total_difficulty: difficulty, + total_difficulty: uncle.difficulty + parent_difficulty, receipts_root: uncle.receipts_root, extra_data: Bytes::new(uncle.extra_data), seal_fields: uncle.seal.into_iter().map(Bytes::new).collect(), diff --git a/sync/Cargo.toml b/sync/Cargo.toml index cb9190f75..b51bed259 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -10,7 +10,7 @@ authors = ["Ethcore . + +use util::*; +use ethcore::header::{ Header as BlockHeader}; + +known_heap_size!(0, HeaderId, SyncBlock); + +/// Block data with optional body. +struct SyncBlock { + header: Bytes, + body: Option, +} + +/// Used to identify header by transactions and uncles hashes +#[derive(Eq, PartialEq, Hash)] +struct HeaderId { + transactions_root: H256, + uncles: H256 +} + +/// A collection of blocks and subchain pointers being downloaded. This keeps track of +/// which headers/bodies need to be downloaded, which are being downloaded and also holds +/// the downloaded blocks. +pub struct BlockCollection { + /// Heads of subchains to download + heads: Vec, + /// Downloaded blocks. + blocks: HashMap, + /// Downloaded blocks by parent. + parents: HashMap, + /// Used to map body to header. + header_ids: HashMap, + /// First block in `blocks`. + head: Option, + /// Set of block header hashes being downloaded + downloading_headers: HashSet, + /// Set of block bodies being downloaded identified by block hash. + downloading_bodies: HashSet, +} + +impl BlockCollection { + /// Create a new instance. + pub fn new() -> BlockCollection { + BlockCollection { + blocks: HashMap::new(), + header_ids: HashMap::new(), + heads: Vec::new(), + parents: HashMap::new(), + head: None, + downloading_headers: HashSet::new(), + downloading_bodies: HashSet::new(), + } + } + + /// Clear everything. + pub fn clear(&mut self) { + self.blocks.clear(); + self.parents.clear(); + self.header_ids.clear(); + self.heads.clear(); + self.head = None; + self.downloading_headers.clear(); + self.downloading_bodies.clear(); + } + + /// Reset collection for a new sync round with given subchain block hashes. + pub fn reset_to(&mut self, hashes: Vec) { + self.clear(); + self.heads = hashes; + } + + /// Insert a set of headers into collection and advance subchain head pointers. + pub fn insert_headers(&mut self, headers: Vec) { + for h in headers.into_iter() { + if let Err(e) = self.insert_header(h) { + trace!(target: "sync", "Ignored invalid header: {:?}", e); + } + } + self.update_heads(); + } + + /// Insert a collection of block bodies for previously downloaded headers. + pub fn insert_bodies(&mut self, bodies: Vec) { + for b in bodies.into_iter() { + if let Err(e) = self.insert_body(b) { + trace!(target: "sync", "Ignored invalid body: {:?}", e); + } + } + } + + /// Returns a set of block hashes that require a body download. The returned set is marked as being downloaded. + pub fn needed_bodies(&mut self, count: usize, _ignore_downloading: bool) -> Vec { + if self.head.is_none() { + return Vec::new(); + } + let mut needed_bodies: Vec = Vec::new(); + let mut head = self.head; + while head.is_some() && needed_bodies.len() < count { + head = self.parents.get(&head.unwrap()).cloned(); + if let Some(head) = head { + match self.blocks.get(&head) { + Some(block) if block.body.is_none() && !self.downloading_bodies.contains(&head) => { + needed_bodies.push(head.clone()); + } + _ => (), + } + } + } + self.downloading_bodies.extend(needed_bodies.iter()); + needed_bodies + } + + /// Returns a set of block hashes that require a header download. The returned set is marked as being downloaded. + pub fn needed_headers(&mut self, count: usize, ignore_downloading: bool) -> Option<(H256, usize)> { + // find subchain to download + let mut download = None; + { + for h in &self.heads { + if ignore_downloading || !self.downloading_headers.contains(&h) { + self.downloading_headers.insert(h.clone()); + download = Some(h.clone()); + break; + } + } + } + download.map(|h| (h, count)) + } + + /// Unmark a header as being downloaded. + pub fn clear_header_download(&mut self, hash: &H256) { + self.downloading_headers.remove(hash); + } + + /// Unmark a block body as being downloaded. + pub fn clear_body_download(&mut self, hash: &H256) { + self.downloading_bodies.remove(hash); + } + + /// Get a valid chain of blocks ordered in descending order and ready for importing into blockchain. + pub fn drain(&mut self) -> Vec { + if self.blocks.is_empty() || self.head.is_none() { + return Vec::new(); + } + + let mut drained = Vec::new(); + let mut hashes = Vec::new(); + { + let mut blocks = Vec::new(); + let mut head = self.head; + while head.is_some() { + head = self.parents.get(&head.unwrap()).cloned(); + if let Some(head) = head { + match self.blocks.get(&head) { + Some(block) if block.body.is_some() => { + blocks.push(block); + hashes.push(head); + self.head = Some(head); + } + _ => break, + } + } + } + + for block in blocks.drain(..) { + let mut block_rlp = RlpStream::new_list(3); + block_rlp.append_raw(&block.header, 1); + let body = Rlp::new(&block.body.as_ref().unwrap()); // incomplete blocks are filtered out in the loop above + block_rlp.append_raw(body.at(0).as_raw(), 1); + block_rlp.append_raw(body.at(1).as_raw(), 1); + drained.push(block_rlp.out()); + } + } + for h in hashes { + self.blocks.remove(&h); + } + trace!("Drained {} blocks, new head :{:?}", drained.len(), self.head); + drained + } + + /// Check if the collection is empty. We consider the syncing round complete once + /// there is no block data left and only a single or none head pointer remains. + pub fn is_empty(&self) -> bool { + return self.heads.len() == 0 || + (self.heads.len() == 1 && self.head.map_or(false, |h| h == self.heads[0])) + } + + /// Chech is collection contains a block header. + pub fn contains(&self, hash: &H256) -> bool { + self.blocks.contains_key(hash) + } + + /// Return heap size. + pub fn heap_size(&self) -> usize { + //TODO: other collections + self.blocks.heap_size_of_children() + } + + /// Check if given block hash is marked as being downloaded. + pub fn is_downloading(&self, hash: &H256) -> bool { + self.downloading_headers.contains(hash) || self.downloading_bodies.contains(hash) + } + + fn insert_body(&mut self, b: Bytes) -> Result<(), UtilError> { + let body = UntrustedRlp::new(&b); + let tx = try!(body.at(0)); + let tx_root = ordered_trie_root(tx.iter().map(|r| r.as_raw().to_vec()).collect()); //TODO: get rid of vectors here + let uncles = try!(body.at(1)).as_raw().sha3(); + let header_id = HeaderId { + transactions_root: tx_root, + uncles: uncles + }; + match self.header_ids.get(&header_id).cloned() { + Some(h) => { + self.header_ids.remove(&header_id); + self.downloading_bodies.remove(&h); + match self.blocks.get_mut(&h) { + Some(ref mut block) => { + trace!(target: "sync", "Got body {}", h); + block.body = Some(body.as_raw().to_vec()); + }, + None => warn!("Got body with no header {}", h) + } + } + None => trace!(target: "sync", "Ignored unknown/stale block body") + }; + Ok(()) + } + + fn insert_header(&mut self, header: Bytes) -> Result { + let info: BlockHeader = try!(UntrustedRlp::new(&header).as_val()); + let hash = info.hash(); + if self.blocks.contains_key(&hash) { + return Ok(hash); + } + match self.head { + None if hash == self.heads[0] => { + trace!("New head {}", hash); + self.head = Some(info.parent_hash); + }, + _ => () + } + + let mut block = SyncBlock { + header: header, + body: None, + }; + let header_id = HeaderId { + transactions_root: info.transactions_root, + uncles: info.uncles_hash + }; + if header_id.transactions_root == rlp::SHA3_NULL_RLP && header_id.uncles == rlp::SHA3_EMPTY_LIST_RLP { + // empty body, just mark as downloaded + let mut body_stream = RlpStream::new_list(2); + body_stream.append_raw(&rlp::NULL_RLP, 1); + body_stream.append_raw(&rlp::EMPTY_LIST_RLP, 1); + block.body = Some(body_stream.out()); + } + else { + self.header_ids.insert(header_id, hash.clone()); + } + + self.parents.insert(info.parent_hash.clone(), hash.clone()); + self.blocks.insert(hash.clone(), block); + Ok(hash) + } + + // update subchain headers + fn update_heads(&mut self) { + let mut new_heads = Vec::new(); + let old_subchains: HashSet<_> = { self.heads.iter().map(Clone::clone).collect() }; + for s in self.heads.drain(..) { + let mut h = s.clone(); + loop { + match self.parents.get(&h) { + Some(next) => { + h = next.clone(); + if old_subchains.contains(&h) { + trace!("Completed subchain {:?}", s); + break; // reached head of the other subchain, merge by not adding + } + }, + _ => { + new_heads.push(h); + break; + } + } + } + } + self.heads = new_heads; + } +} + +#[cfg(test)] +mod test { + use super::BlockCollection; + use ethcore::client::{TestBlockChainClient, EachBlockWith, BlockId, BlockChainClient}; + use ethcore::views::HeaderView; + use ethcore::header::BlockNumber; + use util::*; + + fn is_empty(bc: &BlockCollection) -> bool { + bc.heads.is_empty() && + bc.blocks.is_empty() && + bc.parents.is_empty() && + bc.header_ids.is_empty() && + bc.head.is_none() && + bc.downloading_headers.is_empty() && + bc.downloading_bodies.is_empty() + } + + #[test] + fn create_clear() { + let mut bc = BlockCollection::new(); + 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(); + bc.reset_to(hashes); + assert!(!is_empty(&bc)); + bc.clear(); + assert!(is_empty(&bc)); + } + + #[test] + fn insert_headers() { + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + 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 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(); + bc.reset_to(heads); + assert!(!bc.is_empty()); + assert_eq!(hashes[0], bc.heads[0]); + assert!(bc.needed_bodies(1, false).is_empty()); + assert!(!bc.contains(&hashes[0])); + assert!(!bc.is_downloading(&hashes[0])); + + let (h, n) = bc.needed_headers(6, false).unwrap(); + assert!(bc.is_downloading(&hashes[0])); + assert_eq!(hashes[0], h); + assert_eq!(n, 6); + assert_eq!(bc.downloading_headers.len(), 1); + assert!(bc.drain().is_empty()); + + bc.insert_headers(headers[0..6].to_vec()); + assert_eq!(hashes[5], bc.heads[0]); + for h in &hashes[0..6] { + bc.clear_header_download(h) + } + assert_eq!(bc.downloading_headers.len(), 0); + assert!(!bc.is_downloading(&hashes[0])); + assert!(bc.contains(&hashes[0])); + + assert_eq!(&bc.drain()[..], &blocks[0..6]); + assert!(!bc.contains(&hashes[0])); + assert_eq!(hashes[5], bc.head.unwrap()); + + let (h, _) = bc.needed_headers(6, false).unwrap(); + assert_eq!(hashes[5], h); + let (h, _) = bc.needed_headers(6, false).unwrap(); + assert_eq!(hashes[20], h); + bc.insert_headers(headers[10..16].to_vec()); + assert!(bc.drain().is_empty()); + bc.insert_headers(headers[5..10].to_vec()); + assert_eq!(&bc.drain()[..], &blocks[6..16]); + assert_eq!(hashes[15], bc.heads[0]); + + bc.insert_headers(headers[16..].to_vec()); + bc.drain(); + assert!(bc.is_empty()); + } + + #[test] + fn insert_headers_with_gap() { + let mut bc = BlockCollection::new(); + assert!(is_empty(&bc)); + 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 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(); + bc.reset_to(heads); + + bc.insert_headers(headers[2..22].to_vec()); + assert_eq!(hashes[0], bc.heads[0]); + assert_eq!(hashes[21], bc.heads[1]); + assert!(bc.head.is_none()); + bc.insert_headers(headers[0..2].to_vec()); + assert!(bc.head.is_some()); + assert_eq!(hashes[21], bc.heads[0]); + } +} + diff --git a/util/Cargo.toml b/util/Cargo.toml index 0936eae16..1ed4e8e61 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -28,7 +28,7 @@ crossbeam = "0.2" slab = "0.1" sha3 = { path = "sha3" } serde = "0.7.0" -clippy = { version = "0.0.64", optional = true} +clippy = { version = "0.0.67", optional = true} json-tests = { path = "json-tests" } igd = "0.4.2" ethcore-devtools = { path = "../devtools" } diff --git a/util/bigint/src/uint.rs b/util/bigint/src/uint.rs index be9941589..f87b494a9 100644 --- a/util/bigint/src/uint.rs +++ b/util/bigint/src/uint.rs @@ -517,7 +517,7 @@ pub trait Uint: Sized + Default + FromStr + From + fmt::Debug + fmt::Displa /// Return single byte fn byte(&self, index: usize) -> u8; /// Get this Uint as slice of bytes - fn to_bytes(&self, bytes: &mut[u8]); + fn to_raw_bytes(&self, bytes: &mut[u8]); /// Create `Uint(10**n)` fn exp10(n: usize) -> Self; @@ -621,7 +621,7 @@ macro_rules! construct_uint { (arr[index / 8] >> (((index % 8)) * 8)) as u8 } - fn to_bytes(&self, bytes: &mut[u8]) { + fn to_raw_bytes(&self, bytes: &mut[u8]) { assert!($n_words * 8 == bytes.len()); let &$name(ref arr) = self; for i in 0..bytes.len() { @@ -780,7 +780,7 @@ macro_rules! construct_uint { where S: serde::Serializer { let mut hex = "0x".to_owned(); let mut bytes = [0u8; 8 * $n_words]; - self.to_bytes(&mut bytes); + self.to_raw_bytes(&mut bytes); let len = cmp::max((self.bits() + 7) / 8, 1); hex.push_str(bytes[bytes.len() - len..].to_hex().as_ref()); serializer.serialize_str(hex.as_ref()) @@ -1482,7 +1482,7 @@ mod tests { let hex = "8090a0b0c0d0e0f00910203040506077583a2cf8264910e1436bda32571012f0"; let uint = U256::from_str(hex).unwrap(); let mut bytes = [0u8; 32]; - uint.to_bytes(&mut bytes); + uint.to_raw_bytes(&mut bytes); let uint2 = U256::from(&bytes[..]); assert_eq!(uint, uint2); } diff --git a/util/src/hash.rs b/util/src/hash.rs index 69ed17c79..e1b82b14c 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -517,7 +517,7 @@ impl From for H256 { fn from(value: U256) -> H256 { unsafe { let mut ret: H256 = ::std::mem::uninitialized(); - value.to_bytes(&mut ret); + value.to_raw_bytes(&mut ret); ret } } @@ -527,7 +527,7 @@ impl<'_> From<&'_ U256> for H256 { fn from(value: &'_ U256) -> H256 { unsafe { let mut ret: H256 = ::std::mem::uninitialized(); - value.to_bytes(&mut ret); + value.to_raw_bytes(&mut ret); ret } } diff --git a/util/src/keys/geth_import.rs b/util/src/keys/geth_import.rs index 9886a61a8..56e73f790 100644 --- a/util/src/keys/geth_import.rs +++ b/util/src/keys/geth_import.rs @@ -20,6 +20,7 @@ use common::*; use keys::store::SecretStore; use keys::directory::KeyFileContent; use std::path::PathBuf; +use path; /// Enumerates all geth keys in the directory and returns collection of tuples `(accountId, filename)` pub fn enumerate_geth_keys(path: &Path) -> Result, ImportError> { @@ -98,30 +99,7 @@ pub fn import_geth_keys(secret_store: &mut SecretStore, geth_keyfiles_directory: /// /// Based on https://github.com/ethereum/go-ethereum/blob/e553215/common/path.go#L75 pub fn keystore_dir() -> PathBuf { - #[cfg(target_os = "macos")] - fn data_dir(mut home: PathBuf) -> PathBuf { - home.push("Library"); - home.push("Ethereum"); - home - } - - #[cfg(windows)] - fn data_dir(mut home: PathBuf) -> PathBuf { - home.push("AppData"); - home.push("Roaming"); - home.push("Ethereum"); - home - } - - #[cfg(not(any(target_os = "macos", windows)))] - fn data_dir(mut home: PathBuf) -> PathBuf { - home.push(".ethereum"); - home - } - - let mut data_dir = data_dir(::std::env::home_dir().expect("Failed to get home dir")); - data_dir.push("keystore"); - data_dir + path::ethereum::with_default("keystore") } #[cfg(test)] diff --git a/util/src/lib.rs b/util/src/lib.rs index 530f2b4c5..a8f74b5a4 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -29,6 +29,10 @@ #![cfg_attr(feature="dev", allow(clone_on_copy))] // In most cases it expresses function flow better #![cfg_attr(feature="dev", allow(if_not_else))] +// TODO [todr] a lot of warnings to be fixed +#![cfg_attr(feature="dev", allow(needless_borrow))] +#![cfg_attr(feature="dev", allow(assign_op_pattern))] + //! Ethcore-util library //! @@ -148,6 +152,7 @@ pub mod panics; pub mod keys; pub mod table; pub mod network_settings; +pub mod path; pub use common::*; pub use misc::*; diff --git a/util/src/path.rs b/util/src/path.rs new file mode 100644 index 000000000..3a8dcaaae --- /dev/null +++ b/util/src/path.rs @@ -0,0 +1,56 @@ +// 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 . + +//! Path utilities + +/// Default ethereum paths +pub mod ethereum { + use std::path::PathBuf; + + #[cfg(target_os = "macos")] + /// Default path for ethereum installation on Mac Os + pub fn default() -> PathBuf { + let mut home = ::std::env::home_dir().expect("Failed to get home dir"); + home.push("Library"); + home.push("Ethereum"); + home + } + + #[cfg(windows)] + /// Default path for ethereum installation on Windows + pub fn default() -> PathBuf { + let mut home = ::std::env::home_dir().expect("Failed to get home dir"); + home.push("AppData"); + home.push("Roaming"); + home.push("Ethereum"); + home + } + + #[cfg(not(any(target_os = "macos", windows)))] + /// Default path for ethereum installation on posix system which is not Mac OS + pub fn default() -> PathBuf { + let mut home = ::std::env::home_dir().expect("Failed to get home dir"); + home.push(".ethereum"); + home + } + + /// Get the specific folder inside default ethereum installation + pub fn with_default(s: &str) -> PathBuf { + let mut pth = default(); + pth.push(s); + pth + } +} diff --git a/webapp/Cargo.toml b/webapp/Cargo.toml index 51f54f02d..fe6fb9bf6 100644 --- a/webapp/Cargo.toml +++ b/webapp/Cargo.toml @@ -17,9 +17,10 @@ ethcore-rpc = { path = "../rpc" } ethcore-util = { path = "../util" } parity-webapp = { git = "https://github.com/ethcore/parity-webapp.git" } # List of apps -parity-status = { git = "https://github.com/ethcore/parity-status.git", version = "0.3.7" } -parity-wallet = { git = "https://github.com/ethcore/parity-wallet.git", version = "0.2.0", optional = true } -clippy = { version = "0.0.64", optional = true} +parity-status = { git = "https://github.com/ethcore/parity-status.git", version = "0.4.1" } +parity-idmanager = { git = "https://github.com/ethcore/parity-idmanager-rs.git", version = "0.1.3" } +parity-wallet = { git = "https://github.com/ethcore/parity-wallet.git", version = "0.3.0", optional = true } +clippy = { version = "0.0.67", optional = true} [features] default = ["parity-wallet"] diff --git a/webapp/src/api.rs b/webapp/src/api.rs index bad49ec61..75cdb4c58 100644 --- a/webapp/src/api.rs +++ b/webapp/src/api.rs @@ -16,12 +16,8 @@ //! Simple REST API -use std::io::Write; use std::sync::Arc; -use hyper::status::StatusCode; -use hyper::{header, server, Decoder, Encoder, Next}; -use hyper::net::HttpStream; -use endpoint::{Endpoint, Endpoints}; +use endpoint::{Endpoint, Endpoints, ContentHandler, Handler, EndpointPath}; pub struct RestApi { endpoints: Arc, @@ -46,49 +42,8 @@ impl RestApi { } impl Endpoint for RestApi { - fn to_handler(&self, _prefix: &str) -> Box> { - Box::new(RestApiHandler { - pages: self.list_pages(), - write_pos: 0, - }) + fn to_handler(&self, _path: EndpointPath) -> Box { + Box::new(ContentHandler::new(self.list_pages(), "application/json".to_owned())) } } -struct RestApiHandler { - pages: String, - write_pos: usize, -} - -impl server::Handler for RestApiHandler { - fn on_request(&mut self, _request: server::Request) -> Next { - Next::write() - } - - fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next { - Next::write() - } - - fn on_response(&mut self, res: &mut server::Response) -> Next { - res.set_status(StatusCode::Ok); - res.headers_mut().set(header::ContentType("application/json".parse().unwrap())); - Next::write() - } - - fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { - let bytes = self.pages.as_bytes(); - if self.write_pos == bytes.len() { - return Next::end(); - } - - match encoder.write(&bytes[self.write_pos..]) { - Ok(bytes) => { - self.write_pos += bytes; - Next::write() - }, - Err(e) => match e.kind() { - ::std::io::ErrorKind::WouldBlock => Next::write(), - _ => Next::end() - }, - } - } -} diff --git a/webapp/src/apps.rs b/webapp/src/apps.rs index b0a4472f7..1c9b7e5a8 100644 --- a/webapp/src/apps.rs +++ b/webapp/src/apps.rs @@ -14,29 +14,48 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use endpoint::Endpoints; +use endpoint::{Endpoints, Endpoint}; use page::PageEndpoint; +use proxypac::ProxyPac; +use parity_webapp::WebApp; extern crate parity_status; +extern crate parity_idmanager; #[cfg(feature = "parity-wallet")] extern crate parity_wallet; +pub const DAPPS_DOMAIN : &'static str = ".parity"; +pub const RPC_PATH : &'static str = "rpc"; +pub const API_PATH : &'static str = "api"; +pub const UTILS_PATH : &'static str = "parity-utils"; pub fn main_page() -> &'static str { "/status/" } +pub fn utils() -> Box { + Box::new(PageEndpoint::with_prefix(parity_idmanager::App::default(), UTILS_PATH.to_owned())) +} + pub fn all_endpoints() -> Endpoints { let mut pages = Endpoints::new(); - pages.insert("status".to_owned(), Box::new(PageEndpoint::new(parity_status::App::default()))); + pages.insert("proxy".to_owned(), ProxyPac::boxed()); + + insert::(&mut pages, "status"); + insert::(&mut pages, "parity"); + wallet_page(&mut pages); pages } #[cfg(feature = "parity-wallet")] fn wallet_page(pages: &mut Endpoints) { - pages.insert("wallet".to_owned(), Box::new(PageEndpoint::new(parity_wallet::App::default()))); + insert::(pages, "wallet"); } #[cfg(not(feature = "parity-wallet"))] fn wallet_page(_pages: &mut Endpoints) {} + +fn insert(pages: &mut Endpoints, id: &str) { + pages.insert(id.to_owned(), Box::new(PageEndpoint::new(T::default()))); +} diff --git a/webapp/src/endpoint.rs b/webapp/src/endpoint.rs index dee2cadd8..d367734c4 100644 --- a/webapp/src/endpoint.rs +++ b/webapp/src/endpoint.rs @@ -16,12 +16,73 @@ //! URL Endpoint traits -use hyper::server; +use hyper::status::StatusCode; +use hyper::{header, server, Decoder, Encoder, Next}; use hyper::net::HttpStream; + +use std::io::Write; use std::collections::HashMap; +#[derive(Debug, PartialEq, Default, Clone)] +pub struct EndpointPath { + pub app_id: String, + pub host: String, + pub port: u16, +} + pub trait Endpoint : Send + Sync { - fn to_handler(&self, prefix: &str) -> Box>; + fn to_handler(&self, path: EndpointPath) -> Box>; } pub type Endpoints = HashMap>; +pub type Handler = server::Handler; + +pub struct ContentHandler { + content: String, + mimetype: String, + write_pos: usize, +} + +impl ContentHandler { + pub fn new(content: String, mimetype: String) -> Self { + ContentHandler { + content: content, + mimetype: mimetype, + write_pos: 0 + } + } +} + +impl server::Handler for ContentHandler { + fn on_request(&mut self, _request: server::Request) -> Next { + Next::write() + } + + fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next { + Next::write() + } + + fn on_response(&mut self, res: &mut server::Response) -> Next { + res.set_status(StatusCode::Ok); + res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap())); + Next::write() + } + + fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { + let bytes = self.content.as_bytes(); + if self.write_pos == bytes.len() { + return Next::end(); + } + + match encoder.write(&bytes[self.write_pos..]) { + Ok(bytes) => { + self.write_pos += bytes; + Next::write() + }, + Err(e) => match e.kind() { + ::std::io::ErrorKind::WouldBlock => Next::write(), + _ => Next::end() + }, + } + } +} diff --git a/webapp/src/lib.rs b/webapp/src/lib.rs index 30431578f..819e9d362 100644 --- a/webapp/src/lib.rs +++ b/webapp/src/lib.rs @@ -57,12 +57,16 @@ mod page; mod router; mod rpc; mod api; +mod proxypac; use std::sync::{Arc, Mutex}; use std::net::SocketAddr; +use std::collections::HashMap; use jsonrpc_core::{IoHandler, IoDelegate}; use router::auth::{Authorization, NoAuth, HttpBasicAuth}; +static DAPPS_DOMAIN : &'static str = ".parity"; + /// Webapps HTTP+RPC server build. pub struct ServerBuilder { handler: Arc, @@ -103,17 +107,21 @@ pub struct Server { impl Server { fn start_http(addr: &SocketAddr, authorization: A, handler: Arc) -> Result { let panic_handler = Arc::new(Mutex::new(None)); - let endpoints = Arc::new(apps::all_endpoints()); let authorization = Arc::new(authorization); - let rpc_endpoint = Arc::new(rpc::rpc(handler, panic_handler.clone())); - let api = Arc::new(api::RestApi::new(endpoints.clone())); + let endpoints = Arc::new(apps::all_endpoints()); + 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::Utils, apps::utils()); + special + }); try!(hyper::Server::http(addr)) .handle(move |_| router::Router::new( apps::main_page(), endpoints.clone(), - rpc_endpoint.clone(), - api.clone(), + special.clone(), authorization.clone(), )) .map(|l| Server { diff --git a/webapp/src/page/mod.rs b/webapp/src/page/mod.rs index cd527578b..1d987c393 100644 --- a/webapp/src/page/mod.rs +++ b/webapp/src/page/mod.rs @@ -22,28 +22,38 @@ use hyper::header; use hyper::status::StatusCode; use hyper::net::HttpStream; use hyper::{Decoder, Encoder, Next}; -use endpoint::Endpoint; +use endpoint::{Endpoint, EndpointPath}; use parity_webapp::WebApp; pub struct PageEndpoint { + /// Content of the files pub app: Arc, + /// Prefix to strip from the path (when `None` deducted from `app_id`) + pub prefix: Option, } impl PageEndpoint { pub fn new(app: T) -> Self { PageEndpoint { - app: Arc::new(app) + app: Arc::new(app), + prefix: None, + } + } + pub fn with_prefix(app: T, prefix: String) -> Self { + PageEndpoint { + app: Arc::new(app), + prefix: Some(prefix), } } } impl Endpoint for PageEndpoint { - fn to_handler(&self, prefix: &str) -> Box> { + fn to_handler(&self, path: EndpointPath) -> Box> { Box::new(PageHandler { app: self.app.clone(), - prefix: prefix.to_owned(), - prefix_with_slash: prefix.to_owned() + "/", - path: None, + prefix: self.prefix.clone(), + path: path, + file: None, write_pos: 0, }) } @@ -51,21 +61,43 @@ impl Endpoint for PageEndpoint { struct PageHandler { app: Arc, - prefix: String, - prefix_with_slash: String, - path: Option, + prefix: Option, + path: EndpointPath, + file: Option, write_pos: usize, } +impl PageHandler { + fn extract_path(&self, path: &str) -> String { + let app_id = &self.path.app_id; + let prefix = "/".to_owned() + self.prefix.as_ref().unwrap_or(app_id); + let prefix_with_slash = prefix.clone() + "/"; + + // Index file support + match path == "/" || path == &prefix || path == &prefix_with_slash { + true => "index.html".to_owned(), + false => if path.starts_with(&prefix_with_slash) { + path[prefix_with_slash.len()..].to_owned() + } else if path.starts_with("/") { + path[1..].to_owned() + } else { + path.to_owned() + } + } + } +} + impl server::Handler for PageHandler { fn on_request(&mut self, req: server::Request) -> Next { - if let RequestUri::AbsolutePath(ref path) = *req.uri() { - // Index file support - self.path = match path == &self.prefix || path == &self.prefix_with_slash { - true => Some("index.html".to_owned()), - false => Some(path[self.prefix_with_slash.len()..].to_owned()), - }; - } + self.file = match *req.uri() { + RequestUri::AbsolutePath(ref path) => { + Some(self.extract_path(path)) + }, + RequestUri::AbsoluteUri(ref url) => { + Some(self.extract_path(url.path())) + }, + _ => None, + }; Next::write() } @@ -74,7 +106,7 @@ impl server::Handler for PageHandler { } fn on_response(&mut self, res: &mut server::Response) -> Next { - if let Some(f) = self.path.as_ref().and_then(|f| self.app.file(f)) { + if let Some(f) = self.file.as_ref().and_then(|f| self.app.file(f)) { res.set_status(StatusCode::Ok); res.headers_mut().set(header::ContentType(f.content_type.parse().unwrap())); Next::write() @@ -86,7 +118,7 @@ impl server::Handler for PageHandler { fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { let (wrote, res) = { - let file = self.path.as_ref().and_then(|f| self.app.file(f)); + let file = self.file.as_ref().and_then(|f| self.app.file(f)); match file { None => (None, Next::end()), Some(f) if self.write_pos == f.content.len() => (None, Next::end()), diff --git a/webapp/src/proxypac.rs b/webapp/src/proxypac.rs new file mode 100644 index 000000000..9d91d58f0 --- /dev/null +++ b/webapp/src/proxypac.rs @@ -0,0 +1,48 @@ +// 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 . + +//! Serving ProxyPac file + +use endpoint::{Endpoint, Handler, ContentHandler, EndpointPath}; +use apps::DAPPS_DOMAIN; + +pub struct ProxyPac; + +impl ProxyPac { + pub fn boxed() -> Box { + Box::new(ProxyPac) + } +} + +impl Endpoint for ProxyPac { + fn to_handler(&self, path: EndpointPath) -> Box { + let content = format!( +r#" +function FindProxyForURL(url, host) {{ + if (shExpMatch(host, "*{0}")) + {{ + return "PROXY {1}:{2}"; + }} + + return "DIRECT"; +}} +"#, + DAPPS_DOMAIN, path.host, path.port); + Box::new(ContentHandler::new(content, "application/javascript".to_owned())) + } +} + + diff --git a/webapp/src/router/mod.rs b/webapp/src/router/mod.rs index 15fc496f9..e9d255f95 100644 --- a/webapp/src/router/mod.rs +++ b/webapp/src/router/mod.rs @@ -21,21 +21,33 @@ mod url; mod redirect; 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 hyper::net::HttpStream; -use endpoint::{Endpoint, Endpoints}; +use apps; +use endpoint::{Endpoint, Endpoints, EndpointPath}; use self::url::Url; use self::auth::{Authorization, Authorized}; use self::redirect::Redirection; +/// Special endpoints are accessible on every domain (every dapp) +#[derive(Debug, PartialEq, Hash, Eq)] +pub enum SpecialEndpoint { + Rpc, + Api, + Utils, + None, +} + pub struct Router { main_page: &'static str, endpoints: Arc, - rpc: Arc>, - api: Arc>, + special: Arc>>, authorization: Arc, handler: Box>, } @@ -43,32 +55,39 @@ pub struct Router { impl server::Handler for Router { fn on_request(&mut self, req: server::Request) -> Next { + // Check authorization let auth = self.authorization.is_authorized(&req); + + // Choose proper handler depending on path / domain self.handler = match auth { Authorized::No(handler) => handler, Authorized::Yes => { - let path = self.extract_request_path(&req); - match path { - Some(ref url) if self.endpoints.contains_key(url) => { - let prefix = "/".to_owned() + url; - self.endpoints.get(url).unwrap().to_handler(&prefix) + let url = extract_url(&req); + let endpoint = extract_endpoint(&url); + + match endpoint { + // First check special endpoints + (ref path, ref endpoint) if self.special.contains_key(endpoint) => { + self.special.get(endpoint).unwrap().to_handler(path.clone().unwrap_or_default()) }, - Some(ref url) if url == "api" => { - self.api.to_handler("/api") + // Then delegate to dapp + (Some(ref path), _) if self.endpoints.contains_key(&path.app_id) => { + self.endpoints.get(&path.app_id).unwrap().to_handler(path.clone()) }, + // Redirection to main page _ if *req.method() == hyper::method::Method::Get => { Redirection::new(self.main_page) }, + // RPC by default _ => { - self.rpc.to_handler(&"/") + self.special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default()) } } } }; - self.handler.on_request(req) - // Check authorization - // Choose proper handler depending on path + // Delegate on_request to proper handler + self.handler.on_request(req) } /// This event occurs each time the `Request` is ready to be read from. @@ -91,57 +110,146 @@ impl Router { pub fn new( main_page: &'static str, endpoints: Arc, - rpc: Arc>, - api: Arc>, + special: Arc>>, authorization: Arc) -> Self { - let handler = rpc.to_handler(&"/"); + let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default()); Router { main_page: main_page, endpoints: endpoints, - rpc: rpc, - api: api, + special: special, authorization: authorization, handler: handler, } } +} - fn extract_url(&self, 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, - }; +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_request_path(&self, req: &server::Request) -> Option { - let url = self.extract_url(&req); - match url { - Some(ref url) if url.path.len() > 1 => { - let part = url.path[0].clone(); - Some(part) - }, - _ => { - 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 { + return SpecialEndpoint::None; + } + + match url.path[0].as_ref() { + apps::RPC_PATH => SpecialEndpoint::Rpc, + apps::API_PATH => SpecialEndpoint::Api, + apps::UTILS_PATH => SpecialEndpoint::Utils, + _ => SpecialEndpoint::None, + } + } + + match *url { + Some(ref url) => match url.host { + Host::Domain(ref domain) if domain.ends_with(DAPPS_DOMAIN) => { + let len = domain.len() - DAPPS_DOMAIN.len(); + let id = domain[0..len].to_owned(); + + (Some(EndpointPath { + app_id: id, + host: domain.clone(), + port: url.port, + }), special_endpoint(url)) + }, + _ if url.path.len() > 1 => { + let id = url.path[0].clone(); + (Some(EndpointPath { + app_id: id.clone(), + host: format!("{}", url.host), + port: url.port, + }), special_endpoint(url)) + }, + _ => (None, special_endpoint(url)), + }, + _ => (None, SpecialEndpoint::None) + } +} + +#[test] +fn should_extract_endpoint() { + assert_eq!(extract_endpoint(&None), (None, SpecialEndpoint::None)); + + // With path prefix + assert_eq!( + extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok()), + (Some(EndpointPath { + app_id: "status".to_owned(), + host: "localhost".to_owned(), + port: 8080, + }), SpecialEndpoint::None) + ); + + // With path prefix + assert_eq!( + extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok()), + (Some(EndpointPath { + app_id: "rpc".to_owned(), + host: "localhost".to_owned(), + port: 8080, + }), SpecialEndpoint::Rpc) + ); + + assert_eq!( + extract_endpoint(&Url::parse("http://my.status.dapp/parity-utils/inject.js").ok()), + (Some(EndpointPath { + app_id: "my.status".to_owned(), + host: "my.status.dapp".to_owned(), + port: 80, + }), SpecialEndpoint::Utils) + ); + + // By Subdomain + assert_eq!( + extract_endpoint(&Url::parse("http://my.status.dapp/test.html").ok()), + (Some(EndpointPath { + app_id: "my.status".to_owned(), + host: "my.status.dapp".to_owned(), + port: 80, + }), SpecialEndpoint::None) + ); + + // RPC by subdomain + assert_eq!( + extract_endpoint(&Url::parse("http://my.status.dapp/rpc/").ok()), + (Some(EndpointPath { + app_id: "my.status".to_owned(), + host: "my.status.dapp".to_owned(), + port: 80, + }), SpecialEndpoint::Rpc) + ); + + // API by subdomain + assert_eq!( + extract_endpoint(&Url::parse("http://my.status.dapp/api/").ok()), + (Some(EndpointPath { + app_id: "my.status".to_owned(), + host: "my.status.dapp".to_owned(), + port: 80, + }), SpecialEndpoint::Api) + ); +} diff --git a/webapp/src/rpc.rs b/webapp/src/rpc.rs index 51e171d8d..7ba0b9584 100644 --- a/webapp/src/rpc.rs +++ b/webapp/src/rpc.rs @@ -15,11 +15,9 @@ // along with Parity. If not, see . use std::sync::{Arc, Mutex}; -use hyper::server; -use hyper::net::HttpStream; use jsonrpc_core::IoHandler; use jsonrpc_http_server::{ServerHandler, PanicHandler, AccessControlAllowOrigin}; -use endpoint::Endpoint; +use endpoint::{Endpoint, EndpointPath, Handler}; pub fn rpc(handler: Arc, panic_handler: Arc () + Send>>>>) -> Box { Box::new(RpcEndpoint { @@ -36,7 +34,7 @@ struct RpcEndpoint { } impl Endpoint for RpcEndpoint { - fn to_handler(&self, _prefix: &str) -> Box> { + fn to_handler(&self, _path: EndpointPath) -> Box { let panic_handler = PanicHandler { handler: self.panic_handler.clone() }; Box::new(ServerHandler::new(self.handler.clone(), self.cors_domain.clone(), panic_handler)) }