diff --git a/Cargo.lock b/Cargo.lock index 4dbcd1825..6298ef7ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,6 +105,11 @@ name = "bloomchain" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bytes" version = "0.3.0" @@ -347,9 +352,16 @@ version = "1.2.0" dependencies = [ "clippy 0.0.69 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-rpc 1.2.0", "ethcore-util 1.2.0", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_codegen 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ws 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1161,6 +1173,14 @@ dependencies = [ "serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sha1" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sha3" version = "0.1.0" @@ -1405,6 +1425,19 @@ name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ws" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "sha1 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index c6e75072d..27c215108 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -34,9 +34,9 @@ //! let io = IoHandler::new(); //! io.add_method("say_hello", SayHello); //! let _server = Server::start_unsecure_http( -//! &"127.0.0.1:3030".parse().unwrap(), -//! Arc::new(io) -//! ); +//! &"127.0.0.1:3030".parse().unwrap(), +//! Arc::new(io) +//! ); //! } //! ``` //! diff --git a/parity/cli.rs b/parity/cli.rs index 4bb74f188..60b622bf7 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -97,6 +97,11 @@ API and Console Options: --dapps-pass PASSWORD Specify password for Dapps server. Use only in conjunction with --dapps-user. + --signer Enable Trusted Signer WebSocket endpoint used by + System UIs. + --signer-port PORT Specify the port of Trusted Signer server + [default: 8180]. + Sealing/Mining Options: --force-sealing Force the node to author new blocks as if it were always sealing/mining. @@ -234,6 +239,8 @@ pub struct Args { pub flag_dapps_interface: String, pub flag_dapps_user: Option, pub flag_dapps_pass: Option, + pub flag_signer: bool, + pub flag_signer_port: u16, pub flag_force_sealing: bool, pub flag_author: String, pub flag_usd_per_tx: String, diff --git a/parity/main.rs b/parity/main.rs index e87828f64..62474eded 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -50,6 +50,9 @@ extern crate ethcore_rpc; #[cfg(feature = "dapps")] extern crate ethcore_dapps; +#[cfg(feature = "ethcore-signer")] +extern crate ethcore_signer; + #[macro_use] mod die; mod price_info; @@ -63,6 +66,7 @@ mod io_handler; mod cli; mod configuration; mod migration; +mod signer; use std::io::{Write, Read, BufReader, BufRead}; use std::ops::Deref; @@ -89,6 +93,7 @@ use informant::Informant; use die::*; use cli::print_version; use rpc::RpcServer; +use signer::SignerServer; use dapps::WebappServer; use io_handler::ClientIoHandler; use configuration::Configuration; @@ -231,6 +236,14 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) settings: network_settings.clone(), }); + // Set up a signer + let signer_server = signer::start(signer::Configuration { + enabled: conf.args.flag_signer, + port: conf.args.flag_signer_port, + }, signer::Dependencies { + panic_handler: panic_handler.clone(), + }); + // Register IO handler let io_handler = Arc::new(ClientIoHandler { client: service.client(), @@ -241,7 +254,7 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) service.io().register_handler(io_handler).expect("Error registering IO handler"); // Handle exit - wait_for_exit(panic_handler, rpc_server, dapps_server); + wait_for_exit(panic_handler, rpc_server, dapps_server, signer_server); } fn flush_stdout() { @@ -453,7 +466,12 @@ fn execute_account_cli(conf: Configuration) { } } -fn wait_for_exit(panic_handler: Arc, _rpc_server: Option, _dapps_server: Option) { +fn wait_for_exit( + panic_handler: Arc, + _rpc_server: Option, + _dapps_server: Option, + _signer_server: Option + ) { let exit = Arc::new(Condvar::new()); // Handle possible exits diff --git a/parity/setup_log.rs b/parity/setup_log.rs index 0fbc76fb3..4ed153fc2 100644 --- a/parity/setup_log.rs +++ b/parity/setup_log.rs @@ -27,6 +27,8 @@ pub fn setup_log(init: &Option) -> Arc { let mut levels = String::new(); let mut builder = LogBuilder::new(); + // Disable ws info logging by default. + builder.filter(Some("ws"), LogLevelFilter::Warn); builder.filter(None, LogLevelFilter::Info); if env::var("RUST_LOG").is_ok() { diff --git a/parity/signer.rs b/parity/signer.rs new file mode 100644 index 000000000..c78ad36ad --- /dev/null +++ b/parity/signer.rs @@ -0,0 +1,67 @@ +// 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 . + +use std::sync::Arc; +use util::panics::{PanicHandler, ForwardPanic}; +use die::*; + +#[cfg(feature = "ethcore-signer")] +use ethcore_signer as signer; +#[cfg(feature = "ethcore-signer")] +pub use ethcore_signer::Server as SignerServer; +#[cfg(not(feature = "ethcore-signer"))] +pub struct SignerServer; + +pub struct Configuration { + pub enabled: bool, + pub port: u16, +} + +pub struct Dependencies { + pub panic_handler: Arc, +} + +#[cfg(feature = "ethcore-signer")] +pub fn start(conf: Configuration, deps: Dependencies) -> Option { + if !conf.enabled { + return None; + } + + let addr = format!("127.0.0.1:{}", conf.port).parse().unwrap_or_else(|_| die!("Invalid port specified: {}", conf.port)); + let start_result = signer::Server::start(addr); + + match start_result { + Err(signer::ServerError::IoError(err)) => die_with_io_error("Trusted Signer", err), + Err(e) => die!("Trusted Signer: {:?}", e), + Ok(server) => { + deps.panic_handler.forward_from(&server); + Some(server) + }, + } +} + +#[cfg(not(feature = "ethcore-signer"))] +pub fn start(conf: Configuration) -> Option { + if !conf.enabled { + return None; + } + + die!("Your Parity version has been compiled without Trusted Signer support.") +} + + + + diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index f3bb450f3..b1ab256c0 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -18,12 +18,12 @@ //! //! Compliant with ethereum rpc. -pub mod traits; mod impls; -mod types; mod helpers; +pub mod traits; pub mod tests; +pub mod types; pub use self::traits::{Web3, Eth, EthFilter, Personal, Net, Ethcore, Traces, Rpc}; pub use self::impls::*; diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index c38949c08..5810c85e5 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -18,9 +18,12 @@ use serde::{Serialize, Serializer}; use util::numbers::*; use v1::types::{Bytes, Transaction, OptionalValue}; +/// Block Transactions #[derive(Debug)] pub enum BlockTransactions { + /// Only hashes Hashes(Vec), + /// Full transactions Full(Vec) } @@ -34,38 +37,58 @@ impl Serialize for BlockTransactions { } } +/// Block representation #[derive(Debug, Serialize)] pub struct Block { + /// Hash of the block pub hash: OptionalValue, + /// Hash of the parent #[serde(rename="parentHash")] pub parent_hash: H256, + /// Hash of the uncles #[serde(rename="sha3Uncles")] pub uncles_hash: H256, + /// Authors address pub author: Address, // TODO: get rid of this one + /// ? pub miner: Address, + /// State root hash #[serde(rename="stateRoot")] pub state_root: H256, + /// Transactions root hash #[serde(rename="transactionsRoot")] pub transactions_root: H256, + /// Transactions receipts root hash #[serde(rename="receiptsRoot")] pub receipts_root: H256, + /// Block number pub number: OptionalValue, + /// Gas Used #[serde(rename="gasUsed")] pub gas_used: U256, + /// Gas Limit #[serde(rename="gasLimit")] pub gas_limit: U256, + /// Extra data #[serde(rename="extraData")] pub extra_data: Bytes, + /// Logs bloom #[serde(rename="logsBloom")] pub logs_bloom: H2048, + /// Timestamp pub timestamp: U256, + /// Difficulty pub difficulty: U256, + /// Total difficulty #[serde(rename="totalDifficulty")] pub total_difficulty: U256, + /// Seal fields #[serde(rename="sealFields")] pub seal_fields: Vec, + /// Uncles' hashes pub uncles: Vec, + /// Transactions pub transactions: BlockTransactions } diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs index 071486afd..e2d150c66 100644 --- a/rpc/src/v1/types/block_number.rs +++ b/rpc/src/v1/types/block_number.rs @@ -21,9 +21,13 @@ use ethcore::client::BlockID; /// Represents rpc api block number param. #[derive(Debug, PartialEq, Clone)] pub enum BlockNumber { + /// Number Num(u64), + /// Latest block Latest, + /// Earliest block (genesis) Earliest, + /// Pending block (being mined) Pending } diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 0dc2d3b31..76d84d0dd 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Serializable wrapper around vector of bytes + use rustc_serialize::hex::ToHex; use serde::{Serialize, Serializer, Deserialize, Deserializer, Error}; use serde::de::Visitor; @@ -28,7 +30,10 @@ impl Bytes { pub fn new(bytes: Vec) -> Bytes { Bytes(bytes) } - pub fn to_vec(self) -> Vec { self.0 } + /// Convert back to vector + pub fn to_vec(self) -> Vec { + self.0 + } } impl Serialize for Bytes { @@ -80,4 +85,3 @@ mod tests { } } - diff --git a/rpc/src/v1/types/call_request.rs b/rpc/src/v1/types/call_request.rs index 045b1cd9b..50ebbd1f0 100644 --- a/rpc/src/v1/types/call_request.rs +++ b/rpc/src/v1/types/call_request.rs @@ -18,15 +18,23 @@ use util::hash::Address; use util::numbers::U256; use v1::types::Bytes; +/// Call request #[derive(Debug, Default, PartialEq, Deserialize)] pub struct CallRequest { + /// From pub from: Option
, + /// To pub to: Option
, + /// Gas Price #[serde(rename="gasPrice")] pub gas_price: Option, + /// Gas pub gas: Option, + /// Value pub value: Option, + /// Data pub data: Option, + /// Nonce pub nonce: Option, } diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index e50ec9c32..77a3f0500 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -22,10 +22,14 @@ use v1::types::BlockNumber; use ethcore::filter::Filter as EthFilter; use ethcore::client::BlockID; +/// Variadic value #[derive(Debug, PartialEq, Clone)] pub enum VariadicValue where T: Deserialize { + /// Single Single(T), + /// List Multiple(Vec), + /// None Null, } @@ -44,17 +48,24 @@ impl Deserialize for VariadicValue where T: Deserialize { } } +/// Filter Address pub type FilterAddress = VariadicValue
; +/// Topic pub type Topic = VariadicValue; +/// Filter #[derive(Debug, PartialEq, Clone, Deserialize)] #[serde(deny_unknown_fields)] pub struct Filter { + /// From Block #[serde(rename="fromBlock")] pub from_block: Option, + /// To Block #[serde(rename="toBlock")] pub to_block: Option, + /// Address pub address: Option, + /// Topics pub topics: Option>, } diff --git a/rpc/src/v1/types/index.rs b/rpc/src/v1/types/index.rs index e7cbbd255..d7b17aea2 100644 --- a/rpc/src/v1/types/index.rs +++ b/rpc/src/v1/types/index.rs @@ -22,6 +22,7 @@ use serde::de::Visitor; pub struct Index(usize); impl Index { + /// Convert to usize pub fn value(&self) -> usize { self.0 } diff --git a/rpc/src/v1/types/log.rs b/rpc/src/v1/types/log.rs index 426ca68f1..72a482d1b 100644 --- a/rpc/src/v1/types/log.rs +++ b/rpc/src/v1/types/log.rs @@ -18,21 +18,31 @@ use util::numbers::*; use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; use v1::types::Bytes; +/// Log #[derive(Debug, Serialize, PartialEq, Eq, Hash, Clone)] pub struct Log { + /// Address pub address: Address, + /// Topics pub topics: Vec, + /// Data pub data: Bytes, + /// Block Hash #[serde(rename="blockHash")] pub block_hash: Option, + /// Block Number #[serde(rename="blockNumber")] pub block_number: Option, + /// Transaction Hash #[serde(rename="transactionHash")] pub transaction_hash: Option, + /// Transaction Index #[serde(rename="transactionIndex")] pub transaction_index: Option, + /// Log Index #[serde(rename="logIndex")] pub log_index: Option, + /// Log Type #[serde(rename="type")] pub log_type: String, } diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index adf9be071..c1bc34407 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Structures used in RPC communication + #[cfg(feature = "serde_macros")] include!("mod.rs.in"); diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index d896a64dc..824a061ef 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +mod bytes; mod block; mod block_number; -mod bytes; mod filter; mod index; mod log; @@ -29,9 +29,9 @@ mod receipt; mod trace; mod trace_filter; +pub use self::bytes::Bytes; pub use self::block::{Block, BlockTransactions}; pub use self::block_number::BlockNumber; -pub use self::bytes::Bytes; pub use self::filter::Filter; pub use self::index::Index; pub use self::log::Log; diff --git a/rpc/src/v1/types/optionals.rs b/rpc/src/v1/types/optionals.rs index 5db272251..2ed272ade 100644 --- a/rpc/src/v1/types/optionals.rs +++ b/rpc/src/v1/types/optionals.rs @@ -17,9 +17,12 @@ use serde::{Serialize, Serializer}; use serde_json::Value; +/// Optional value #[derive(Debug)] pub enum OptionalValue where T: Serialize { + /// Some Value(T), + /// None Null } diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index 51d914e1a..32a7f5945 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -19,22 +19,31 @@ use util::hash::{Address, H256}; use v1::types::Log; use ethcore::receipt::LocalizedReceipt; +/// Receipt #[derive(Debug, Serialize)] pub struct Receipt { + /// Transaction Hash #[serde(rename="transactionHash")] pub transaction_hash: H256, + /// Transaction index #[serde(rename="transactionIndex")] pub transaction_index: U256, + /// Block hash #[serde(rename="blockHash")] pub block_hash: H256, + /// Block number #[serde(rename="blockNumber")] pub block_number: U256, + /// Cumulative gas used #[serde(rename="cumulativeGasUsed")] pub cumulative_gas_used: U256, + /// Gas used #[serde(rename="gasUsed")] pub gas_used: U256, + /// Contract address #[serde(rename="contractAddress")] pub contract_address: Option
, + /// Logs pub logs: Vec, } diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index c0e480140..6d750425e 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -17,19 +17,26 @@ use serde::{Serialize, Serializer}; use util::numbers::*; +/// Sync info #[derive(Default, Debug, Serialize, PartialEq)] pub struct SyncInfo { + /// Starting block #[serde(rename="startingBlock")] pub starting_block: U256, + /// Current block #[serde(rename="currentBlock")] pub current_block: U256, + /// Highest block seen so far #[serde(rename="highestBlock")] pub highest_block: U256, } +/// Sync status #[derive(Debug, PartialEq)] pub enum SyncStatus { + /// Info when syncing Info(SyncInfo), + /// Not syncing None } diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index 4cd1ac408..6ea58543a 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -19,11 +19,16 @@ use ethcore::trace::trace; use ethcore::trace::LocalizedTrace; use v1::types::Bytes; +/// Create response #[derive(Debug, Serialize)] pub struct Create { + /// Sender from: Address, + /// Value value: U256, + /// Gas gas: U256, + /// Initialization code init: Bytes, } @@ -38,12 +43,18 @@ impl From for Create { } } +/// Call response #[derive(Debug, Serialize)] pub struct Call { + /// Sender from: Address, + /// Recipient to: Address, + /// Transfered Value value: U256, + /// Gas gas: U256, + /// Input data input: Bytes, } @@ -59,10 +70,13 @@ impl From for Call { } } +/// Action #[derive(Debug, Serialize)] pub enum Action { + /// Call #[serde(rename="call")] Call(Call), + /// Create #[serde(rename="create")] Create(Create), } @@ -76,10 +90,13 @@ impl From for Action { } } +/// Call Result #[derive(Debug, Serialize)] pub struct CallResult { + /// Gas used #[serde(rename="gasUsed")] gas_used: U256, + /// Output bytes output: Bytes, } @@ -92,11 +109,15 @@ impl From for CallResult { } } +/// Craete Result #[derive(Debug, Serialize)] pub struct CreateResult { + /// Gas used #[serde(rename="gasUsed")] gas_used: U256, + /// Code code: Bytes, + /// Assigned address address: Address, } @@ -110,14 +131,19 @@ impl From for CreateResult { } } +/// Response #[derive(Debug, Serialize)] pub enum Res { + /// Call #[serde(rename="call")] Call(CallResult), + /// Create #[serde(rename="create")] Create(CreateResult), + /// Call failure #[serde(rename="failedCall")] FailedCall, + /// Creation failure #[serde(rename="failedCreate")] FailedCreate, } @@ -133,19 +159,28 @@ impl From for Res { } } +/// Trace #[derive(Debug, Serialize)] pub struct Trace { + /// Action action: Action, + /// Result result: Res, + /// Trace address #[serde(rename="traceAddress")] trace_address: Vec, + /// Subtraces subtraces: U256, + /// Transaction position #[serde(rename="transactionPosition")] transaction_position: U256, + /// Transaction hash #[serde(rename="transactionHash")] transaction_hash: H256, + /// Block Number #[serde(rename="blockNumber")] block_number: U256, + /// Block Hash #[serde(rename="blockHash")] block_hash: H256, } diff --git a/rpc/src/v1/types/trace_filter.rs b/rpc/src/v1/types/trace_filter.rs index 3d45f4c79..ee2f231f0 100644 --- a/rpc/src/v1/types/trace_filter.rs +++ b/rpc/src/v1/types/trace_filter.rs @@ -21,14 +21,19 @@ use ethcore::client::BlockID; use ethcore::client; use super::BlockNumber; +/// Trace filter #[derive(Debug, PartialEq, Deserialize)] pub struct TraceFilter { + /// From block #[serde(rename="fromBlock")] pub from_block: Option, + /// To block #[serde(rename="toBlock")] pub to_block: Option, + /// From address #[serde(rename="fromAddress")] pub from_address: Option>, + /// To address #[serde(rename="toAddress")] pub to_address: Option>, } diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 8a46d5e15..1c9a41084 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -18,22 +18,34 @@ use util::numbers::*; use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use v1::types::{Bytes, OptionalValue}; +/// Transaction #[derive(Debug, Default, Serialize)] pub struct Transaction { + /// Hash pub hash: H256, + /// Nonce pub nonce: U256, + /// Block hash #[serde(rename="blockHash")] pub block_hash: OptionalValue, + /// Block number #[serde(rename="blockNumber")] pub block_number: OptionalValue, + /// Transaction Index #[serde(rename="transactionIndex")] pub transaction_index: OptionalValue, + /// Sender pub from: Address, + /// Recipient pub to: OptionalValue
, + /// Transfered value pub value: U256, + /// Gas Price #[serde(rename="gasPrice")] pub gas_price: U256, + /// Gas pub gas: U256, + /// Data pub input: Bytes } diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index f00fa9ef0..1b51e6b12 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -14,19 +14,29 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! `TransactionRequest` type + use util::hash::Address; use util::numbers::U256; -use v1::types::Bytes; +use v1::types::bytes::Bytes; -#[derive(Debug, Default, PartialEq, Deserialize)] +/// Transaction request coming from RPC +#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Deserialize)] pub struct TransactionRequest { + /// Sender pub from: Address, + /// Recipient pub to: Option
, + /// Gas Price #[serde(rename="gasPrice")] pub gas_price: Option, + /// Gas pub gas: Option, + /// Value of transaction in wei pub value: Option, + /// Additional data sent with transaction pub data: Option, + /// Transaction's nonce pub nonce: Option, } @@ -37,7 +47,7 @@ mod tests { use serde_json; use util::numbers::{U256}; use util::hash::Address; - use v1::types::Bytes; + use v1::types::bytes::Bytes; use super::*; #[test] @@ -126,3 +136,4 @@ mod tests { }); } } + diff --git a/signer/Cargo.toml b/signer/Cargo.toml index f72865f4f..59c7f90b2 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -9,13 +9,23 @@ build = "build.rs" [build-dependencies] rustc_version = "0.1" +serde_codegen = { version = "0.7.0", optional = true } +syntex = "^0.32.0" [dependencies] -ethcore-util = { path = "../util" } +serde = "0.7.0" +serde_json = "0.7.0" +rustc-serialize = "0.3" log = "0.3" env_logger = "0.3" +ws = "0.4.7" +ethcore-util = { path = "../util" } +ethcore-rpc = { path = "../rpc" } + +serde_macros = { version = "0.7.0", optional = true } clippy = { version = "0.0.69", optional = true} [features] -default = [] +default = ["serde_codegen"] +nightly = ["serde_macros"] dev = ["clippy"] diff --git a/signer/build.rs b/signer/build.rs index 41b9a1b3e..2bcfc7da5 100644 --- a/signer/build.rs +++ b/signer/build.rs @@ -19,7 +19,34 @@ extern crate rustc_version; use rustc_version::{version_meta, Channel}; fn main() { + serde::main(); if let Channel::Nightly = version_meta().channel { println!("cargo:rustc-cfg=nightly"); } } + +#[cfg(not(feature = "serde_macros"))] +mod serde { + extern crate syntex; + extern crate serde_codegen; + + use std::env; + use std::path::Path; + + pub fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + + let src = Path::new("src/types/mod.rs.in"); + let dst = Path::new(&out_dir).join("mod.rs"); + + let mut registry = syntex::Registry::new(); + + serde_codegen::register(&mut registry); + registry.expand("", &src, &dst).unwrap(); + } +} + +#[cfg(feature = "serde_macros")] +mod serde { + pub fn main() {} +} diff --git a/signer/src/lib.rs b/signer/src/lib.rs index fd17758d2..4317338e0 100644 --- a/signer/src/lib.rs +++ b/signer/src/lib.rs @@ -17,6 +17,8 @@ #![warn(missing_docs)] #![cfg_attr(all(nightly, feature="dev"), feature(plugin))] #![cfg_attr(all(nightly, feature="dev"), plugin(clippy))] +// Generated by serde +#![cfg_attr(all(nightly, feature="dev"), allow(redundant_closure_call))] //! Signer module //! @@ -28,12 +30,33 @@ //! and their responsibility is to confirm (or confirm and sign) //! the transaction for you. //! +//! ``` +//! extern crate ethcore_signer; //! +//! use ethcore_signer::Server; +//! +//! fn main() { +//! let _server = Server::start("127.0.0.1:8084".parse().unwrap()); +//! } +//! ``` #[macro_use] extern crate log; extern crate env_logger; +extern crate serde; +extern crate serde_json; +extern crate rustc_serialize; + +extern crate ethcore_util as util; +extern crate ethcore_rpc as rpc; +extern crate ws; + +mod signing_queue; +mod ws_server; + +pub use ws_server::*; + #[cfg(test)] mod tests { #[test] diff --git a/signer/src/signing_queue.rs b/signer/src/signing_queue.rs new file mode 100644 index 000000000..611d467c2 --- /dev/null +++ b/signer/src/signing_queue.rs @@ -0,0 +1,74 @@ +// 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 . + +use std::collections::HashSet; +use rpc::v1::types::TransactionRequest; + +pub trait SigningQueue { + fn add_request(&mut self, transaction: TransactionRequest); + + fn remove_request(&mut self, id: TransactionRequest); + + fn requests(&self) -> &HashSet; +} + +impl SigningQueue for HashSet { + fn add_request(&mut self, transaction: TransactionRequest) { + self.insert(transaction); + } + + fn remove_request(&mut self, id: TransactionRequest) { + self.remove(&id); + } + + fn requests(&self) -> &HashSet { + self + } +} + + +#[cfg(test)] +mod test { + use std::collections::HashSet; + use util::hash::Address; + use util::numbers::U256; + use rpc::v1::types::TransactionRequest; + use super::*; + + #[test] + fn should_work_for_hashset() { + // given + let mut queue = HashSet::new(); + + let request = TransactionRequest { + from: Address::from(1), + to: Some(Address::from(2)), + gas_price: None, + gas: None, + value: Some(U256::from(10_000_000)), + data: None, + nonce: None, + }; + + // when + queue.add_request(request.clone()); + let all = queue.requests(); + + // then + assert_eq!(all.len(), 1); + assert!(all.contains(&request)); + } +} diff --git a/signer/src/types/mod.rs b/signer/src/types/mod.rs new file mode 100644 index 000000000..d5e15046a --- /dev/null +++ b/signer/src/types/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Reusable types with JSON Serialization. + +#[cfg(feature = "serde_macros")] +include!("mod.rs.in"); + +#[cfg(not(feature = "serde_macros"))] +include!(concat!(env!("OUT_DIR"), "/mod.rs")); diff --git a/signer/src/types/mod.rs.in b/signer/src/types/mod.rs.in new file mode 100644 index 000000000..a59f81ece --- /dev/null +++ b/signer/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 . + + + + + + + + + +// TODO [ToDr] Types are empty for now. But they are about to come. diff --git a/signer/src/ws_server.rs b/signer/src/ws_server.rs new file mode 100644 index 000000000..d2ab02d66 --- /dev/null +++ b/signer/src/ws_server.rs @@ -0,0 +1,128 @@ +// 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 . + +//! `WebSockets` server. + +use ws; +use std; +use std::thread; +use std::ops::Drop; +use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::net::SocketAddr; +use util::panics::{PanicHandler, OnPanicListener, MayPanic}; + +/// Signer startup error +#[derive(Debug)] +pub enum ServerError { + /// Wrapped `std::io::Error` + IoError(std::io::Error), + /// Other `ws-rs` error + WebSocket(ws::Error) +} + +impl From for ServerError { + fn from(err: ws::Error) -> Self { + match err.kind { + ws::ErrorKind::Io(e) => ServerError::IoError(e), + _ => ServerError::WebSocket(err), + } + } +} + +/// `WebSockets` server implementation. +pub struct Server { + handle: Option>>, + broadcaster: ws::Sender, + panic_handler: Arc, +} + +impl Server { + /// Starts a new `WebSocket` server in separate thread. + /// Returns a `Server` handle which closes the server when droped. + pub fn start(addr: SocketAddr) -> Result { + let config = { + let mut config = ws::Settings::default(); + config.max_connections = 5; + config.method_strict = true; + config + }; + + // Create WebSocket + let session_id = Arc::new(AtomicUsize::new(1)); + let ws = try!(ws::Builder::new().with_settings(config).build(Factory { + session_id: session_id, + })); + + let panic_handler = PanicHandler::new_in_arc(); + let ph = panic_handler.clone(); + let broadcaster = ws.broadcaster(); + // Spawn a thread with event loop + let handle = thread::spawn(move || { + ph.catch_panic(move || { + ws.listen(addr).unwrap() + }).unwrap() + }); + + // Return a handle + Ok(Server { + handle: Some(handle), + broadcaster: broadcaster, + panic_handler: panic_handler, + }) + } +} + +impl MayPanic for Server { + fn on_panic(&self, closure: F) where F: OnPanicListener { + self.panic_handler.on_panic(closure); + } +} + +impl Drop for Server { + fn drop(&mut self) { + self.broadcaster.shutdown().expect("WsServer should close nicely."); + self.handle.take().unwrap().join().unwrap(); + } +} + +struct Session { + id: usize, + out: ws::Sender, +} + +impl ws::Handler for Session { + fn on_open(&mut self, _shake: ws::Handshake) -> ws::Result<()> { + try!(self.out.send(format!("Hello client no: {}. We are not implemented yet.", self.id))); + try!(self.out.close(ws::CloseCode::Normal)); + Ok(()) + } +} + +struct Factory { + session_id: Arc, +} + +impl ws::Factory for Factory { + type Handler = Session; + + fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { + Session { + id: self.session_id.fetch_add(1, Ordering::SeqCst), + out: sender, + } + } +}