diff --git a/Cargo.lock b/Cargo.lock index 62ec76bd4..c81020f66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,7 +299,7 @@ version = "1.2.0" dependencies = [ "ethcore-devtools 1.2.0", "ethcore-util 1.2.0", - "nanomsg 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)", + "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -321,7 +321,7 @@ dependencies = [ "ethcore-ipc 1.2.0", "jsonrpc-core 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "nanomsg 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)", + "nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)", ] [[package]] @@ -331,7 +331,9 @@ dependencies = [ "clippy 0.0.69 (registry+https://github.com/rust-lang/crates.io-index)", "ethash 1.2.0", "ethcore 1.2.0", + "ethcore-devtools 1.2.0", "ethcore-util 1.2.0", + "ethjson 0.1.0", "ethminer 1.2.0", "ethsync 1.2.0", "json-ipc-server 0.1.0 (git+https://github.com/ethcore/json-ipc-server.git)", @@ -383,7 +385,7 @@ dependencies = [ "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "rocksdb 0.4.3 (git+https://github.com/ethcore/rust-rocksdb)", + "rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)", "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)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -572,7 +574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "json-ipc-server" version = "0.1.0" -source = "git+https://github.com/ethcore/json-ipc-server.git#fdcba83d00b127c7419fe21406a5b81998f686eb" +source = "git+https://github.com/ethcore/json-ipc-server.git#4f9226c4f84dcce2385a188374e3b5fc66b63e68" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -653,14 +655,6 @@ name = "libc" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "librocksdb-sys" -version = "0.2.3" -source = "git+https://github.com/ethcore/rust-rocksdb#6b6ce93e2828182691e00da57fdfb2926226f1f1" -dependencies = [ - "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "log" version = "0.3.6" @@ -736,8 +730,8 @@ dependencies = [ [[package]] name = "nanomsg" -version = "0.5.0" -source = "git+https://github.com/ethcore/nanomsg.rs.git#9c81fb3b0f71714b173d0abf14bfd30addf8c7b1" +version = "0.5.1" +source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00" dependencies = [ "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)", @@ -746,7 +740,7 @@ dependencies = [ [[package]] name = "nanomsg-sys" version = "0.5.0" -source = "git+https://github.com/ethcore/nanomsg.rs.git#9c81fb3b0f71714b173d0abf14bfd30addf8c7b1" +source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00" dependencies = [ "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1067,11 +1061,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rocksdb" -version = "0.4.3" -source = "git+https://github.com/ethcore/rust-rocksdb#6b6ce93e2828182691e00da57fdfb2926226f1f1" +version = "0.4.5" +source = "git+https://github.com/ethcore/rust-rocksdb#9140e37ce0fdb748097f85653c01b0f7e3736ea9" dependencies = [ "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "librocksdb-sys 0.2.3 (git+https://github.com/ethcore/rust-rocksdb)", + "rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)", +] + +[[package]] +name = "rocksdb-sys" +version = "0.3.0" +source = "git+https://github.com/ethcore/rust-rocksdb#9140e37ce0fdb748097f85653c01b0f7e3736ea9" +dependencies = [ + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 842decd88..a6b4c1781 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -36,6 +36,7 @@ pub enum Action { /// Create creates new contract. Create, /// Calls contract at given address. + /// In the case of a transfer, this is the receiver's address.' Call(Address), } diff --git a/json/src/bytes.rs b/json/src/bytes.rs index 812b109f7..18dc73844 100644 --- a/json/src/bytes.rs +++ b/json/src/bytes.rs @@ -19,6 +19,7 @@ use rustc_serialize::hex::FromHex; use serde::{Deserialize, Deserializer, Error}; use serde::de::Visitor; +use std::ops::Deref; /// Lenient bytes json deserialization for test json files. #[derive(Default, Debug, PartialEq, Clone)] @@ -30,6 +31,14 @@ impl Into> for Bytes { } } +impl Deref for Bytes { + type Target = Vec; + + fn deref(&self) -> &Vec { + &self.0 + } +} + impl Deserialize for Bytes { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer { diff --git a/json/src/vm/call.rs b/json/src/vm/call.rs index 2fe6265aa..1947fd25c 100644 --- a/json/src/vm/call.rs +++ b/json/src/vm/call.rs @@ -39,16 +39,47 @@ pub struct Call { mod tests { use serde_json; use vm::Call; + use util::numbers::U256; + use uint::Uint; + use util::hash::Address as Hash160; + use hash::Address; + use maybe::MaybeEmpty; + use std::str::FromStr; #[test] - fn call_deserialization() { + fn call_deserialization_empty_dest() { let s = r#"{ "data" : "0x1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff", "destination" : "", "gasLimit" : "0x1748766aa5", "value" : "0x00" }"#; - let _deserialized: Call = serde_json::from_str(s).unwrap(); - // TODO: validate all fields + let call: Call = serde_json::from_str(s).unwrap(); + + assert_eq!(&call.data[..], + &[0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77, + 0x88, 0x88, 0x99, 0x99, 0x00, 0x00, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, + 0xee, 0xee, 0xff, 0xff]); + + assert_eq!(call.destination, MaybeEmpty::None); + assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); + assert_eq!(call.value, Uint(U256::from(0))); + } + + #[test] + fn call_deserialization_full_dest() { + let s = r#"{ + "data" : "0x1234", + "destination" : "5a39ed1020c04d4d84539975b893a4e7c53eab6c", + "gasLimit" : "0x1748766aa5", + "value" : "0x00" + }"#; + + let call: Call = serde_json::from_str(s).unwrap(); + + assert_eq!(&call.data[..], &[0x12, 0x34]); + assert_eq!(call.destination, MaybeEmpty::Some(Address(Hash160::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c").unwrap()))); + assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); + assert_eq!(call.value, Uint(U256::from(0))); } } diff --git a/miner/src/lib.rs b/miner/src/lib.rs index e9ac7aba2..a1780efff 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -107,12 +107,12 @@ pub trait MinerService : Send + Sync { /// Imports transactions to transaction queue. fn import_transactions(&self, transactions: Vec, fetch_account: T) -> Vec> - where T: Fn(&Address) -> AccountDetails; + where T: Fn(&Address) -> AccountDetails, Self: Sized; /// Imports own (node owner) transaction to queue. fn import_own_transaction(&self, chain: &BlockChainClient, transaction: SignedTransaction, fetch_account: T) -> Result - where T: Fn(&Address) -> AccountDetails; + where T: Fn(&Address) -> AccountDetails, Self: Sized; /// Returns hashes of transactions currently in pending fn pending_transactions_hashes(&self) -> Vec; @@ -131,7 +131,8 @@ pub trait MinerService : Send + Sync { fn submit_seal(&self, chain: &BlockChainClient, pow_hash: H256, seal: Vec) -> Result<(), Error>; /// Get the sealing work package and if `Some`, apply some transform. - fn map_sealing_work(&self, chain: &BlockChainClient, f: F) -> Option where F: FnOnce(&ClosedBlock) -> T; + fn map_sealing_work(&self, chain: &BlockChainClient, f: F) -> Option + where F: FnOnce(&ClosedBlock) -> T, Self: Sized; /// Query pending transactions for hash. fn transaction(&self, hash: &H256) -> Option; diff --git a/miner/src/miner.rs b/miner/src/miner.rs index 3860a79e6..fc63aec6c 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -395,6 +395,7 @@ impl MinerService for Miner { Err(ref e) => { trace!(target: "own_tx", "Failed to import transaction {:?} (hash: {:?})", e, hash); trace!(target: "own_tx", "Status: {:?}", transaction_queue.status()); + warn!(target: "own_tx", "Error importing transaction: {:?}", e); }, } import diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 2cdbb0a2b..61b51cf88 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -19,6 +19,8 @@ ethcore = { path = "../ethcore" } ethash = { path = "../ethash" } ethsync = { path = "../sync" } ethminer = { path = "../miner" } +ethjson = { path = "../json" } +ethcore-devtools = { path = "../devtools" } rustc-serialize = "0.3" transient-hashmap = "0.1" serde_macros = { version = "0.7.0", optional = true } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 699ee9ba1..607bcf4bd 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -33,6 +33,11 @@ extern crate ethminer; extern crate transient_hashmap; extern crate json_ipc_server as ipc; +#[cfg(test)] +extern crate ethjson; +#[cfg(test)] +extern crate ethcore_devtools as devtools; + use std::sync::Arc; use std::net::SocketAddr; use self::jsonrpc_core::{IoHandler, IoDelegate}; diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index f7c34ffa7..55bee30f5 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -26,6 +26,7 @@ use jsonrpc_core::*; use util::numbers::*; use util::sha3::*; use util::rlp::{encode, decode, UntrustedRlp, View}; +use util::keys::store::AccountProvider; use ethcore::client::{BlockChainClient, BlockID, TransactionID, UncleID}; use ethcore::block::IsBlock; use ethcore::views::*; @@ -37,7 +38,6 @@ use self::ethash::SeedHashCompute; use v1::traits::Eth; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; use v1::impls::dispatch_transaction; -use util::keys::store::AccountProvider; use serde; /// Eth rpc implementation. @@ -529,4 +529,16 @@ impl Eth for EthClient where to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))) }) } + + fn compile_lll(&self, _: Params) -> Result { + rpc_unimplemented!() + } + + fn compile_serpent(&self, _: Params) -> Result { + rpc_unimplemented!() + } + + fn compile_solidity(&self, _: Params) -> Result { + rpc_unimplemented!() + } } diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs index f4a972fb5..04011902b 100644 --- a/rpc/src/v1/impls/eth_signing.rs +++ b/rpc/src/v1/impls/eth_signing.rs @@ -43,6 +43,12 @@ impl EthSigningQueueClient { } impl EthSigning for EthSigningQueueClient { + + fn sign(&self, _params: Params) -> Result { + // TODO [ToDr] Implement sign when rest of the signing queue is ready. + rpc_unimplemented!() + } + fn send_transaction(&self, params: Params) -> Result { from_params::<(TransactionRequest, )>(params) .and_then(|(request, )| { @@ -85,6 +91,12 @@ impl EthSigning for EthSigningUnsafeClient where A: AccountProvider + 'static, M: MinerService + 'static { + fn sign(&self, params: Params) -> Result { + from_params::<(Address, H256)>(params).and_then(|(addr, msg)| { + to_value(&take_weak!(self.accounts).sign(&addr, &msg).unwrap_or(H520::zero())) + }) + } + fn send_transaction(&self, params: Params) -> Result { from_params::<(TransactionRequest, )>(params) .and_then(|(request, )| { diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index de458a53f..f5d6f1fda 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -22,7 +22,7 @@ use std::sync::{Arc, Weak}; use std::ops::Deref; use std::collections::BTreeMap; use jsonrpc_core::*; -use ethminer::{MinerService}; +use ethminer::MinerService; use v1::traits::Ethcore; use v1::types::Bytes; diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index d3a9b70a2..975a5fa77 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -25,6 +25,10 @@ macro_rules! take_weak { } } +macro_rules! rpc_unimplemented { + () => (Err(Error::internal_error())) +} + mod web3; mod eth; mod eth_filter; @@ -61,22 +65,14 @@ fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedT where C: BlockChainClient, M: MinerService { let hash = signed_transaction.hash(); - let import = { - miner.import_own_transaction(client, signed_transaction, |a: &Address| { - AccountDetails { - nonce: client.latest_nonce(&a), - balance: client.latest_balance(&a), - } - }) - }; - - match import { - Ok(_) => to_value(&hash), - Err(e) => { - warn!("Error sending transaction: {:?}", e); - to_value(&H256::zero()) + let import = miner.import_own_transaction(client, signed_transaction, |a: &Address| { + AccountDetails { + nonce: client.latest_nonce(&a), + balance: client.latest_balance(&a), } - } + }); + + to_value(&import.map(|_| hash).unwrap_or(H256::zero())) } fn sign_and_dispatch(client: &Weak, miner: &Weak, request: TransactionRequest, secret: H256) -> Result diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs new file mode 100644 index 000000000..80a856aca --- /dev/null +++ b/rpc/src/v1/tests/eth.rs @@ -0,0 +1,229 @@ +// Copyright 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 . + +//! rpc integration tests. +use std::collections::HashMap; +use std::sync::Arc; +use std::str::FromStr; + +use ethcore::client::{BlockChainClient, Client, ClientConfig}; +use ethcore::spec::Genesis; +use ethcore::block::Block; +use ethcore::ethereum; +use ethcore::transaction::{Transaction, Action}; +use ethminer::{MinerService, ExternalMiner}; +use devtools::RandomTempPath; +use util::io::IoChannel; +use util::hash::Address; +use util::numbers::{Uint, U256}; +use util::keys::{AccountProvider, TestAccount, TestAccountProvider}; +use jsonrpc_core::IoHandler; +use ethjson::blockchain::BlockChain; + +use v1::traits::eth::Eth; +use v1::impls::EthClient; +use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService}; + +struct EthTester { + _client: Arc, + _miner: Arc, + accounts: Arc, + handler: IoHandler, +} + +#[test] +fn harness_works() { + let chain: BlockChain = extract_chain!("BlockchainTests/bcUncleTest"); + chain_harness(chain, |_| {}); +} + +#[test] +fn eth_get_balance() { + let chain = extract_chain!("BlockchainTests/bcWalletTest", "wallet2outOf3txs"); + chain_harness(chain, |tester| { + // final account state + let req_latest = r#"{ + "jsonrpc": "2.0", + "method": "eth_getBalance", + "params": ["0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa", "latest"], + "id": 1 + }"#; + let res_latest = r#"{"jsonrpc":"2.0","result":"0x09","id":1}"#.to_owned(); + assert_eq!(tester.handler.handle_request(req_latest).unwrap(), res_latest); + + // non-existant account + let req_new_acc = r#"{ + "jsonrpc": "2.0", + "method": "eth_getBalance", + "params": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], + "id": 3 + }"#; + + let res_new_acc = r#"{"jsonrpc":"2.0","result":"0x00","id":3}"#.to_owned(); + assert_eq!(tester.handler.handle_request(req_new_acc).unwrap(), res_new_acc); + }); +} + +#[test] +fn eth_block_number() { + let chain = extract_chain!("BlockchainTests/bcRPC_API_Test"); + chain_harness(chain, |tester| { + let req_number = r#"{ + "jsonrpc": "2.0", + "method": "eth_blockNumber", + "params": [], + "id": 1 + }"#; + + let res_number = r#"{"jsonrpc":"2.0","result":"0x20","id":1}"#.to_owned(); + assert_eq!(tester.handler.handle_request(req_number).unwrap(), res_number); + }); +} + +#[cfg(test)] +#[test] +fn eth_transaction_count() { + let chain = extract_chain!("BlockchainTests/bcRPC_API_Test"); + chain_harness(chain, |tester| { + let address = tester.accounts.new_account("123").unwrap(); + let secret = tester.accounts.account_secret(&address).unwrap(); + + let req_before = r#"{ + "jsonrpc": "2.0", + "method": "eth_getTransactionCount", + "params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"], + "id": 15 + }"#; + + let res_before = r#"{"jsonrpc":"2.0","result":"0x00","id":15}"#; + + assert_eq!(tester.handler.handle_request(&req_before).unwrap(), res_before); + + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + value: U256::from(0x9184e72au64), + data: vec![] + }.sign(&secret); + + let req_send_trans = r#"{ + "jsonrpc": "2.0", + "method": "eth_sendTransaction", + "params": [{ + "from": ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a" + }], + "id": 16 + }"#; + + let res_send_trans = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":16}"#; + + // dispatch the transaction. + assert_eq!(tester.handler.handle_request(&req_send_trans).unwrap(), res_send_trans); + + // we have submitted the transaction -- but this shouldn't be reflected in a "latest" query. + let req_after_latest = r#"{ + "jsonrpc": "2.0", + "method": "eth_getTransactionCount", + "params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"], + "id": 17 + }"#; + + let res_after_latest = r#"{"jsonrpc":"2.0","result":"0x00","id":17}"#; + + assert_eq!(&tester.handler.handle_request(&req_after_latest).unwrap(), res_after_latest); + + // the pending transactions should have been updated. + let req_after_pending = r#"{ + "jsonrpc": "2.0", + "method": "eth_getTransactionCount", + "params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "pending"], + "id": 18 + }"#; + + let res_after_pending = r#"{"jsonrpc":"2.0","result":"0x01","id":18}"#; + + assert_eq!(&tester.handler.handle_request(&req_after_pending).unwrap(), res_after_pending); + }); +} + +fn account_provider() -> Arc { + let mut accounts = HashMap::new(); + accounts.insert(Address::from(1), TestAccount::new("test")); + let ap = TestAccountProvider::new(accounts); + Arc::new(ap) +} + +fn sync_provider() -> Arc { + Arc::new(TestSyncProvider::new(Config { + network_id: U256::from(3), + num_peers: 120, + })) +} + +fn miner_service() -> Arc { + Arc::new(TestMinerService::default()) +} + +// given a blockchain, this harness will create an EthClient wrapping it +// which tests can pass specially crafted requests to. +fn chain_harness(chain: BlockChain, mut cb: F) -> U + where F: FnMut(&EthTester) -> U { + let genesis = Genesis::from(chain.genesis()); + let mut spec = ethereum::new_frontier_test(); + let state = chain.pre_state.clone().into(); + spec.set_genesis_state(state); + spec.overwrite_genesis_params(genesis); + assert!(spec.is_state_root_valid()); + + let dir = RandomTempPath::new(); + let client = Client::new(ClientConfig::default(), spec, dir.as_path(), IoChannel::disconnected()).unwrap(); + let sync_provider = sync_provider(); + let miner_service = miner_service(); + let account_provider = account_provider(); + let external_miner = Arc::new(ExternalMiner::default()); + + for b in &chain.blocks_rlp() { + if Block::is_good(&b) { + let _ = client.import_block(b.clone()); + client.flush_queue(); + client.import_verified_blocks(&IoChannel::disconnected()); + } + } + + assert!(client.chain_info().best_block_hash == chain.best_block.into()); + + let eth_client = EthClient::new(&client, &sync_provider, &account_provider, + &miner_service, &external_miner); + + let handler = IoHandler::new(); + let delegate = eth_client.to_delegate(); + handler.add_delegate(delegate); + + let tester = EthTester { + _miner: miner_service, + _client: client, + accounts: account_provider, + handler: handler, + }; + + cb(&tester) +} diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 5d57d5d61..600f88508 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -115,12 +115,16 @@ impl MinerService for TestMinerService { } /// Imports transactions to transaction queue. - fn import_transactions(&self, transactions: Vec, _fetch_account: T) -> + fn import_transactions(&self, transactions: Vec, fetch_account: T) -> Vec> where T: Fn(&Address) -> AccountDetails { // lets assume that all txs are valid self.imported_transactions.lock().unwrap().extend_from_slice(&transactions); + for sender in transactions.iter().filter_map(|t| t.sender().ok()) { + let nonce = self.last_nonce(&sender).unwrap_or(fetch_account(&sender).nonce); + self.last_nonces.write().unwrap().insert(sender, nonce + U256::from(1)); + } transactions .iter() .map(|_| Ok(TransactionImportResult::Current)) @@ -128,9 +132,16 @@ impl MinerService for TestMinerService { } /// Imports transactions to transaction queue. - fn import_own_transaction(&self, _chain: &BlockChainClient, transaction: SignedTransaction, _fetch_account: T) -> + fn import_own_transaction(&self, chain: &BlockChainClient, transaction: SignedTransaction, _fetch_account: T) -> Result where T: Fn(&Address) -> AccountDetails { + + // keep the pending nonces up to date + if let Ok(ref sender) = transaction.sender() { + let nonce = self.last_nonce(sender).unwrap_or(chain.latest_nonce(sender)); + self.last_nonces.write().unwrap().insert(sender.clone(), nonce + U256::from(1)); + } + // lets assume that all txs are valid self.imported_transactions.lock().unwrap().push(transaction); @@ -200,7 +211,9 @@ impl MinerService for TestMinerService { } fn nonce(&self, _chain: &BlockChainClient, address: &Address) -> U256 { - self.latest_closed_block.lock().unwrap().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.nonce(address).clone()) + // we assume all transactions are in a pending block, ignoring the + // reality of gas limits. + self.last_nonce(address).unwrap_or(U256::zero()) } fn code(&self, _chain: &BlockChainClient, address: &Address) -> Option { diff --git a/rpc/src/v1/tests/helpers/mod.rs b/rpc/src/v1/tests/helpers/mod.rs index 9b321af98..1b8f9e256 100644 --- a/rpc/src/v1/tests/helpers/mod.rs +++ b/rpc/src/v1/tests/helpers/mod.rs @@ -20,4 +20,4 @@ mod sync_provider; mod miner_service; pub use self::sync_provider::{Config, TestSyncProvider}; -pub use self::miner_service::{TestMinerService}; +pub use self::miner_service::TestMinerService; diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index fc81586dd..114b5b08f 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -16,9 +16,9 @@ //! Test implementation of SyncProvider. -use util::{U256}; +use util::U256; use ethsync::{SyncProvider, SyncStatus, SyncState}; -use std::sync::{RwLock}; +use std::sync::RwLock; /// TestSyncProvider config. pub struct Config { diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index ce097307c..be59a3714 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -20,14 +20,16 @@ use std::sync::{Arc, RwLock}; use jsonrpc_core::IoHandler; use util::hash::{Address, H256, FixedHash}; use util::numbers::{Uint, U256}; -use util::keys::{TestAccount, TestAccountProvider}; +use util::keys::{AccountProvider, TestAccount, TestAccountProvider}; use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID}; use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; use ethcore::receipt::LocalizedReceipt; use ethcore::transaction::{Transaction, Action}; -use ethminer::ExternalMiner; +use ethminer::{ExternalMiner, MinerService}; +use ethsync::SyncState; use v1::{Eth, EthClient, EthSigning, EthSigningUnsafeClient}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService}; +use rustc_serialize::hex::ToHex; fn blockchain_client() -> Arc { let client = TestBlockChainClient::new(); @@ -95,9 +97,28 @@ fn rpc_eth_protocol_version() { } #[test] -#[ignore] fn rpc_eth_syncing() { - unimplemented!() + let request = r#"{"jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1}"#; + + let tester = EthTester::default(); + + let false_res = r#"{"jsonrpc":"2.0","result":false,"id":1}"#; + assert_eq!(tester.io.handle_request(request), Some(false_res.to_owned())); + + { + let mut status = tester.sync.status.write().unwrap(); + status.state = SyncState::Blocks; + status.highest_block_number = Some(2500); + + // causes TestBlockChainClient to return 1000 for its best block number. + let mut blocks = tester.client.blocks.write().unwrap(); + for i in 0..1000 { + blocks.insert(H256::from(i), Vec::new()); + } + } + + let true_res = r#"{"jsonrpc":"2.0","result":{"currentBlock":"0x03e8","highestBlock":"0x09c4","startingBlock":"0x00"},"id":1}"#; + assert_eq!(tester.io.handle_request(request), Some(true_res.to_owned())); } #[test] @@ -133,9 +154,47 @@ fn rpc_eth_submit_hashrate() { } #[test] -#[ignore] +fn rpc_eth_sign() { + let tester = EthTester::default(); + + let account = tester.accounts_provider.new_account("abcd").unwrap(); + let message = H256::from("0x0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f"); + let signed = tester.accounts_provider.sign(&account, &message).unwrap(); + + let req = r#"{ + "jsonrpc": "2.0", + "method": "eth_sign", + "params": [ + ""#.to_owned() + &format!("0x{:?}", account) + r#"", + "0x0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f" + ], + "id": 1 + }"#; + let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:?}", signed) + r#"","id":1}"#; + + assert_eq!(tester.io.handle_request(&req), Some(res)); +} + +#[test] fn rpc_eth_author() { - unimplemented!() + let make_res = |addr| r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:?}", addr) + r#"","id":1}"#; + let tester = EthTester::default(); + + let req = r#"{ + "jsonrpc": "2.0", + "method": "eth_coinbase", + "params": [], + "id": 1 + }"#; + + assert_eq!(tester.io.handle_request(req), Some(make_res(Address::zero()))); + + for i in 0..20 { + let addr = tester.accounts_provider.new_account(&format!("{}", i)).unwrap(); + tester.miner.set_author(addr.clone()); + + assert_eq!(tester.io.handle_request(req), Some(make_res(addr))); + } } #[test] @@ -196,18 +255,22 @@ fn rpc_eth_balance() { assert_eq!(tester.io.handle_request(request), Some(response.to_owned())); } -#[ignore] //TODO: propert test #[test] fn rpc_eth_balance_pending() { let tester = EthTester::default(); + tester.client.set_balance(Address::from(1), U256::from(5)); let request = r#"{ "jsonrpc": "2.0", "method": "eth_getBalance", - "params": ["0x0000000000000000000000000000000000000001", "latest"], + "params": ["0x0000000000000000000000000000000000000001", "pending"], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":"0x","id":1}"#; + + // the TestMinerService doesn't communicate with the the TestBlockChainClient in any way. + // if this returns zero, we know that the "pending" call is being properly forwarded to the + // miner. + let response = r#"{"jsonrpc":"2.0","result":"0x00","id":1}"#; assert_eq!(tester.io.handle_request(request), Some(response.to_owned())); } @@ -506,7 +569,7 @@ fn rpc_eth_send_transaction() { let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; - assert_eq!(tester.io.handle_request(request.as_ref()), Some(response)); + assert_eq!(tester.io.handle_request(&request), Some(response)); tester.miner.last_nonces.write().unwrap().insert(address.clone(), U256::zero()); @@ -521,19 +584,38 @@ fn rpc_eth_send_transaction() { let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", t.hash()).as_ref() + r#"","id":1}"#; - assert_eq!(tester.io.handle_request(request.as_ref()), Some(response)); + assert_eq!(tester.io.handle_request(&request), Some(response)); } #[test] -#[ignore] fn rpc_eth_send_raw_transaction() { - unimplemented!() -} + let tester = EthTester::default(); + let address = tester.accounts_provider.new_account("abcd").unwrap(); + let secret = tester.accounts_provider.account_secret(&address).unwrap(); -#[test] -#[ignore] -fn rpc_eth_sign() { - unimplemented!() + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(0x9184e72a000u64), + gas: U256::from(0x76c0), + action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + value: U256::from(0x9184e72au64), + data: vec![] + }.sign(&secret); + + let rlp = ::util::rlp::encode(&t).to_vec().to_hex(); + + let req = r#"{ + "jsonrpc": "2.0", + "method": "eth_sendRawTransaction", + "params": [ + "0x"#.to_owned() + &rlp + r#"" + ], + "id": 1 + }"#; + + let res = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:?}", t.hash()) + r#"","id":1}"#; + + assert_eq!(tester.io.handle_request(&req), Some(res)); } #[test] diff --git a/rpc/src/v1/tests/mocked/mod.rs b/rpc/src/v1/tests/mocked/mod.rs index 450960f78..986dbfdef 100644 --- a/rpc/src/v1/tests/mocked/mod.rs +++ b/rpc/src/v1/tests/mocked/mod.rs @@ -14,7 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! RPC serialization tests. +//! RPC mocked tests. Most of these test that the RPC server is serializing and forwarding +//! method calls properly. mod eth; mod eth_signing; diff --git a/rpc/src/v1/tests/mod.rs b/rpc/src/v1/tests/mod.rs index d3519eb1e..3455bfd1f 100644 --- a/rpc/src/v1/tests/mod.rs +++ b/rpc/src/v1/tests/mod.rs @@ -2,5 +2,40 @@ pub mod helpers; +// extract a chain from the given JSON file, +// stored in ethcore/res/ethereum/tests/. +// +// usage: +// `extract_chain!("Folder/File")` will load Folder/File.json and extract +// the first block chain stored within. +// +// `extract_chain!("Folder/File", "with_name")` will load Folder/File.json and +// extract the chain with that name. This will panic if no chain by that name +// is found. +macro_rules! extract_chain { + ($file:expr, $name:expr) => {{ + const RAW_DATA: &'static [u8] = + include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/", $file, ".json")); + let mut chain = None; + for (name, c) in ::ethjson::blockchain::Test::load(RAW_DATA).unwrap() { + if name == $name { + chain = Some(c); + break; + } + } + chain.unwrap() + }}; + + ($file:expr) => {{ + const RAW_DATA: &'static [u8] = + include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/", $file, ".json")); + + ::ethjson::blockchain::Test::load(RAW_DATA) + .unwrap().into_iter().next().unwrap().1 + }}; +} + #[cfg(test)] -mod mocked; \ No newline at end of file +mod mocked; +#[cfg(test)] +mod eth; diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 970bc5dd2..c4369ff2a 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -21,109 +21,109 @@ use jsonrpc_core::*; /// Eth rpc interface. pub trait Eth: Sized + Send + Sync + 'static { /// Returns protocol version. - fn protocol_version(&self, _: Params) -> Result { rpc_unimplemented!() } + fn protocol_version(&self, _: Params) -> Result; /// Returns an object with data about the sync status or false. (wtf?) - fn syncing(&self, _: Params) -> Result { rpc_unimplemented!() } + fn syncing(&self, _: Params) -> Result; /// Returns the number of hashes per second that the node is mining with. - fn hashrate(&self, _: Params) -> Result { rpc_unimplemented!() } + fn hashrate(&self, _: Params) -> Result; /// Returns block author. - fn author(&self, _: Params) -> Result { rpc_unimplemented!() } + fn author(&self, _: Params) -> Result; /// Returns true if client is actively mining new blocks. - fn is_mining(&self, _: Params) -> Result { rpc_unimplemented!() } + fn is_mining(&self, _: Params) -> Result; /// Returns current gas_price. - fn gas_price(&self, _: Params) -> Result { rpc_unimplemented!() } + fn gas_price(&self, _: Params) -> Result; /// Returns accounts list. - fn accounts(&self, _: Params) -> Result { rpc_unimplemented!() } + fn accounts(&self, _: Params) -> Result; /// Returns highest block number. - fn block_number(&self, _: Params) -> Result { rpc_unimplemented!() } + fn block_number(&self, _: Params) -> Result; /// Returns balance of the given account. - fn balance(&self, _: Params) -> Result { rpc_unimplemented!() } + fn balance(&self, _: Params) -> Result; /// Returns content of the storage at given address. - fn storage_at(&self, _: Params) -> Result { rpc_unimplemented!() } + fn storage_at(&self, _: Params) -> Result; /// Returns block with given hash. - fn block_by_hash(&self, _: Params) -> Result { rpc_unimplemented!() } + fn block_by_hash(&self, _: Params) -> Result; /// Returns block with given number. - fn block_by_number(&self, _: Params) -> Result { rpc_unimplemented!() } + fn block_by_number(&self, _: Params) -> Result; /// Returns the number of transactions sent from given address at given time (block number). - fn transaction_count(&self, _: Params) -> Result { rpc_unimplemented!() } + fn transaction_count(&self, _: Params) -> Result; /// Returns the number of transactions in a block with given hash. - fn block_transaction_count_by_hash(&self, _: Params) -> Result { rpc_unimplemented!() } + fn block_transaction_count_by_hash(&self, _: Params) -> Result; /// Returns the number of transactions in a block with given block number. - fn block_transaction_count_by_number(&self, _: Params) -> Result { rpc_unimplemented!() } + fn block_transaction_count_by_number(&self, _: Params) -> Result; /// Returns the number of uncles in a block with given hash. - fn block_uncles_count_by_hash(&self, _: Params) -> Result { rpc_unimplemented!() } + fn block_uncles_count_by_hash(&self, _: Params) -> Result; /// Returns the number of uncles in a block with given block number. - fn block_uncles_count_by_number(&self, _: Params) -> Result { rpc_unimplemented!() } + fn block_uncles_count_by_number(&self, _: Params) -> Result; /// Returns the code at given address at given time (block number). - fn code_at(&self, _: Params) -> Result { rpc_unimplemented!() } + fn code_at(&self, _: Params) -> Result; /// Sends signed transaction. - fn send_raw_transaction(&self, _: Params) -> Result { rpc_unimplemented!() } + fn send_raw_transaction(&self, _: Params) -> Result; /// Call contract. - fn call(&self, _: Params) -> Result { rpc_unimplemented!() } + fn call(&self, _: Params) -> Result; /// Estimate gas needed for execution of given contract. - fn estimate_gas(&self, _: Params) -> Result { rpc_unimplemented!() } + fn estimate_gas(&self, _: Params) -> Result; /// Get transaction by it's hash. - fn transaction_by_hash(&self, _: Params) -> Result { rpc_unimplemented!() } + fn transaction_by_hash(&self, _: Params) -> Result; /// Returns transaction at given block hash and index. - fn transaction_by_block_hash_and_index(&self, _: Params) -> Result { rpc_unimplemented!() } + fn transaction_by_block_hash_and_index(&self, _: Params) -> Result; /// Returns transaction by given block number and index. - fn transaction_by_block_number_and_index(&self, _: Params) -> Result { rpc_unimplemented!() } + fn transaction_by_block_number_and_index(&self, _: Params) -> Result; /// Returns transaction receipt. - fn transaction_receipt(&self, _: Params) -> Result { rpc_unimplemented!() } + fn transaction_receipt(&self, _: Params) -> Result; /// Returns an uncles at given block and index. - fn uncle_by_block_hash_and_index(&self, _: Params) -> Result { rpc_unimplemented!() } + fn uncle_by_block_hash_and_index(&self, _: Params) -> Result; /// Returns an uncles at given block and index. - fn uncle_by_block_number_and_index(&self, _: Params) -> Result { rpc_unimplemented!() } + fn uncle_by_block_number_and_index(&self, _: Params) -> Result; /// Returns available compilers. - fn compilers(&self, _: Params) -> Result { rpc_unimplemented!() } + fn compilers(&self, _: Params) -> Result; /// Compiles lll code. - fn compile_lll(&self, _: Params) -> Result { rpc_unimplemented!() } + fn compile_lll(&self, _: Params) -> Result; /// Compiles solidity. - fn compile_solidity(&self, _: Params) -> Result { rpc_unimplemented!() } + fn compile_solidity(&self, _: Params) -> Result; /// Compiles serpent. - fn compile_serpent(&self, _: Params) -> Result { rpc_unimplemented!() } + fn compile_serpent(&self, _: Params) -> Result; /// Returns logs matching given filter object. - fn logs(&self, _: Params) -> Result { rpc_unimplemented!() } + fn logs(&self, _: Params) -> Result; /// Returns the hash of the current block, the seedHash, and the boundary condition to be met. - fn work(&self, _: Params) -> Result { rpc_unimplemented!() } + fn work(&self, _: Params) -> Result; /// Used for submitting a proof-of-work solution. - fn submit_work(&self, _: Params) -> Result { rpc_unimplemented!() } + fn submit_work(&self, _: Params) -> Result; /// Used for submitting mining hashrate. - fn submit_hashrate(&self, _: Params) -> Result { rpc_unimplemented!() } + fn submit_hashrate(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { @@ -171,22 +171,22 @@ pub trait Eth: Sized + Send + Sync + 'static { // TODO: do filters api properly pub trait EthFilter: Sized + Send + Sync + 'static { /// Returns id of new filter. - fn new_filter(&self, _: Params) -> Result { rpc_unimplemented!() } + fn new_filter(&self, _: Params) -> Result; /// Returns id of new block filter. - fn new_block_filter(&self, _: Params) -> Result { rpc_unimplemented!() } + fn new_block_filter(&self, _: Params) -> Result; /// Returns id of new block filter. - fn new_pending_transaction_filter(&self, _: Params) -> Result { rpc_unimplemented!() } + fn new_pending_transaction_filter(&self, _: Params) -> Result; /// Returns filter changes since last poll. - fn filter_changes(&self, _: Params) -> Result { rpc_unimplemented!() } + fn filter_changes(&self, _: Params) -> Result; /// Returns all logs matching given filter (in a range 'from' - 'to'). - fn filter_logs(&self, _: Params) -> Result { rpc_unimplemented!() } + fn filter_logs(&self, _: Params) -> Result; /// Uninstalls filter. - fn uninstall_filter(&self, _: Params) -> Result { rpc_unimplemented!() } + fn uninstall_filter(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { @@ -204,10 +204,10 @@ pub trait EthFilter: Sized + Send + Sync + 'static { /// Signing methods implementation relying on unlocked accounts. pub trait EthSigning: Sized + Send + Sync + 'static { /// Signs the data with given address signature. - fn sign(&self, _: Params) -> Result { rpc_unimplemented!() } + fn sign(&self, _: Params) -> Result; /// Sends transaction. - fn send_transaction(&self, _: Params) -> Result { rpc_unimplemented!() } + fn send_transaction(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index 2a2159bd9..3646f6c5a 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -22,55 +22,55 @@ use jsonrpc_core::*; pub trait Ethcore: Sized + Send + Sync + 'static { /// Sets new minimal gas price for mined blocks. - fn set_min_gas_price(&self, _: Params) -> Result { rpc_unimplemented!() } + fn set_min_gas_price(&self, _: Params) -> Result; /// Sets new gas floor target for mined blocks. - fn set_gas_floor_target(&self, _: Params) -> Result { rpc_unimplemented!() } + fn set_gas_floor_target(&self, _: Params) -> Result; /// Sets new extra data for mined blocks. - fn set_extra_data(&self, _: Params) -> Result { rpc_unimplemented!() } + fn set_extra_data(&self, _: Params) -> Result; /// Sets new author for mined block. - fn set_author(&self, _: Params) -> Result { rpc_unimplemented!() } + fn set_author(&self, _: Params) -> Result; /// Sets the limits for transaction queue. - fn set_transactions_limit(&self, _: Params) -> Result { rpc_unimplemented!() } + fn set_transactions_limit(&self, _: Params) -> Result; /// Returns current transactions limit. - fn transactions_limit(&self, _: Params) -> Result { rpc_unimplemented!() } + fn transactions_limit(&self, _: Params) -> Result; /// Returns mining extra data. - fn extra_data(&self, _: Params) -> Result { rpc_unimplemented!() } + fn extra_data(&self, _: Params) -> Result; /// Returns mining gas floor target. - fn gas_floor_target(&self, _: Params) -> Result { rpc_unimplemented!() } + fn gas_floor_target(&self, _: Params) -> Result; /// Returns minimal gas price for transaction to be included in queue. - fn min_gas_price(&self, _: Params) -> Result { rpc_unimplemented!() } + fn min_gas_price(&self, _: Params) -> Result; /// Returns latest logs - fn dev_logs(&self, _: Params) -> Result { rpc_unimplemented!() } + fn dev_logs(&self, _: Params) -> Result; /// Returns logs levels - fn dev_logs_levels(&self, _: Params) -> Result { rpc_unimplemented!() } + fn dev_logs_levels(&self, _: Params) -> Result; /// Returns chain name - fn net_chain(&self, _: Params) -> Result { rpc_unimplemented!() } + fn net_chain(&self, _: Params) -> Result; /// Returns max peers - fn net_max_peers(&self, _: Params) -> Result { rpc_unimplemented!() } + fn net_max_peers(&self, _: Params) -> Result; /// Returns network port - fn net_port(&self, _: Params) -> Result { rpc_unimplemented!() } + fn net_port(&self, _: Params) -> Result; /// Returns rpc settings - fn rpc_settings(&self, _: Params) -> Result { rpc_unimplemented!() } + fn rpc_settings(&self, _: Params) -> Result; /// Returns node name - fn node_name(&self, _: Params) -> Result { rpc_unimplemented!() } + fn node_name(&self, _: Params) -> Result; /// Returns default extra data - fn default_extra_data(&self, _: Params) -> Result { rpc_unimplemented!() } + fn default_extra_data(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index 5384b0ef4..d994ffc24 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -16,10 +16,6 @@ //! Ethereum rpc interfaces. -macro_rules! rpc_unimplemented { - () => (Err(Error::internal_error())) -} - pub mod web3; pub mod eth; pub mod net; diff --git a/rpc/src/v1/traits/net.rs b/rpc/src/v1/traits/net.rs index 732d6efb2..56fba3e32 100644 --- a/rpc/src/v1/traits/net.rs +++ b/rpc/src/v1/traits/net.rs @@ -21,14 +21,14 @@ use jsonrpc_core::*; /// Net rpc interface. pub trait Net: Sized + Send + Sync + 'static { /// Returns protocol version. - fn version(&self, _: Params) -> Result { rpc_unimplemented!() } + fn version(&self, _: Params) -> Result; /// Returns number of peers connected to node. - fn peer_count(&self, _: Params) -> Result { rpc_unimplemented!() } + fn peer_count(&self, _: Params) -> Result; /// Returns true if client is actively listening for network connections. /// Otherwise false. - fn is_listening(&self, _: Params) -> Result { rpc_unimplemented!() } + fn is_listening(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index cde66be2c..a36358766 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -22,16 +22,16 @@ use jsonrpc_core::*; pub trait Personal: Sized + Send + Sync + 'static { /// Lists all stored accounts - fn accounts(&self, _: Params) -> Result { rpc_unimplemented!() } + fn accounts(&self, _: Params) -> Result; /// Creates new account (it becomes new current unlocked account) - fn new_account(&self, _: Params) -> Result { rpc_unimplemented!() } + fn new_account(&self, _: Params) -> Result; /// Unlocks specified account for use (can only be one unlocked account at one moment) - fn unlock_account(&self, _: Params) -> Result { rpc_unimplemented!() } + fn unlock_account(&self, _: Params) -> Result; /// Sends transaction and signs it in single call. The account is not unlocked in such case. - fn sign_and_send_transaction(&self, _: Params) -> Result { rpc_unimplemented!() } + fn sign_and_send_transaction(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { diff --git a/rpc/src/v1/traits/rpc.rs b/rpc/src/v1/traits/rpc.rs index 5c981c8a1..669d0d8c6 100644 --- a/rpc/src/v1/traits/rpc.rs +++ b/rpc/src/v1/traits/rpc.rs @@ -23,10 +23,10 @@ use jsonrpc_core::*; pub trait Rpc: Sized + Send + Sync + 'static { /// Returns supported modules for Geth 1.3.6 - fn modules(&self, _: Params) -> Result { rpc_unimplemented!() } + fn modules(&self, _: Params) -> Result; /// Returns supported modules for Geth 1.4.0 - fn rpc_modules(&self, _: Params) -> Result { rpc_unimplemented!() } + fn rpc_modules(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs index b42feede5..54d04b954 100644 --- a/rpc/src/v1/traits/traces.rs +++ b/rpc/src/v1/traits/traces.rs @@ -21,16 +21,16 @@ use jsonrpc_core::*; /// Traces specific rpc interface. pub trait Traces: Sized + Send + Sync + 'static { /// Returns traces matching given filter. - fn filter(&self, _: Params) -> Result { rpc_unimplemented!() } + fn filter(&self, _: Params) -> Result; /// Returns transaction trace at given index. - fn trace(&self, _: Params) -> Result { rpc_unimplemented!() } + fn trace(&self, _: Params) -> Result; /// Returns all traces of given transaction. - fn transaction_traces(&self, _: Params) -> Result { rpc_unimplemented!() } + fn transaction_traces(&self, _: Params) -> Result; /// Returns all traces produced at given block. - fn block_traces(&self, _: Params) -> Result { rpc_unimplemented!() } + fn block_traces(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { diff --git a/rpc/src/v1/traits/web3.rs b/rpc/src/v1/traits/web3.rs index c7b21a1a1..335911025 100644 --- a/rpc/src/v1/traits/web3.rs +++ b/rpc/src/v1/traits/web3.rs @@ -21,10 +21,10 @@ use jsonrpc_core::*; /// Web3 rpc interface. pub trait Web3: Sized + Send + Sync + 'static { /// Returns current client version. - fn client_version(&self, _: Params) -> Result { rpc_unimplemented!() } + fn client_version(&self, _: Params) -> Result; /// Returns sha3 of the given data - fn sha3(&self, _: Params) -> Result { rpc_unimplemented!() } + fn sha3(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { diff --git a/signer/src/lib.rs b/signer/src/lib.rs index 74018d9b1..e2df72bcc 100644 --- a/signer/src/lib.rs +++ b/signer/src/lib.rs @@ -31,10 +31,11 @@ //! ``` //! extern crate ethcore_signer; //! -//! use ethcore_signer::Server; +//! use ethcore_signer::ServerBuilder; //! //! fn main() { -//! let _server = Server::start("127.0.0.1:8084".parse().unwrap()); +//! let builder = ServerBuilder::new(); +//! let _server = builder.start("127.0.0.1:8084".parse().unwrap()).unwrap(); //! } //! ``` diff --git a/util/src/crypto.rs b/util/src/crypto.rs index 3eff70717..b92bb2bae 100644 --- a/util/src/crypto.rs +++ b/util/src/crypto.rs @@ -38,13 +38,10 @@ lazy_static! { impl Signature { /// Create a new signature from the R, S and V componenets. pub fn from_rsv(r: &H256, s: &H256, v: u8) -> Signature { - use std::ptr; let mut ret: Signature = Signature::new(); - unsafe { - let retslice: &mut [u8] = &mut ret; - ptr::copy(r.as_ptr(), retslice.as_mut_ptr(), 32); - ptr::copy(s.as_ptr(), retslice.as_mut_ptr().offset(32), 32); - } + (&mut ret[0..32]).copy_from_slice(r); + (&mut ret[32..64]).copy_from_slice(s); + ret[64] = v; ret } @@ -145,7 +142,10 @@ impl KeyPair { let (sec, publ) = try!(context.generate_keypair(&mut rng)); let serialized = publ.serialize_vec(context, false); let p: Public = Public::from_slice(&serialized[1..65]); - let s: Secret = unsafe { ::std::mem::transmute(sec) }; + + let mut s = Secret::new(); + s.copy_from_slice(&sec[0..32]); + Ok(KeyPair { secret: s, public: p, @@ -193,12 +193,14 @@ pub mod ec { /// Returns siganture of message hash. pub fn sign(secret: &Secret, message: &H256) -> Result { // TODO: allow creation of only low-s signatures. - use secp256k1::*; + use secp256k1::{Message, key}; + let context = &crypto::SECP256K1; + // no way to create from raw byte array. let sec: &key::SecretKey = unsafe { ::std::mem::transmute(secret) }; let s = try!(context.sign_recoverable(&try!(Message::from_slice(&message)), sec)); let (rec_id, data) = s.serialize_compact(context); - let mut signature: crypto::Signature = unsafe { ::std::mem::uninitialized() }; + let mut signature = crypto::Signature::new(); signature.clone_from_slice(&data); signature[64] = rec_id.to_i32() as u8; @@ -217,10 +219,12 @@ pub mod ec { let rsig = try!(RecoverableSignature::from_compact(context, &signature[0..64], try!(RecoveryId::from_i32(signature[64] as i32)))); let sig = rsig.to_standard(context); - let mut pdata: [u8; 65] = [4u8; 65]; - let ptr = pdata[1..].as_mut_ptr(); - let src = public.as_ptr(); - unsafe { ::std::ptr::copy_nonoverlapping(src, ptr, 64) }; + let pdata: [u8; 65] = { + let mut temp = [4u8; 65]; + (&mut temp[1..65]).copy_from_slice(public); + temp + }; + let publ = try!(key::PublicKey::from_slice(context, &pdata)); match context.verify(&try!(Message::from_slice(&message)), &sig, &publ) { Ok(_) => Ok(true), @@ -252,21 +256,27 @@ pub mod ec { /// ECDH functions #[cfg_attr(feature="dev", allow(similar_names))] pub mod ecdh { - use crypto::*; - use crypto::{self}; + use hash::FixedHash; + use crypto::{self, Secret, Public, CryptoError}; /// Agree on a shared secret - pub fn agree(secret: &Secret, public: &Public, ) -> Result { - use secp256k1::*; + pub fn agree(secret: &Secret, public: &Public) -> Result { + use secp256k1::{ecdh, key}; + let context = &crypto::SECP256K1; - let mut pdata: [u8; 65] = [4u8; 65]; - let ptr = pdata[1..].as_mut_ptr(); - let src = public.as_ptr(); - unsafe { ::std::ptr::copy_nonoverlapping(src, ptr, 64) }; + let pdata = { + let mut temp = [4u8; 65]; + (&mut temp[1..65]).copy_from_slice(&public[0..64]); + temp + }; + let publ = try!(key::PublicKey::from_slice(context, &pdata)); + // no way to create SecretKey from raw byte array. let sec: &key::SecretKey = unsafe { ::std::mem::transmute(secret) }; let shared = ecdh::SharedSecret::new_raw(context, &publ, &sec); - let s: Secret = unsafe { ::std::mem::transmute(shared) }; + + let mut s = crypto::Secret::new(); + s.copy_from_slice(&shared[0..32]); Ok(s) } } diff --git a/util/src/hash.rs b/util/src/hash.rs index e1b82b14c..ba96f812c 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -75,6 +75,7 @@ pub fn clean_0x(s: &str) -> &str { macro_rules! impl_hash { ($from: ident, $size: expr) => { #[derive(Eq)] + #[repr(C)] /// Unformatted binary data of fixed length. pub struct $from (pub [u8; $size]); diff --git a/util/src/keys/mod.rs b/util/src/keys/mod.rs index 5d718affc..38fa51eca 100644 --- a/util/src/keys/mod.rs +++ b/util/src/keys/mod.rs @@ -21,4 +21,5 @@ pub mod store; mod geth_import; mod test_account_provider; +pub use self::store::AccountProvider; pub use self::test_account_provider::{TestAccount, TestAccountProvider};