From 03ba0cc498a25b15729b27c8e6de315d33415820 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 4 Aug 2016 17:42:12 +0300 Subject: [PATCH 1/6] Bump json-ipc-server again (#1839) * bump ipc version * bumping once more --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index ddef7cba9..2b2874cd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -645,7 +645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "json-ipc-server" version = "0.2.4" -source = "git+https://github.com/ethcore/json-ipc-server.git#7a02a0f8b249fda100b9bab5f90b2081d410d8cf" +source = "git+https://github.com/ethcore/json-ipc-server.git#56b6307130710ebc73cb9be087b6ed0b6c400bcf" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", From 979f4e0617bbb15d8c6c3d454848afd382cdfff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 4 Aug 2016 16:42:29 +0200 Subject: [PATCH 2/6] eth_checkTransaction renamed to eth_checkRequest (#1817) * Making ConfirmationsQueue a bit more generic [WiP] * Generalizing cofirmations * New confirmations types - tests * Separating transaction type in queue. Closes #1310 * Handling sign requests * Speeding up tests * Renaming methods * eth_postSign * Bumping ui * Renaming checkRequest methods, adding tests * Removing duplicate method [ci skip] * Remove `_posted` [ci:skip] * Remove `_posted` --- rpc/src/v1/impls/eth_signing.rs | 10 ++--- rpc/src/v1/impls/mod.rs | 20 ++++++--- rpc/src/v1/tests/mocked/eth_signing.rs | 61 +++++++++++++++++++++++++- rpc/src/v1/traits/eth.rs | 9 ++-- 4 files changed, 85 insertions(+), 15 deletions(-) diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs index c4eb187fe..8cedd865a 100644 --- a/rpc/src/v1/impls/eth_signing.rs +++ b/rpc/src/v1/impls/eth_signing.rs @@ -26,7 +26,7 @@ use ethcore::account_provider::AccountProvider; use v1::helpers::{SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationsQueue, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest}; use v1::traits::EthSigning; use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U256 as RpcU256}; -use v1::impls::{default_gas_price, sign_and_dispatch, transaction_rejected_error, signer_disabled_error}; +use v1::impls::{default_gas_price, sign_and_dispatch, request_rejected_error, request_not_found_error, signer_disabled_error}; fn fill_optional_fields(request: TRequest, client: &C, miner: &M) -> FilledRequest where C: MiningBlockChainClient, M: MinerService { @@ -143,7 +143,7 @@ impl EthSigning for EthSigningQueueClient }) } - fn check_transaction(&self, params: Params) -> Result { + fn check_request(&self, params: Params) -> Result { try!(self.active()); let mut pending = self.pending.lock(); from_params::<(RpcU256, )>(params).and_then(|(id, )| { @@ -151,10 +151,10 @@ impl EthSigning for EthSigningQueueClient let res = match pending.get(&id) { Some(ref promise) => match promise.result() { ConfirmationResult::Waiting => { return Ok(Value::Null); } - ConfirmationResult::Rejected => Err(transaction_rejected_error()), + ConfirmationResult::Rejected => Err(request_rejected_error()), ConfirmationResult::Confirmed(rpc_response) => rpc_response, }, - _ => { return Err(Error::invalid_params()); } + _ => { return Err(request_not_found_error()); } }; pending.remove(&id); res @@ -225,7 +225,7 @@ impl EthSigning for EthSigningUnsafeClient where Err(signer_disabled_error()) } - fn check_transaction(&self, _: Params) -> Result { + fn check_request(&self, _: Params) -> Result { // We don't support this in non-signer mode. Err(signer_disabled_error()) } diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index a9dddf843..f0eb4a301 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -72,10 +72,11 @@ mod error_codes { pub const NO_AUTHOR_CODE: i64 = -32002; pub const UNKNOWN_ERROR: i64 = -32009; pub const TRANSACTION_ERROR: i64 = -32010; - pub const TRANSACTION_REJECTED: i64 = -32011; pub const ACCOUNT_LOCKED: i64 = -32020; pub const PASSWORD_INVALID: i64 = -32021; pub const SIGNER_DISABLED: i64 = -32030; + pub const REQUEST_REJECTED: i64 = -32040; + pub const REQUEST_NOT_FOUND: i64 = -32041; } fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result @@ -171,11 +172,20 @@ fn password_error(error: AccountError) -> Error { } } -/// Error returned when transaction is rejected (in Trusted Signer). -pub fn transaction_rejected_error() -> Error { +/// Error returned when request is rejected (in Trusted Signer). +pub fn request_rejected_error() -> Error { Error { - code: ErrorCode::ServerError(error_codes::TRANSACTION_REJECTED), - message: "Transaction has been rejected.".into(), + code: ErrorCode::ServerError(error_codes::REQUEST_REJECTED), + message: "Request has been rejected.".into(), + data: None, + } +} + +/// Error returned when request is not found in queue. +pub fn request_not_found_error() -> Error { + Error { + code: ErrorCode::ServerError(error_codes::REQUEST_NOT_FOUND), + message: "Request not found.".into(), data: None, } } diff --git a/rpc/src/v1/tests/mocked/eth_signing.rs b/rpc/src/v1/tests/mocked/eth_signing.rs index 69a21cab5..5acc935b9 100644 --- a/rpc/src/v1/tests/mocked/eth_signing.rs +++ b/rpc/src/v1/tests/mocked/eth_signing.rs @@ -17,7 +17,7 @@ use std::str::FromStr; use std::sync::Arc; use std::time::Duration; -use jsonrpc_core::IoHandler; +use jsonrpc_core::{IoHandler, to_value}; use v1::impls::EthSigningQueueClient; use v1::traits::EthSigning; use v1::helpers::{ConfirmationsQueue, SigningQueue}; @@ -107,6 +107,65 @@ fn should_post_sign_to_queue() { assert_eq!(tester.queue.requests().len(), 1); } +#[test] +fn should_check_status_of_request() { + // given + let tester = eth_signing(); + let address = Address::random(); + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_postSign", + "params": [ + ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "0x0000000000000000000000000000000000000000000000000000000000000005" + ], + "id": 1 + }"#; + tester.io.handle_request(&request).expect("Sent"); + + // when + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_checkRequest", + "params": ["0x1"], + "id": 1 + }"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + + // then + assert_eq!(tester.io.handle_request(&request), Some(response.to_owned())); +} + +#[test] +fn should_check_status_of_request_when_its_resolved() { + // given + let tester = eth_signing(); + let address = Address::random(); + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_postSign", + "params": [ + ""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", + "0x0000000000000000000000000000000000000000000000000000000000000005" + ], + "id": 1 + }"#; + tester.io.handle_request(&request).expect("Sent"); + tester.queue.request_confirmed(U256::from(1), to_value(&"Hello World!")); + + // when + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_checkRequest", + "params": ["0x1"], + "id": 1 + }"#; + let response = r#"{"jsonrpc":"2.0","result":"Hello World!","id":1}"#; + + // then + assert_eq!(tester.io.handle_request(&request), Some(response.to_owned())); +} + #[test] fn should_sign_if_account_is_unlocked() { // given diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 72dc17962..60b482916 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -220,20 +220,21 @@ pub trait EthSigning: Sized + Send + Sync + 'static { /// Will return a transaction ID for later use with check_transaction. fn post_transaction(&self, _: Params) -> Result; - /// Checks the progress of a previously posted transaction. + /// Checks the progress of a previously posted request (transaction/sign). /// Should be given a valid send_transaction ID. /// Returns the transaction hash, the zero hash (not yet available), + /// or the signature, /// or an error. - fn check_transaction(&self, _: Params) -> Result; + fn check_request(&self, _: Params) -> Result; /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { let mut delegate = IoDelegate::new(Arc::new(self)); delegate.add_method("eth_sign", EthSigning::sign); - delegate.add_method("eth_postSign", EthSigning::post_sign); delegate.add_method("eth_sendTransaction", EthSigning::send_transaction); + delegate.add_method("eth_postSign", EthSigning::post_sign); delegate.add_method("eth_postTransaction", EthSigning::post_transaction); - delegate.add_method("eth_checkTransaction", EthSigning::check_transaction); + delegate.add_method("eth_checkRequest", EthSigning::check_request); delegate } } From 25aabe6e52c751887c25d407ca48ee6322185ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 4 Aug 2016 18:17:21 +0200 Subject: [PATCH 3/6] Supporting blockid in eth_call and trace_call/trace_raw (#1837) * Supporting blockid in eth_call and trace_call/trace_raw * Nicer state diff handling * Purging deref.deref --- Cargo.lock | 4 +- ethcore/src/block_queue.rs | 4 +- ethcore/src/client/client.rs | 58 +++++++++++------------ ethcore/src/client/test_client.rs | 14 +++--- ethcore/src/client/traits.rs | 7 ++- ethcore/src/error.rs | 2 +- ethcore/src/miner/miner.rs | 23 +++++---- ethcore/src/miner/mod.rs | 4 +- ethcore/src/types/executed.rs | 20 +++++--- ethcore/src/types/ids.rs | 2 +- rpc/src/v1/impls/eth.rs | 30 ++---------- rpc/src/v1/impls/mod.rs | 29 +++++++++++- rpc/src/v1/impls/traces.rs | 15 +++--- rpc/src/v1/tests/helpers/miner_service.rs | 4 +- rpc/src/v1/tests/mocked/eth.rs | 55 +++++++++++++++++---- 15 files changed, 154 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b2874cd4..a9416fdc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1542,13 +1542,13 @@ dependencies = [ "checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf" "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf" "checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76" -"checksum elastic-array 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7ec9af4640023400b86c9ce9126c79ed17ab247ec10a2f530eb78e3893b51aa" +"checksum elastic-array 0.4.0 (git+https://github.com/ethcore/elastic-array)" = "" "checksum env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "aba65b63ffcc17ffacd6cf5aa843da7c5a25e3bd4bbe0b7def8b214e411250e5" "checksum eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)" = "" "checksum gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "3da3a2cbaeb01363c8e3704fd9fd0eb2ceb17c6f27abd4c1ef040fb57d20dc79" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" -"checksum heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "927f352867eb72d0ef81b0e2aa457cd9b0888b2d26672cf7ca5912d771215191" +"checksum heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "abb306abb8d398e053cfb1b3e7b72c2f580be048b85745c52652954f8ad1439c" "checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58" "checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae" "checksum hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb0f4d00bb781e559b6e66ae4b5479df0fdf9ab15949f52fa2f1f5de16d4cc07" diff --git a/ethcore/src/block_queue.rs b/ethcore/src/block_queue.rs index 6772c0461..51e5058f7 100644 --- a/ethcore/src/block_queue.rs +++ b/ethcore/src/block_queue.rs @@ -226,7 +226,7 @@ impl BlockQueue { }; let block_hash = block.header.hash(); - match verify_block_unordered(block.header, block.bytes, engine.deref().deref()) { + match verify_block_unordered(block.header, block.bytes, &**engine) { Ok(verified) => { let mut verifying = verification.verifying.lock(); for e in verifying.iter_mut() { @@ -319,7 +319,7 @@ impl BlockQueue { } } - match verify_block_basic(&header, &bytes, self.engine.deref().deref()) { + match verify_block_basic(&header, &bytes, &**self.engine) { Ok(()) => { self.processing.write().insert(h.clone()); self.verification.unverified.lock().push_back(UnverifiedBlock { header: header, bytes: bytes }); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 343eeec82..cb03df6b5 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -15,7 +15,6 @@ // along with Parity. If not, see . use std::collections::{HashSet, HashMap, VecDeque}; -use std::ops::Deref; use std::sync::{Arc, Weak}; use std::path::{Path}; use std::fmt; @@ -35,7 +34,7 @@ use util::kvdb::*; // other use views::{BlockView, HeaderView, BodyView}; -use error::{ImportError, ExecutionError, ReplayError, BlockError, ImportResult}; +use error::{ImportError, ExecutionError, CallError, BlockError, ImportResult}; use header::BlockNumber; use state::State; use spec::Spec; @@ -272,7 +271,7 @@ impl Client { } fn check_and_close_block(&self, block: &PreverifiedBlock) -> Result { - let engine = self.engine.deref().deref(); + let engine = &**self.engine; let header = &block.header; // Check the block isn't so old we won't be able to enact it. @@ -283,7 +282,7 @@ impl Client { } // Verify Block Family - let verify_family_result = self.verifier.verify_block_family(header, &block.bytes, engine, self.chain.deref()); + let verify_family_result = self.verifier.verify_block_family(header, &block.bytes, engine, &*self.chain); if let Err(e) = verify_family_result { warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); @@ -639,8 +638,8 @@ impl Client { } impl BlockChainClient for Client { - fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result { - let header = self.block_header(BlockID::Latest).unwrap(); + fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result { + let header = try!(self.block_header(block).ok_or(CallError::StatePruned)); let view = HeaderView::new(&header); let last_hashes = self.build_last_hashes(view.hash()); let env_info = EnvInfo { @@ -653,7 +652,9 @@ impl BlockChainClient for Client { gas_limit: U256::max_value(), }; // that's just a copy of the state. - let mut state = self.state(); + let mut state = try!(self.state_at(block).ok_or(CallError::StatePruned)); + let original_state = if analytics.state_diffing { Some(state.clone()) } else { None }; + let sender = try!(t.sender().map_err(|e| { let message = format!("Transaction malformed: {:?}", e); ExecutionError::TransactionMalformed(message) @@ -665,26 +666,23 @@ impl BlockChainClient for Client { state.add_balance(&sender, &(needed_balance - balance)); } let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; - let mut ret = Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options); + let mut ret = try!(Executive::new(&mut state, &env_info, &**self.engine, &self.vm_factory).transact(t, options)); // TODO gav move this into Executive. - if analytics.state_diffing { - if let Ok(ref mut x) = ret { - x.state_diff = Some(state.diff_from(self.state())); - } - } - ret + ret.state_diff = original_state.map(|original| state.diff_from(original)); + + Ok(ret) } - fn replay(&self, id: TransactionID, analytics: CallAnalytics) -> Result { - let address = try!(self.transaction_address(id).ok_or(ReplayError::TransactionNotFound)); - let header_data = try!(self.block_header(BlockID::Hash(address.block_hash)).ok_or(ReplayError::StatePruned)); - let body_data = try!(self.block_body(BlockID::Hash(address.block_hash)).ok_or(ReplayError::StatePruned)); - let mut state = try!(self.state_at_beginning(BlockID::Hash(address.block_hash)).ok_or(ReplayError::StatePruned)); + fn replay(&self, id: TransactionID, analytics: CallAnalytics) -> Result { + let address = try!(self.transaction_address(id).ok_or(CallError::TransactionNotFound)); + let header_data = try!(self.block_header(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned)); + let body_data = try!(self.block_body(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned)); + let mut state = try!(self.state_at_beginning(BlockID::Hash(address.block_hash)).ok_or(CallError::StatePruned)); let txs = BodyView::new(&body_data).transactions(); if address.index >= txs.len() { - return Err(ReplayError::TransactionNotFound); + return Err(CallError::TransactionNotFound); } let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; @@ -700,20 +698,18 @@ impl BlockChainClient for Client { gas_limit: view.gas_limit(), }; for t in txs.iter().take(address.index) { - match Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, Default::default()) { + match Executive::new(&mut state, &env_info, &**self.engine, &self.vm_factory).transact(t, Default::default()) { Ok(x) => { env_info.gas_used = env_info.gas_used + x.gas_used; } - Err(ee) => { return Err(ReplayError::Execution(ee)) } + Err(ee) => { return Err(CallError::Execution(ee)) } } } let t = &txs[address.index]; - let orig = state.clone(); - let mut ret = Executive::new(&mut state, &env_info, self.engine.deref().deref(), &self.vm_factory).transact(t, options); - if analytics.state_diffing { - if let Ok(ref mut x) = ret { - x.state_diff = Some(state.diff_from(orig)); - } - } - ret.map_err(ReplayError::Execution) + + let original_state = if analytics.state_diffing { Some(state.clone()) } else { None }; + let mut ret = try!(Executive::new(&mut state, &env_info, &**self.engine, &self.vm_factory).transact(t, options)); + ret.state_diff = original_state.map(|original| state.diff_from(original)); + + Ok(ret) } fn keep_alive(&self) { @@ -1002,7 +998,7 @@ impl BlockChainClient for Client { impl MiningBlockChainClient for Client { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { - let engine = self.engine.deref().deref(); + let engine = &**self.engine; let h = self.chain.best_block_hash(); let mut open_block = OpenBlock::new( diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 7698bf07d..be63338f4 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -37,7 +37,7 @@ use spec::Spec; use block_queue::BlockQueueInfo; use block::{OpenBlock, SealedBlock}; use executive::Executed; -use error::{ExecutionError, ReplayError}; +use error::CallError; use trace::LocalizedTrace; /// Test client. @@ -61,7 +61,7 @@ pub struct TestBlockChainClient { /// Code. pub code: RwLock>, /// Execution result. - pub execution_result: RwLock>, + pub execution_result: RwLock>>, /// Transaction receipts. pub receipts: RwLock>, /// Block queue size. @@ -125,7 +125,7 @@ impl TestBlockChainClient { } /// Set the execution result. - pub fn set_execution_result(&self, result: Executed) { + pub fn set_execution_result(&self, result: Result) { *self.execution_result.write() = Some(result); } @@ -292,12 +292,12 @@ impl MiningBlockChainClient for TestBlockChainClient { } impl BlockChainClient for TestBlockChainClient { - fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result { - Ok(self.execution_result.read().clone().unwrap()) + fn call(&self, _t: &SignedTransaction, _block: BlockID, _analytics: CallAnalytics) -> Result { + self.execution_result.read().clone().unwrap() } - fn replay(&self, _id: TransactionID, _analytics: CallAnalytics) -> Result { - Ok(self.execution_result.read().clone().unwrap()) + fn replay(&self, _id: TransactionID, _analytics: CallAnalytics) -> Result { + self.execution_result.read().clone().unwrap() } fn block_total_difficulty(&self, _id: BlockID) -> Option { diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 348b90c90..ca4b8e6b1 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -26,7 +26,7 @@ use transaction::{LocalizedTransaction, SignedTransaction}; use log_entry::LocalizedLogEntry; use filter::Filter; use views::{BlockView}; -use error::{ImportResult, ExecutionError, ReplayError}; +use error::{ImportResult, CallError}; use receipt::LocalizedReceipt; use trace::LocalizedTrace; use evm::Factory as EvmFactory; @@ -154,11 +154,10 @@ pub trait BlockChainClient : Sync + Send { fn logs(&self, filter: Filter) -> Vec; /// Makes a non-persistent transaction call. - // TODO: should be able to accept blockchain location for call. - fn call(&self, t: &SignedTransaction, analytics: CallAnalytics) -> Result; + fn call(&self, t: &SignedTransaction, block: BlockID, analytics: CallAnalytics) -> Result; /// Replays a given transaction for inspection. - fn replay(&self, t: TransactionID, analytics: CallAnalytics) -> Result; + fn replay(&self, t: TransactionID, analytics: CallAnalytics) -> Result; /// Returns traces matching given filter. fn filter_traces(&self, filter: TraceFilter) -> Option>; diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 9fbee0796..61c024443 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -22,7 +22,7 @@ use basic_types::LogBloom; use client::Error as ClientError; use ipc::binary::{BinaryConvertError, BinaryConvertable}; use types::block_import_error::BlockImportError; -pub use types::executed::{ExecutionError, ReplayError}; +pub use types::executed::{ExecutionError, CallError}; #[derive(Debug, PartialEq, Clone)] /// Errors concerning transaction processing. diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 98cd137ed..f2296de00 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -171,7 +171,7 @@ pub struct Miner { // for sealing... options: MinerOptions, - + next_allowed_reseal: Mutex, sealing_block_last_request: Mutex, gas_range_target: RwLock<(U256, U256)>, @@ -420,7 +420,7 @@ impl Miner { *sealing_block_last_request = best_number; } - // Return if we restarted + // Return if we restarted prepare_new } @@ -464,7 +464,7 @@ impl MinerService for Miner { } } - fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result { + fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result { let sealing_work = self.sealing_work.lock(); match sealing_work.queue.peek_last_ref() { Some(work) => { @@ -484,6 +484,8 @@ impl MinerService for Miner { }; // that's just a copy of the state. let mut state = block.state().clone(); + let original_state = if analytics.state_diffing { Some(state.clone()) } else { None }; + let sender = try!(t.sender().map_err(|e| { let message = format!("Transaction malformed: {:?}", e); ExecutionError::TransactionMalformed(message) @@ -495,18 +497,15 @@ impl MinerService for Miner { state.add_balance(&sender, &(needed_balance - balance)); } let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; - let mut ret = Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options); + let mut ret = try!(Executive::new(&mut state, &env_info, self.engine(), chain.vm_factory()).transact(t, options)); // TODO gav move this into Executive. - if analytics.state_diffing { - if let Ok(ref mut x) = ret { - x.state_diff = Some(state.diff_from(block.state().clone())); - } - } - ret + ret.state_diff = original_state.map(|original| state.diff_from(original)); + + Ok(ret) }, None => { - chain.call(t, analytics) + chain.call(t, BlockID::Latest, analytics) } } } @@ -770,7 +769,7 @@ impl MinerService for Miner { false } }; - + if requires_reseal { // -------------------------------------------------------------------------- // | NOTE Code below requires transaction_queue and sealing_work locks. | diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 15933901b..77a372c65 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -58,7 +58,7 @@ use util::{H256, U256, Address, Bytes}; use client::{MiningBlockChainClient, Executed, CallAnalytics}; use block::ClosedBlock; use receipt::Receipt; -use error::{Error, ExecutionError}; +use error::{Error, CallError}; use transaction::SignedTransaction; /// Miner client API @@ -163,7 +163,7 @@ pub trait MinerService : Send + Sync { fn balance(&self, chain: &MiningBlockChainClient, address: &Address) -> U256; /// Call into contract code using pending state. - fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result; + fn call(&self, chain: &MiningBlockChainClient, t: &SignedTransaction, analytics: CallAnalytics) -> Result; /// Get storage value in pending state. fn storage_at(&self, chain: &MiningBlockChainClient, address: &Address, position: &H256) -> H256; diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index efc4da9e2..63e1f4a2a 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -105,7 +105,7 @@ pub struct Executed { } /// Result of executing the transaction. -#[derive(PartialEq, Debug, Binary)] +#[derive(PartialEq, Debug, Clone, Binary)] pub enum ExecutionError { /// Returned when there gas paid for transaction execution is /// lower than base gas required. @@ -172,19 +172,25 @@ impl fmt::Display for ExecutionError { } /// Result of executing the transaction. -#[derive(PartialEq, Debug, Binary)] -pub enum ReplayError { +#[derive(PartialEq, Debug, Clone, Binary)] +pub enum CallError { /// Couldn't find the transaction in the chain. TransactionNotFound, - /// Couldn't find the transaction block's state in the chain. + /// Couldn't find requested block's state in the chain. StatePruned, /// Error executing. Execution(ExecutionError), } -impl fmt::Display for ReplayError { +impl From for CallError { + fn from(error: ExecutionError) -> Self { + CallError::Execution(error) + } +} + +impl fmt::Display for CallError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::ReplayError::*; + use self::CallError::*; let msg = match *self { TransactionNotFound => "Transaction couldn't be found in the chain".into(), @@ -192,7 +198,7 @@ impl fmt::Display for ReplayError { Execution(ref e) => format!("{}", e), }; - f.write_fmt(format_args!("Transaction replay error ({}).", msg)) + f.write_fmt(format_args!("Transaction execution error ({}).", msg)) } } diff --git a/ethcore/src/types/ids.rs b/ethcore/src/types/ids.rs index 7fcd0fd19..d62a1e7db 100644 --- a/ethcore/src/types/ids.rs +++ b/ethcore/src/types/ids.rs @@ -23,7 +23,7 @@ use std::mem; use std::collections::VecDeque; /// Uniquely identifies block. -#[derive(Debug, PartialEq, Clone, Hash, Eq, Binary)] +#[derive(Debug, PartialEq, Copy, Clone, Hash, Eq, Binary)] pub enum BlockID { /// Block's sha3. /// Querying by hash is always faster. diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index c704650a3..0d2b6164a 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -44,8 +44,7 @@ use self::ethash::SeedHashCompute; use v1::traits::Eth; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, Index, Filter, Log, Receipt, H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256}; use v1::helpers::CallRequest as CRequest; -use v1::impls::{default_gas_price, dispatch_transaction, error_codes}; -use serde; +use v1::impls::{default_gas_price, dispatch_transaction, error_codes, from_params_default_second, from_params_default_third}; /// Eth RPC options pub struct EthClientOptions { @@ -215,27 +214,6 @@ pub fn pending_logs(miner: &M, filter: &EthcoreFilter) -> Vec where M: M const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6. -fn params_len(params: &Params) -> usize { - match params { - &Params::Array(ref vec) => vec.len(), - _ => 0, - } -} - -fn from_params_default_second(params: Params) -> Result<(F, BlockNumber, ), Error> where F: serde::de::Deserialize { - match params_len(¶ms) { - 1 => from_params::<(F, )>(params).map(|(f,)| (f, BlockNumber::Latest)), - _ => from_params::<(F, BlockNumber)>(params), - } -} - -fn from_params_default_third(params: Params) -> Result<(F1, F2, BlockNumber, ), Error> where F1: serde::de::Deserialize, F2: serde::de::Deserialize { - match params_len(¶ms) { - 2 => from_params::<(F1, F2, )>(params).map(|(f1, f2)| (f1, f2, BlockNumber::Latest)), - _ => from_params::<(F1, F2, BlockNumber)>(params) - } -} - fn make_unsupported_err() -> Error { Error { code: ErrorCode::ServerError(error_codes::UNSUPPORTED_REQUEST_CODE), @@ -656,8 +634,7 @@ impl Eth for EthClient where let signed = try!(self.sign_call(request)); let r = match block_number { BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()), - BlockNumber::Latest => take_weak!(self.client).call(&signed, Default::default()), - _ => panic!("{:?}", block_number), + block_number => take_weak!(self.client).call(&signed, block_number.into(), Default::default()), }; to_value(&r.map(|e| Bytes(e.output)).unwrap_or(Bytes::new(vec![]))) }) @@ -671,8 +648,7 @@ impl Eth for EthClient where let signed = try!(self.sign_call(request)); let r = match block_number { BlockNumber::Pending => take_weak!(self.miner).call(take_weak!(self.client).deref(), &signed, Default::default()), - BlockNumber::Latest => take_weak!(self.client).call(&signed, Default::default()), - _ => return Err(Error::invalid_params()), + block => take_weak!(self.client).call(&signed, block.into(), Default::default()), }; to_value(&RpcU256::from(r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))) }) diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index f0eb4a301..e21014eff 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -53,8 +53,9 @@ pub use self::ethcore_set::EthcoreSetClient; pub use self::traces::TracesClient; pub use self::rpc::RpcClient; +use serde; use v1::helpers::TransactionRequest; -use v1::types::{H256 as RpcH256, H520 as RpcH520}; +use v1::types::{H256 as RpcH256, H520 as RpcH520, BlockNumber}; use ethcore::error::Error as EthcoreError; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; @@ -63,7 +64,7 @@ use ethcore::account_provider::{AccountProvider, Error as AccountError}; use util::numbers::*; use util::rlp::encode; use util::bytes::ToPretty; -use jsonrpc_core::{Error, ErrorCode, Value, to_value}; +use jsonrpc_core::{Error, ErrorCode, Value, to_value, from_params, Params}; mod error_codes { // NOTE [ToDr] Codes from [-32099, -32000] @@ -79,6 +80,30 @@ mod error_codes { pub const REQUEST_NOT_FOUND: i64 = -32041; } +fn params_len(params: &Params) -> usize { + match params { + &Params::Array(ref vec) => vec.len(), + _ => 0, + } +} + +/// Deserialize request parameters with optional second parameter `BlockNumber` defaulting to `BlockNumber::Latest`. +pub fn from_params_default_second(params: Params) -> Result<(F, BlockNumber, ), Error> where F: serde::de::Deserialize { + match params_len(¶ms) { + 1 => from_params::<(F, )>(params).map(|(f,)| (f, BlockNumber::Latest)), + _ => from_params::<(F, BlockNumber)>(params), + } +} + +/// Deserialize request parameters with optional third parameter `BlockNumber` defaulting to `BlockNumber::Latest`. +pub fn from_params_default_third(params: Params) -> Result<(F1, F2, BlockNumber, ), Error> where F1: serde::de::Deserialize, F2: serde::de::Deserialize { + match params_len(¶ms) { + 2 => from_params::<(F1, F2, )>(params).map(|(f1, f2)| (f1, f2, BlockNumber::Latest)), + _ => from_params::<(F1, F2, BlockNumber)>(params) + } +} + + fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result where C: MiningBlockChainClient, M: MinerService { let hash = RpcH256::from(signed_transaction.hash()); diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 9ea045b0f..d8d2b0257 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -25,6 +25,7 @@ use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Act use v1::traits::Traces; use v1::helpers::CallRequest as CRequest; use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, Bytes, TraceResults, H256}; +use v1::impls::from_params_default_third; fn to_call_analytics(flags: Vec) -> CallAnalytics { CallAnalytics { @@ -122,11 +123,11 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: fn call(&self, params: Params) -> Result { try!(self.active()); - from_params(params) - .and_then(|(request, flags)| { + from_params_default_third(params) + .and_then(|(request, flags, block)| { let request = CallRequest::into(request); let signed = try!(self.sign_call(request)); - match take_weak!(self.client).call(&signed, to_call_analytics(flags)) { + match take_weak!(self.client).call(&signed, block.into(), to_call_analytics(flags)) { Ok(e) => to_value(&TraceResults::from(e)), _ => Ok(Value::Null), } @@ -135,11 +136,11 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: fn raw_transaction(&self, params: Params) -> Result { try!(self.active()); - from_params::<(Bytes, _)>(params) - .and_then(|(raw_transaction, flags)| { - let raw_transaction = raw_transaction.to_vec(); + from_params_default_third(params) + .and_then(|(raw_transaction, flags, block)| { + let raw_transaction = Bytes::to_vec(raw_transaction); match UntrustedRlp::new(&raw_transaction).as_val() { - Ok(signed) => match take_weak!(self.client).call(&signed, to_call_analytics(flags)) { + Ok(signed) => match take_weak!(self.client).call(&signed, block.into(), to_call_analytics(flags)) { Ok(e) => to_value(&TraceResults::from(e)), _ => Ok(Value::Null), }, diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index 5cce14533..b3a3deedf 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -18,7 +18,7 @@ use util::{Address, H256, Bytes, U256, FixedHash, Uint}; use util::standard::*; -use ethcore::error::{Error, ExecutionError}; +use ethcore::error::{Error, CallError}; use ethcore::client::{MiningBlockChainClient, Executed, CallAnalytics}; use ethcore::block::{ClosedBlock, IsBlock}; use ethcore::transaction::SignedTransaction; @@ -220,7 +220,7 @@ impl MinerService for TestMinerService { self.latest_closed_block.lock().as_ref().map_or_else(U256::zero, |b| b.block().fields().state.balance(address).clone()) } - fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result { + fn call(&self, _chain: &MiningBlockChainClient, _t: &SignedTransaction, _analytics: CallAnalytics) -> Result { unimplemented!(); } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 9e819c1b0..813dc5136 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -423,9 +423,9 @@ fn rpc_eth_code() { } #[test] -fn rpc_eth_call() { +fn rpc_eth_call_latest() { let tester = EthTester::default(); - tester.client.set_execution_result(Executed { + tester.client.set_execution_result(Ok(Executed { gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -436,7 +436,7 @@ fn rpc_eth_call() { trace: vec![], vm_trace: None, state_diff: None, - }); + })); let request = r#"{ "jsonrpc": "2.0", @@ -458,9 +458,9 @@ fn rpc_eth_call() { } #[test] -fn rpc_eth_call_default_block() { +fn rpc_eth_call() { let tester = EthTester::default(); - tester.client.set_execution_result(Executed { + tester.client.set_execution_result(Ok(Executed { gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -471,7 +471,42 @@ fn rpc_eth_call_default_block() { trace: vec![], vm_trace: None, state_diff: None, - }); + })); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }, + "0x0"], + "id": 1 + }"#; + let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#; + + assert_eq!(tester.io.handle_request(request), Some(response.to_owned())); +} + +#[test] +fn rpc_eth_call_default_block() { + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); let request = r#"{ "jsonrpc": "2.0", @@ -494,7 +529,7 @@ fn rpc_eth_call_default_block() { #[test] fn rpc_eth_estimate_gas() { let tester = EthTester::default(); - tester.client.set_execution_result(Executed { + tester.client.set_execution_result(Ok(Executed { gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -505,7 +540,7 @@ fn rpc_eth_estimate_gas() { trace: vec![], vm_trace: None, state_diff: None, - }); + })); let request = r#"{ "jsonrpc": "2.0", @@ -529,7 +564,7 @@ fn rpc_eth_estimate_gas() { #[test] fn rpc_eth_estimate_gas_default_block() { let tester = EthTester::default(); - tester.client.set_execution_result(Executed { + tester.client.set_execution_result(Ok(Executed { gas: U256::zero(), gas_used: U256::from(0xff30), refunded: U256::from(0x5), @@ -540,7 +575,7 @@ fn rpc_eth_estimate_gas_default_block() { trace: vec![], vm_trace: None, state_diff: None, - }); + })); let request = r#"{ "jsonrpc": "2.0", From aa59aa439de112212a6161e65d88042eadd4acdc Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Thu, 4 Aug 2016 19:17:39 +0300 Subject: [PATCH 4/6] Fix state not using "account_starting_nonce" (#1830) * failng test * use account_starting_nonce instead of zero * simplier test * jsons are getting closer * incorrect test client and incorrect tests fix * null_morden is using 0x0 starting nonce * replaced json with the correct one * superwhatever line --- ethcore/res/null_morden.json | 2 +- ethcore/src/client/test_client.rs | 2 +- ethcore/src/state.rs | 2 +- rpc/src/v1/tests/eth.rs | 66 +++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/ethcore/res/null_morden.json b/ethcore/res/null_morden.json index c820579d0..9b792934a 100644 --- a/ethcore/res/null_morden.json +++ b/ethcore/res/null_morden.json @@ -4,7 +4,7 @@ "Null": null }, "params": { - "accountStartNonce": "0x0100000", + "accountStartNonce": "0x0", "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID" : "0x2" diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index be63338f4..0e7157f38 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -310,7 +310,7 @@ impl BlockChainClient for TestBlockChainClient { fn nonce(&self, address: &Address, id: BlockID) -> Option { match id { - BlockID::Latest => Some(self.nonces.read().get(address).cloned().unwrap_or_else(U256::zero)), + BlockID::Latest => Some(self.nonces.read().get(address).cloned().unwrap_or(self.spec.params.account_start_nonce)), _ => None, } } diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index e08b0d8f4..28091a29d 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -175,7 +175,7 @@ impl State { /// Get the nonce of account `a`. pub fn nonce(&self, a: &Address) -> U256 { self.ensure_cached(a, false, - |a| a.as_ref().map_or(U256::zero(), |account| *account.nonce())) + |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) } /// Mutate storage of account `address` so that it is `value` for `key`. diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 7e871048e..4f2a5d7f7 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -238,6 +238,54 @@ const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{ } "#; +const POSITIVE_NONCE_SPEC: &'static [u8] = br#"{ + "name": "Frontier (Test)", + "engine": { + "Ethash": { + "params": { + "gasLimitBoundDivisor": "0x0400", + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "frontierCompatibilityModeLimit": "0xffffffffffffffff", + "daoHardforkTransition": "0xffffffffffffffff", + "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", + "daoHardforkAccounts": [] + } + } + }, + "params": { + "accountStartNonce": "0x0100", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x50000", + "networkID" : "0x1" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x50000" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "faa34835af5c2ea724333018a515fbb7d5bc0b33": { "balance": "10000000000000", "nonce": "0" } + } +} +"#; + #[test] fn eth_transaction_count() { use util::crypto::Secret; @@ -367,6 +415,24 @@ fn verify_transaction_counts(name: String, chain: BlockChain) { } } +#[test] +fn starting_nonce_test() { + let tester = EthTester::from_spec_provider(|| Spec::load(POSITIVE_NONCE_SPEC)); + let address = ::util::hash::Address::from(10); + + let sample = tester.handler.handle_request(&(r#" + { + "jsonrpc": "2.0", + "method": "eth_getTransactionCount", + "params": [""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"", "latest"], + "id": 15 + } + "#) + ).unwrap(); + + assert_eq!(r#"{"jsonrpc":"2.0","result":"0x0100","id":15}"#, &sample); +} + register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest"); register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest"); register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest"); From 8702a29e6f6ff115bb09721502a7303536b64533 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 4 Aug 2016 23:54:26 +0200 Subject: [PATCH 5/6] use mutex in dbtransaction (#1843) --- util/src/kvdb.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/util/src/kvdb.rs b/util/src/kvdb.rs index 1b1aa8ead..469a124bb 100644 --- a/util/src/kvdb.rs +++ b/util/src/kvdb.rs @@ -28,7 +28,7 @@ const DB_BACKGROUND_COMPACTIONS: i32 = 2; /// Write transaction. Batches a sequence of put/delete operations for efficiency. pub struct DBTransaction { - ops: RwLock>, + ops: Mutex>, } enum DBOp { @@ -52,7 +52,7 @@ impl DBTransaction { /// Create new transaction. pub fn new(_db: &Database) -> DBTransaction { DBTransaction { - ops: RwLock::new(Vec::with_capacity(256)), + ops: Mutex::new(Vec::with_capacity(256)), } } @@ -60,7 +60,7 @@ impl DBTransaction { pub fn put(&self, col: Option, key: &[u8], value: &[u8]) -> Result<(), String> { let mut ekey = ElasticArray32::new(); ekey.append_slice(key); - self.ops.write().push(DBOp::Insert { + self.ops.lock().push(DBOp::Insert { col: col, key: ekey, value: value.to_vec(), @@ -72,7 +72,7 @@ impl DBTransaction { pub fn put_vec(&self, col: Option, key: &[u8], value: Bytes) -> Result<(), String> { let mut ekey = ElasticArray32::new(); ekey.append_slice(key); - self.ops.write().push(DBOp::Insert { + self.ops.lock().push(DBOp::Insert { col: col, key: ekey, value: value, @@ -85,7 +85,7 @@ impl DBTransaction { pub fn put_compressed(&self, col: Option, key: &[u8], value: Bytes) -> Result<(), String> { let mut ekey = ElasticArray32::new(); ekey.append_slice(key); - self.ops.write().push(DBOp::InsertCompressed { + self.ops.lock().push(DBOp::InsertCompressed { col: col, key: ekey, value: value, @@ -97,7 +97,7 @@ impl DBTransaction { pub fn delete(&self, col: Option, key: &[u8]) -> Result<(), String> { let mut ekey = ElasticArray32::new(); ekey.append_slice(key); - self.ops.write().push(DBOp::Delete { + self.ops.lock().push(DBOp::Delete { col: col, key: ekey, }); @@ -290,30 +290,30 @@ impl Database { } - fn to_overly_column(col: Option) -> usize { + fn to_overlay_column(col: Option) -> usize { col.map_or(0, |c| (c + 1) as usize) } /// Commit transaction to database. pub fn write_buffered(&self, tr: DBTransaction) -> Result<(), String> { let mut overlay = self.overlay.write(); - let ops = mem::replace(&mut *tr.ops.write(), Vec::new()); + let ops = tr.ops.into_inner(); for op in ops { match op { DBOp::Insert { col, key, value } => { - let c = Self::to_overly_column(col); + let c = Self::to_overlay_column(col); overlay[c].deletions.remove(&key); overlay[c].compressed_insertions.remove(&key); overlay[c].insertions.insert(key, value); }, DBOp::InsertCompressed { col, key, value } => { - let c = Self::to_overly_column(col); + let c = Self::to_overlay_column(col); overlay[c].deletions.remove(&key); overlay[c].insertions.remove(&key); overlay[c].compressed_insertions.insert(key, value); }, DBOp::Delete { col, key } => { - let c = Self::to_overly_column(col); + let c = Self::to_overlay_column(col); overlay[c].insertions.remove(&key); overlay[c].compressed_insertions.remove(&key); overlay[c].deletions.insert(key); @@ -364,7 +364,7 @@ impl Database { /// Commit transaction to database. pub fn write(&self, tr: DBTransaction) -> Result<(), String> { let batch = WriteBatch::new(); - let ops = mem::replace(&mut *tr.ops.write(), Vec::new()); + let ops = tr.ops.into_inner(); for op in ops { match op { DBOp::Insert { col, key, value } => { @@ -384,7 +384,7 @@ impl Database { /// Get value by key. pub fn get(&self, col: Option, key: &[u8]) -> Result, String> { - let overlay = &self.overlay.read()[Self::to_overly_column(col)]; + let overlay = &self.overlay.read()[Self::to_overlay_column(col)]; overlay.insertions.get(key).or_else(|| overlay.compressed_insertions.get(key)).map_or_else(|| col.map_or_else( || self.db.get(key).map(|r| r.map(|v| v.to_vec())), From b20833154e1abe5bc5630bd757988d64efbfff2a Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Fri, 5 Aug 2016 09:15:55 +0200 Subject: [PATCH 6/6] protect unsafety in plainhasher; do more hashing (#1841) --- util/src/hash.rs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/util/src/hash.rs b/util/src/hash.rs index 62b6dfd8f..690c37b8f 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -543,27 +543,37 @@ impl_hash!(H2048, 256); // Specialized HashMap and HashSet /// Hasher that just takes 8 bytes of the provided value. -pub struct PlainHasher(u64); +/// May only be used for keys which are 32 bytes. +pub struct PlainHasher { + prefix: [u8; 8], + _marker: [u64; 0], // for alignment +} impl Default for PlainHasher { #[inline] fn default() -> PlainHasher { - PlainHasher(0) + PlainHasher { + prefix: [0; 8], + _marker: [0; 0], + } } } impl Hasher for PlainHasher { #[inline] fn finish(&self) -> u64 { - self.0 + unsafe { ::std::mem::transmute(self.prefix) } } #[inline] fn write(&mut self, bytes: &[u8]) { debug_assert!(bytes.len() == 32); - let mut prefix = [0u8; 8]; - prefix.clone_from_slice(&bytes[0..8]); - self.0 = unsafe { ::std::mem::transmute(prefix) }; + + for quarter in bytes.chunks(8) { + for (x, y) in self.prefix.iter_mut().zip(quarter) { + *x ^= *y + } + } } } @@ -578,6 +588,12 @@ mod tests { use bigint::uint::*; use std::str::FromStr; + #[test] + fn hasher_alignment() { + use std::mem::align_of; + assert_eq!(align_of::(), align_of::()); + } + #[test] #[cfg_attr(feature="dev", allow(eq_op))] fn hash() {