From 7ad8599324ac0dc3ba76cbea65c45a39066b0494 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 1 Jul 2016 13:26:44 +0300 Subject: [PATCH 01/21] uncle as rlp in the api --- ethcore/src/client/client.rs | 9 +++-- ethcore/src/client/mod.rs | 4 +- ethcore/src/client/test_client.rs | 4 +- ethcore/src/types/ids.rs | 10 ++--- rpc/src/v1/impls/eth.rs | 64 +++++++++++++++++-------------- 5 files changed, 49 insertions(+), 42 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index d5e509e62..e265fe4c0 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -22,7 +22,7 @@ use util::*; use util::panics::*; use views::BlockView; use error::{Error, ImportError, ExecutionError, BlockError, ImportResult}; -use header::{BlockNumber, Header}; +use header::{BlockNumber}; use state::State; use spec::Spec; use engine::Engine; @@ -50,6 +50,7 @@ pub use types::blockchain_info::BlockChainInfo; pub use types::block_status::BlockStatus; use evm::Factory as EvmFactory; use miner::{Miner, MinerService, TransactionImportResult, AccountDetails}; +use basic_types::*; const MAX_TX_QUEUE_SIZE: usize = 4096; @@ -579,9 +580,9 @@ impl BlockChainClient for Client { self.transaction_address(id).and_then(|address| self.chain.transaction(&address)) } - fn uncle(&self, id: UncleID) -> Option
{ - let index = id.1; - self.block(id.0).and_then(|block| BlockView::new(&block).uncle_at(index)) + fn uncle(&self, id: UncleID) -> Option { + let index = id.position; + self.block(id.block).and_then(|block| BlockView::new(&block).uncle_at(index).and_then(|u| Some(u.rlp(Seal::With)))) } fn transaction_receipt(&self, id: TransactionID) -> Option { diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 7f3c3bb3a..27a1d3265 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -38,7 +38,7 @@ use util::Itertools; use blockchain::TreeRoute; use block_queue::BlockQueueInfo; use block::{OpenBlock, SealedBlock}; -use header::{BlockNumber, Header}; +use header::{BlockNumber}; use transaction::{LocalizedTransaction, SignedTransaction}; use log_entry::LocalizedLogEntry; use filter::Filter; @@ -126,7 +126,7 @@ pub trait BlockChainClient : Sync + Send { fn transaction(&self, id: TransactionID) -> Option; /// Get uncle with given id. - fn uncle(&self, id: UncleID) -> Option
; + fn uncle(&self, id: UncleID) -> Option; /// Get transaction receipt with given hash. fn transaction_receipt(&self, id: TransactionID) -> Option; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index f51f978de..be90d9b67 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -244,7 +244,7 @@ impl MiningBlockChainClient for TestBlockChainClient { fn prepare_open_block(&self, _author: Address, _gas_range_target: (U256, U256), _extra_data: Bytes) -> OpenBlock { unimplemented!(); } - + fn vm_factory(&self) -> &EvmFactory { unimplemented!(); } @@ -298,7 +298,7 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } - fn uncle(&self, _id: UncleID) -> Option { + fn uncle(&self, _id: UncleID) -> Option { unimplemented!(); } diff --git a/ethcore/src/types/ids.rs b/ethcore/src/types/ids.rs index 0a492735c..99dadc4ea 100644 --- a/ethcore/src/types/ids.rs +++ b/ethcore/src/types/ids.rs @@ -55,10 +55,10 @@ pub struct TraceId { } /// Uniquely identifies Uncle. -#[derive(Debug)] -pub struct UncleID ( +#[derive(Debug, Binary)] +pub struct UncleID { /// Block id. - pub BlockID, + pub block: BlockID, /// Position in block. - pub usize -); + pub position: usize +} diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 8bfc661e3..f2325de93 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -41,6 +41,7 @@ use v1::traits::Eth; use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, CallRequest, OptionalValue, Index, Filter, Log, Receipt}; use v1::impls::{default_gas_price, dispatch_transaction, error_codes}; use serde; +use ethcore::header::Header as BlockHeader; /// Eth rpc implementation. pub struct EthClient where @@ -126,33 +127,38 @@ impl EthClient where fn uncle(&self, id: UncleID) -> Result { let client = take_weak!(self.client); - match client.uncle(id).and_then(|u| client.block_total_difficulty(BlockID::Hash(u.parent_hash().clone())).map(|diff| (diff, u))) { - Some((parent_difficulty, uncle)) => { - let block = Block { - hash: OptionalValue::Value(uncle.hash()), - parent_hash: uncle.parent_hash, - uncles_hash: uncle.uncles_hash, - author: uncle.author, - miner: uncle.author, - state_root: uncle.state_root, - transactions_root: uncle.transactions_root, - number: OptionalValue::Value(U256::from(uncle.number)), - gas_used: uncle.gas_used, - gas_limit: uncle.gas_limit, - logs_bloom: uncle.log_bloom, - timestamp: U256::from(uncle.timestamp), - difficulty: uncle.difficulty, - total_difficulty: uncle.difficulty + parent_difficulty, - receipts_root: uncle.receipts_root, - extra_data: Bytes::new(uncle.extra_data), - seal_fields: uncle.seal.into_iter().map(|f| decode(&f)).map(Bytes::new).collect(), - uncles: vec![], - transactions: BlockTransactions::Hashes(vec![]), - }; - to_value(&block) - }, - None => Ok(Value::Null) - } + + let uncle: BlockHeader = match client.uncle(id) { + Some(rlp) => decode(&rlp), + None => { return Ok(Value::Null); } + }; + let parent_difficulty = match client.block_total_difficulty(BlockID::Hash(uncle.parent_hash().clone())) { + Some(difficulty) => difficulty, + None => { return Ok(Value::Null); } + }; + + let block = Block { + hash: OptionalValue::Value(uncle.hash()), + parent_hash: uncle.parent_hash, + uncles_hash: uncle.uncles_hash, + author: uncle.author, + miner: uncle.author, + state_root: uncle.state_root, + transactions_root: uncle.transactions_root, + number: OptionalValue::Value(U256::from(uncle.number)), + gas_used: uncle.gas_used, + gas_limit: uncle.gas_limit, + logs_bloom: uncle.log_bloom, + timestamp: U256::from(uncle.timestamp), + difficulty: uncle.difficulty, + total_difficulty: uncle.difficulty + parent_difficulty, + receipts_root: uncle.receipts_root, + extra_data: Bytes::new(uncle.extra_data), + seal_fields: uncle.seal.into_iter().map(|f| decode(&f)).map(Bytes::new).collect(), + uncles: vec![], + transactions: BlockTransactions::Hashes(vec![]), + }; + to_value(&block) } fn sign_call(&self, request: CallRequest) -> Result { @@ -435,12 +441,12 @@ impl Eth for EthClient where fn uncle_by_block_hash_and_index(&self, params: Params) -> Result { from_params::<(H256, Index)>(params) - .and_then(|(hash, index)| self.uncle(UncleID(BlockID::Hash(hash), index.value()))) + .and_then(|(hash, index)| self.uncle(UncleID { block: BlockID::Hash(hash), position: index.value() })) } fn uncle_by_block_number_and_index(&self, params: Params) -> Result { from_params::<(BlockNumber, Index)>(params) - .and_then(|(number, index)| self.uncle(UncleID(number.into(), index.value()))) + .and_then(|(number, index)| self.uncle(UncleID { block: number.into(), position: index.value() })) } fn compilers(&self, params: Params) -> Result { From d4ff3e51e9302d742189c4d81e6984d8b1aff72a Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 1 Jul 2016 16:33:37 +0300 Subject: [PATCH 02/21] uncle rlp in block view --- ethcore/src/client/client.rs | 2 +- ethcore/src/views/block.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index e265fe4c0..87ad26647 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -582,7 +582,7 @@ impl BlockChainClient for Client { fn uncle(&self, id: UncleID) -> Option { let index = id.position; - self.block(id.block).and_then(|block| BlockView::new(&block).uncle_at(index).and_then(|u| Some(u.rlp(Seal::With)))) + self.block(id.block).and_then(|block| BlockView::new(&block).uncle_rlp_at(index)) } fn transaction_receipt(&self, id: TransactionID) -> Option { diff --git a/ethcore/src/views/block.rs b/ethcore/src/views/block.rs index 82b8fb805..42fd52a20 100644 --- a/ethcore/src/views/block.rs +++ b/ethcore/src/views/block.rs @@ -139,6 +139,11 @@ impl<'a> BlockView<'a> { pub fn uncle_at(&self, index: usize) -> Option
{ self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_val()) } + + /// Return nth uncle rlp. + pub fn uncle_rlp_at(&self, index: usize) -> Option { + self.rlp.at(2).iter().nth(index).map(|rlp| rlp.as_raw().to_vec()) + } } impl<'a> Hashable for BlockView<'a> { From 0907722fc87dc857226474bb9744a5d7280fc4df Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 1 Jul 2016 16:34:15 +0300 Subject: [PATCH 03/21] fix warning --- ethcore/src/client/client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 87ad26647..9bfe1758d 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -50,7 +50,6 @@ pub use types::blockchain_info::BlockChainInfo; pub use types::block_status::BlockStatus; use evm::Factory as EvmFactory; use miner::{Miner, MinerService, TransactionImportResult, AccountDetails}; -use basic_types::*; const MAX_TX_QUEUE_SIZE: usize = 4096; From 07521c17b4e9b876db9d3a110ce6cf5c5625240e Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 1 Jul 2016 21:33:59 +0300 Subject: [PATCH 04/21] dedicated types --- ethcore/src/client/mod.rs | 4 ++- ethcore/src/error.rs | 17 ++++++++- ethcore/src/types/block_import_error.rs | 44 +++++++++++++++++++++++ ethcore/src/types/mod.rs.in | 2 ++ ethcore/src/types/transaction_import.rs | 46 +++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 ethcore/src/types/block_import_error.rs create mode 100644 ethcore/src/types/transaction_import.rs diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 27a1d3265..e54e8e382 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -49,6 +49,8 @@ use trace::LocalizedTrace; use evm::Factory as EvmFactory; use miner::{TransactionImportResult}; use error::Error as EthError; +pub use block_import_error::BlockImportError; +pub use transaction_import::{TransactionImportResult, TransactionImportError}; /// Options concerning what analytics we run on the call. #[derive(Eq, PartialEq, Default, Clone, Copy, Debug)] @@ -188,7 +190,7 @@ pub trait BlockChainClient : Sync + Send { fn last_hashes(&self) -> LastHashes; /// import transactions from network/other 3rd party - fn import_transactions(&self, transactions: Vec) -> Vec>; + fn import_transactions(&self, transactions: Vec) -> Vec>; /// Queue transactions for importing. fn queue_transactions(&self, transactions: Vec); diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 92d3cbe6b..7460762c2 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -250,7 +250,7 @@ impl fmt::Display for Error { } /// Result of import block operation. -pub type ImportResult = Result; +pub type ImportResult = Result; impl From for Error { fn from(err: ClientError) -> Error { @@ -312,6 +312,21 @@ impl From for Error { } } +impl From for Error { + fn from(err: BlockImportError) -> Error { + match err { + BlockImportError::Block(e) => Error::Block(e), + BlockImportError::Import(e) => Error::Import(e), + BlockImportError::Other(s) => Error::Util(UtilError::SimpleString(s)), + } + } +} + +binary_fixed_size!(BlockError); +binary_fixed_size!(ImportError); +binary_fixed_size!(TransactionError); + + // TODO: uncomment below once https://github.com/rust-lang/rust/issues/27336 sorted. /*#![feature(concat_idents)] macro_rules! assimilate { diff --git a/ethcore/src/types/block_import_error.rs b/ethcore/src/types/block_import_error.rs new file mode 100644 index 000000000..8b400613f --- /dev/null +++ b/ethcore/src/types/block_import_error.rs @@ -0,0 +1,44 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Block import error related types + +use std::mem; +use ipc::binary::BinaryConvertError; +use std::collections::VecDeque; +use error::{ImportError, BlockError, Error}; +use std::convert::From; + +/// Error dedicated to import block function +#[derive(Binary, Debug)] +pub enum BlockImportError { + /// Import error + Import(ImportError), + /// Block error + Block(BlockError), + /// Other error + Other(String), +} + +impl From for BlockImportError { + fn from(e: Error) -> Self { + match e { + Error::Block(block_error) => BlockImportError::Block(block_error), + Error::Import(import_error) => BlockImportError::Import(import_error), + _ => BlockImportError::Other(format!("other block import error: {:?}", e)), + } + } +} diff --git a/ethcore/src/types/mod.rs.in b/ethcore/src/types/mod.rs.in index b51e9e57b..97579da8a 100644 --- a/ethcore/src/types/mod.rs.in +++ b/ethcore/src/types/mod.rs.in @@ -25,3 +25,5 @@ pub mod executed; pub mod block_status; pub mod account_diff; pub mod state_diff; +pub mod transaction_import; +pub mod block_import_error; diff --git a/ethcore/src/types/transaction_import.rs b/ethcore/src/types/transaction_import.rs new file mode 100644 index 000000000..78078cfea --- /dev/null +++ b/ethcore/src/types/transaction_import.rs @@ -0,0 +1,46 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Transaction import result related types + +use ipc::binary::BinaryConvertError; +use std::collections::VecDeque; + +#[derive(Debug, Clone, PartialEq)] +/// Represents the result of importing transaction. +pub enum TransactionImportResult { + /// Transaction was imported to current queue. + Current, + /// Transaction was imported to future queue. + Future +} + +binary_fixed_size!(TransactionImportResult); + +#[derive(Debug, Clone, Binary)] +pub enum TransactionImportError { + Transaction(TransactionError), + Other(String), +} + +impl From for BlockImportError { + fn from(e: Error) -> Self { + match e { + Error::Transaction(transaction_error) => TransactionImportError::Transaction(block_error), + _ => TransactionImportError::Other(format!("other block import error: {:?}", e)), + } + } +} From 46b0af6121f017cef079811f3e168cc078f5c543 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 1 Jul 2016 22:13:56 +0300 Subject: [PATCH 05/21] flush work --- ethcore/src/client/client.rs | 16 +++++++++------- ethcore/src/client/mod.rs | 3 +-- ethcore/src/client/test_client.rs | 8 +++++--- ethcore/src/error.rs | 6 ++++-- ethcore/src/miner/miner.rs | 4 +++- ethcore/src/miner/mod.rs | 3 ++- ethcore/src/miner/transaction_queue.rs | 10 +--------- ethcore/src/types/transaction_import.rs | 8 +++++--- 8 files changed, 30 insertions(+), 28 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 9bfe1758d..655c66fde 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -38,7 +38,9 @@ use filter::Filter; use log_entry::LocalizedLogEntry; use block_queue::{BlockQueue, BlockQueueInfo}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; -use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics}; +use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile, + BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, TransactionImportError, + BlockImportError, TransactionImportResult}; use client::Error as ClientError; use env_info::EnvInfo; use executive::{Executive, Executed, TransactOptions, contract_address}; @@ -49,7 +51,7 @@ use trace; pub use types::blockchain_info::BlockChainInfo; pub use types::block_status::BlockStatus; use evm::Factory as EvmFactory; -use miner::{Miner, MinerService, TransactionImportResult, AccountDetails}; +use miner::{Miner, MinerService, AccountDetails}; const MAX_TX_QUEUE_SIZE: usize = 4096; @@ -648,17 +650,17 @@ impl BlockChainClient for Client { self.chain.block_receipts(hash).map(|receipts| rlp::encode(&receipts).to_vec()) } - fn import_block(&self, bytes: Bytes) -> ImportResult { + fn import_block(&self, bytes: Bytes) -> Result { { let header = BlockView::new(&bytes).header_view(); if self.chain.is_known(&header.sha3()) { - return Err(ImportError::AlreadyInChain.into()); + return Err(BlockImportError::Import(ImportError::AlreadyInChain)); } if self.block_status(BlockID::Hash(header.parent_hash())) == BlockStatus::Unknown { - return Err(BlockError::UnknownParent(header.parent_hash()).into()); + return Err(BlockImportError::Block(BlockError::UnknownParent(header.parent_hash()))); } } - self.block_queue.import_block(bytes) + Ok(try!(self.block_queue.import_block(bytes))) } fn queue_info(&self) -> BlockQueueInfo { @@ -772,7 +774,7 @@ impl BlockChainClient for Client { self.build_last_hashes(self.chain.best_block_hash()) } - fn import_transactions(&self, transactions: Vec) -> Vec> { + fn import_transactions(&self, transactions: Vec) -> Vec> { let fetch_account = |a: &Address| AccountDetails { nonce: self.latest_nonce(a), balance: self.latest_balance(a), diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index e54e8e382..54d38b034 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -47,7 +47,6 @@ use error::{ImportResult, ExecutionError}; use receipt::LocalizedReceipt; use trace::LocalizedTrace; use evm::Factory as EvmFactory; -use miner::{TransactionImportResult}; use error::Error as EthError; pub use block_import_error::BlockImportError; pub use transaction_import::{TransactionImportResult, TransactionImportError}; @@ -147,7 +146,7 @@ pub trait BlockChainClient : Sync + Send { fn block_receipts(&self, hash: &H256) -> Option; /// Import a block into the blockchain. - fn import_block(&self, bytes: Bytes) -> ImportResult; + fn import_block(&self, bytes: Bytes) -> Result; /// Get block queue information. fn queue_info(&self) -> BlockQueueInfo; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index be90d9b67..0ce308a30 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -20,7 +20,9 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder}; use util::*; use transaction::{Transaction, LocalizedTransaction, SignedTransaction, Action}; use blockchain::TreeRoute; -use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics}; +use client::{BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockID, + TransactionID, UncleID, TraceId, TraceFilter, LastHashes, CallAnalytics, + TransactionImportError, BlockImportError}; use header::{Header as BlockHeader, BlockNumber}; use filter::Filter; use log_entry::LocalizedLogEntry; @@ -402,7 +404,7 @@ impl BlockChainClient for TestBlockChainClient { None } - fn import_block(&self, b: Bytes) -> ImportResult { + fn import_block(&self, b: Bytes) -> Result { let header = Rlp::new(&b).val_at::(0); let h = header.hash(); let number: usize = header.number as usize; @@ -487,7 +489,7 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } - fn import_transactions(&self, transactions: Vec) -> Vec> { + fn import_transactions(&self, transactions: Vec) -> Vec> { let nonces = self.nonces.read().unwrap(); let balances = self.balances.read().unwrap(); let fetch_account = |a: &Address| AccountDetails { diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index 7460762c2..cfe6b3679 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -20,8 +20,10 @@ use util::*; use header::BlockNumber; use basic_types::LogBloom; use client::Error as ClientError; - pub use types::executed::ExecutionError; +use ipc::binary::{BinaryConvertError, BinaryConvertable}; +use types::transaction_import::TransactionImportError; +use types::block_import_error::BlockImportError; #[derive(Debug, PartialEq)] /// Errors concerning transaction processing. @@ -250,7 +252,7 @@ impl fmt::Display for Error { } /// Result of import block operation. -pub type ImportResult = Result; +pub type ImportResult = Result; impl From for Error { fn from(err: ClientError) -> Error { diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index d3e32c797..fe1877116 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -29,8 +29,10 @@ use transaction::SignedTransaction; use receipt::{Receipt}; use spec::Spec; use engine::Engine; -use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; +use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, TransactionOrigin}; use miner::work_notify::WorkPoster; +use client::TransactionImportResult; + /// Different possible definitions for pending transaction set. #[derive(Debug)] diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index 152bd1a61..3d0185753 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -47,9 +47,10 @@ mod external; mod transaction_queue; mod work_notify; -pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionImportResult, TransactionOrigin}; +pub use self::transaction_queue::{TransactionQueue, AccountDetails, TransactionOrigin}; pub use self::miner::{Miner, MinerOptions, PendingSet}; pub use self::external::{ExternalMiner, ExternalMinerService}; +pub use client::TransactionImportResult; use std::collections::BTreeMap; use util::{H256, U256, Address, Bytes}; diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 7f5b59c38..fde8b67d8 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -90,6 +90,7 @@ use util::hash::{Address, H256}; use util::table::*; use transaction::*; use error::{Error, TransactionError}; +use client::TransactionImportResult; /// Transaction origin #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -309,15 +310,6 @@ pub struct TransactionQueueStatus { pub future: usize, } -#[derive(Debug, PartialEq)] -/// Represents the result of importing transaction. -pub enum TransactionImportResult { - /// Transaction was imported to current queue. - Current, - /// Transaction was imported to future queue. - Future -} - /// Details of account pub struct AccountDetails { /// Most recent account nonce diff --git a/ethcore/src/types/transaction_import.rs b/ethcore/src/types/transaction_import.rs index 78078cfea..fa7455cfd 100644 --- a/ethcore/src/types/transaction_import.rs +++ b/ethcore/src/types/transaction_import.rs @@ -16,8 +16,10 @@ //! Transaction import result related types -use ipc::binary::BinaryConvertError; +use ipc::binary::{BinaryConvertError, BinaryConvertable}; use std::collections::VecDeque; +use error::{TransactionError, Error}; +use std::mem; #[derive(Debug, Clone, PartialEq)] /// Represents the result of importing transaction. @@ -36,10 +38,10 @@ pub enum TransactionImportError { Other(String), } -impl From for BlockImportError { +impl From for TransactionImportError { fn from(e: Error) -> Self { match e { - Error::Transaction(transaction_error) => TransactionImportError::Transaction(block_error), + Error::Transaction(transaction_error) => TransactionImportError::Transaction(transaction_error), _ => TransactionImportError::Other(format!("other block import error: {:?}", e)), } } From edb86631954a18a810ca4ddeee5ee415cb9aab72 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 1 Jul 2016 22:34:50 +0300 Subject: [PATCH 06/21] ethcore finished --- ethcore/src/client/client.rs | 8 ++++++-- ethcore/src/client/mod.rs | 1 - ethcore/src/client/test_client.rs | 4 +++- ethcore/src/error.rs | 3 +-- ethcore/src/types/transaction_import.rs | 4 ++++ util/src/error.rs | 2 +- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 655c66fde..522f1c457 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -21,7 +21,7 @@ use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use util::*; use util::panics::*; use views::BlockView; -use error::{Error, ImportError, ExecutionError, BlockError, ImportResult}; +use error::{ImportError, ExecutionError, BlockError, ImportResult}; use header::{BlockNumber}; use state::State; use spec::Spec; @@ -779,7 +779,11 @@ impl BlockChainClient for Client { nonce: self.latest_nonce(a), balance: self.latest_balance(a), }; - self.miner.import_transactions(self, transactions, fetch_account) + + self.miner.import_transactions(self, transactions, &fetch_account) + .into_iter() + .map(|res| res.map_err(|e| e.into())) + .collect() } fn queue_transactions(&self, transactions: Vec) { diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index 54d38b034..c09da450d 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -47,7 +47,6 @@ use error::{ImportResult, ExecutionError}; use receipt::LocalizedReceipt; use trace::LocalizedTrace; use evm::Factory as EvmFactory; -use error::Error as EthError; pub use block_import_error::BlockImportError; pub use transaction_import::{TransactionImportResult, TransactionImportError}; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 0ce308a30..1e51719c0 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -40,7 +40,6 @@ use error::{ExecutionError}; use trace::LocalizedTrace; use miner::{TransactionImportResult, AccountDetails}; -use error::Error as EthError; /// Test client. pub struct TestBlockChainClient { @@ -498,6 +497,9 @@ impl BlockChainClient for TestBlockChainClient { }; self.miner.import_transactions(self, transactions, &fetch_account) + .into_iter() + .map(|res| res.map_err(|e| e.into())) + .collect() } fn queue_transactions(&self, transactions: Vec) { diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs index cfe6b3679..8c37e98ef 100644 --- a/ethcore/src/error.rs +++ b/ethcore/src/error.rs @@ -22,10 +22,9 @@ use basic_types::LogBloom; use client::Error as ClientError; pub use types::executed::ExecutionError; use ipc::binary::{BinaryConvertError, BinaryConvertable}; -use types::transaction_import::TransactionImportError; use types::block_import_error::BlockImportError; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] /// Errors concerning transaction processing. pub enum TransactionError { /// Transaction is already imported to the queue diff --git a/ethcore/src/types/transaction_import.rs b/ethcore/src/types/transaction_import.rs index fa7455cfd..c52a9fb80 100644 --- a/ethcore/src/types/transaction_import.rs +++ b/ethcore/src/types/transaction_import.rs @@ -20,6 +20,7 @@ use ipc::binary::{BinaryConvertError, BinaryConvertable}; use std::collections::VecDeque; use error::{TransactionError, Error}; use std::mem; +use util::Populatable; #[derive(Debug, Clone, PartialEq)] /// Represents the result of importing transaction. @@ -32,9 +33,12 @@ pub enum TransactionImportResult { binary_fixed_size!(TransactionImportResult); +/// Api-level error for transaction import #[derive(Debug, Clone, Binary)] pub enum TransactionImportError { + /// Transaction error Transaction(TransactionError), + /// Other error Other(String), } diff --git a/util/src/error.rs b/util/src/error.rs index 5e9f8e7a8..c5f40d717 100644 --- a/util/src/error.rs +++ b/util/src/error.rs @@ -101,7 +101,7 @@ impl fmt::Display for Mismatch { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] /// Error indicating value found is outside of a valid range. pub struct OutOfBounds { /// Minimum allowed value. From 0f7b66f5573a2fbd99fb1ad33470a687ca5f0326 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 1 Jul 2016 22:40:54 +0300 Subject: [PATCH 07/21] fixed compilation & warnings --- parity/main.rs | 6 +++--- sync/src/chain.rs | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/parity/main.rs b/parity/main.rs index d466987ef..8d46d28e5 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -82,8 +82,8 @@ use rustc_serialize::hex::FromHex; use ctrlc::CtrlC; use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError, paint, Colour, version}; use util::panics::{MayPanic, ForwardPanic, PanicHandler}; -use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path}; -use ethcore::error::{Error, ImportError}; +use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path, BlockImportError}; +use ethcore::error::{ImportError}; use ethcore::service::ClientService; use ethcore::spec::Spec; use ethsync::EthSync; @@ -465,7 +465,7 @@ fn execute_import(conf: Configuration) { while client.queue_info().is_full() { sleep(Duration::from_secs(1)); } match client.import_block(bytes) { Ok(_) => {} - Err(Error::Import(ImportError::AlreadyInChain)) => { trace!("Skipping block already in chain."); } + Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!("Skipping block already in chain."); } Err(e) => die!("Cannot import block: {:?}", e) } informant.tick(client.deref(), None); diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 1897b02f4..6845c32e3 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -93,7 +93,7 @@ use util::*; use std::mem::{replace}; use ethcore::views::{HeaderView, BlockView}; use ethcore::header::{BlockNumber, Header as BlockHeader}; -use ethcore::client::{BlockChainClient, BlockStatus, BlockID, BlockChainInfo}; +use ethcore::client::{BlockChainClient, BlockStatus, BlockID, BlockChainInfo, BlockImportError}; use ethcore::error::*; use ethcore::block::Block; use io::SyncIo; @@ -544,10 +544,10 @@ impl ChainSync { peer.latest_number = Some(header.number()); } match io.chain().import_block(block_rlp.as_raw().to_vec()) { - Err(Error::Import(ImportError::AlreadyInChain)) => { + Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!(target: "sync", "New block already in chain {:?}", h); }, - Err(Error::Import(ImportError::AlreadyQueued)) => { + Err(BlockImportError::Import(ImportError::AlreadyQueued)) => { trace!(target: "sync", "New block already queued {:?}", h); }, Ok(_) => { @@ -557,7 +557,7 @@ impl ChainSync { } trace!(target: "sync", "New block queued {:?} ({})", h, header.number); }, - Err(Error::Block(BlockError::UnknownParent(p))) => { + Err(BlockImportError::Block(BlockError::UnknownParent(p))) => { unknown = true; trace!(target: "sync", "New block with unknown parent ({:?}) {:?}", p, h); }, @@ -841,11 +841,11 @@ impl ChainSync { } match io.chain().import_block(block) { - Err(Error::Import(ImportError::AlreadyInChain)) => { + Err(BlockImportError::Import(ImportError::AlreadyInChain)) => { trace!(target: "sync", "Block already in chain {:?}", h); self.block_imported(&h, number, &parent); }, - Err(Error::Import(ImportError::AlreadyQueued)) => { + Err(BlockImportError::Import(ImportError::AlreadyQueued)) => { trace!(target: "sync", "Block already queued {:?}", h); self.block_imported(&h, number, &parent); }, @@ -854,7 +854,7 @@ impl ChainSync { imported.insert(h.clone()); self.block_imported(&h, number, &parent); }, - Err(Error::Block(BlockError::UnknownParent(_))) if self.state == SyncState::NewBlocks => { + Err(BlockImportError::Block(BlockError::UnknownParent(_))) if self.state == SyncState::NewBlocks => { trace!(target: "sync", "Unknown new block parent, restarting sync"); break; }, From 8f6e48e4c3f1cf3e077dcebcab7eff06e1a90341 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Sat, 2 Jul 2016 20:08:52 +0300 Subject: [PATCH 08/21] fix tests --- ethcore/src/miner/transaction_queue.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index fde8b67d8..0e71b1d83 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -804,6 +804,7 @@ mod test { use error::{Error, TransactionError}; use super::*; use super::{TransactionSet, TransactionOrder, VerifiedTransaction}; + use client::TransactionImportResult; fn unwrap_tx_err(err: Result) -> TransactionError { match err.unwrap_err() { From 39a46eee8fefa22b7e6062b13d97e51cedb179ef Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Mon, 4 Jul 2016 16:57:57 +0200 Subject: [PATCH 09/21] add owning NibbleVec --- util/src/lib.rs | 1 + util/src/nibblevec.rs | 130 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 util/src/nibblevec.rs diff --git a/util/src/lib.rs b/util/src/lib.rs index 31e072dc7..009b50782 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -145,6 +145,7 @@ pub mod crypto; pub mod triehash; pub mod trie; pub mod nibbleslice; +pub mod nibblevec; mod heapsizeof; pub mod squeeze; pub mod semantic_version; diff --git a/util/src/nibblevec.rs b/util/src/nibblevec.rs new file mode 100644 index 000000000..dac022929 --- /dev/null +++ b/util/src/nibblevec.rs @@ -0,0 +1,130 @@ +//! An owning, nibble-oriented byte vector. + +use ::NibbleSlice; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] +/// Owning, nibble-oriented byte vector. Counterpart to NibbleSlice. +pub struct NibbleVec { + inner: Vec, + len: usize, +} + +impl NibbleVec { + /// Make a new NibbleVec + pub fn new() -> Self { + NibbleVec { + inner: Vec::new(), + len: 0 + } + } + + /// Make a NibbleVec with capacity for `n` nibbles. + pub fn with_capacity(n: usize) -> Self { + NibbleVec { + inner: Vec::with_capacity((n / 2) + (n % 2)), + len: 0 + } + } + + /// Length of the NibbleVec + pub fn len(&self) -> usize { self.len } + + /// Capacity of the NibbleVec. + pub fn capacity(&self) -> usize { self.inner.capacity() * 2 } + + /// Try to get the nibble at the given offset. + pub fn at(&self, idx: usize) -> u8 { + if idx % 2 == 0 { + self.inner[idx / 2] >> 4 + } else { + self.inner[idx / 2] & 0x0F + } + } + + /// Push a nibble onto the NibbleVec. Ignores the high 4 bits. + pub fn push(&mut self, nibble: u8) { + let nibble = nibble & 0x0F; + + if self.len % 2 == 0 { + self.inner.push(nibble << 4); + } else { + *self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; + } + + self.len += 1; + } + + /// Try to pop a nibble off the NibbleVec. Fails if len == 0. + pub fn pop(&mut self) -> Option { + if self.len == 0 { + return None; + } + + let byte = self.inner.pop().expect("len != 0; inner has last elem; qed"); + let nibble = if self.len % 2 == 0 { + self.inner.push(byte & 0xF0); + byte & 0x0F + } else { + byte >> 4 + }; + + self.len -= 1; + Some(nibble) + } + + /// Try to treat this NibbleVec as a NibbleSlice. Works only if len is even. + pub fn as_nibbleslice(&self) -> Option { + if self.len % 2 == 0 { + Some(NibbleSlice::new(self.inner())) + } else { + None + } + } + + /// Get the underlying byte slice. + pub fn inner(&self) -> &[u8] { + &self.inner[..] + } +} + +impl<'a> From> for NibbleVec { + fn from(s: NibbleSlice<'a>) -> Self { + let mut v = NibbleVec::with_capacity(s.len()); + for i in 0..s.len() { + v.push(s.at(i)); + } + v + } +} + +#[cfg(test)] +mod tests { + use super::NibbleVec; + + #[test] + fn push_pop() { + let mut v = NibbleVec::new(); + + for i in 0..16 { + v.push(i); + assert_eq!(v.len() - 1, i as usize); + assert_eq!(v.at(i as usize), i); + } + + for i in (0..16).rev() { + assert_eq!(v.pop(), Some(i)); + assert_eq!(v.len(), i as usize); + } + } + + #[test] + fn nibbleslice_conv() { + let mut v = NibbleVec::new(); + for i in 0..10 { + v.push(i); + } + + let v2: NibbleVec = v.as_nibbleslice().unwrap().into(); + assert_eq!(v, v2); + } +} \ No newline at end of file From 24f09de60e910927f743584d5e08dfa1eff9fa96 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 4 Jul 2016 18:21:22 +0200 Subject: [PATCH 10/21] Fixed public address config --- parity/configuration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parity/configuration.rs b/parity/configuration.rs index 78c4b286a..14932423c 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -254,7 +254,7 @@ impl Configuration { let host = IpAddr::from_str(host).unwrap_or_else(|_| die!("Invalid host given with `--nat extip:{}`", host)); Some(SocketAddr::new(host, port)) } else { - listen_address + None }; (listen_address, public_address) } From 548b4e164eea2b1a71a23ba8f18691cd73cbf03c Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 4 Jul 2016 18:24:14 +0200 Subject: [PATCH 11/21] Attempt to fix blochchain DB sync --- ethcore/src/blockchain/blockchain.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 4b2246b21..252c17b34 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -30,7 +30,7 @@ use blockchain::best_block::BestBlock; use types::tree_route::TreeRoute; use blockchain::update::ExtrasUpdate; use blockchain::{CacheSize, ImportRoute, Config}; -use db::{Writable, Readable, CacheUpdatePolicy}; +use db::{Writable, Readable, CacheUpdatePolicy, Key}; const LOG_BLOOMS_LEVELS: usize = 3; const LOG_BLOOMS_ELEMENTS_PER_INDEX: usize = 16; @@ -295,7 +295,22 @@ impl BlockChain { // load best block let best_block_hash = match bc.extras_db.get(b"best").unwrap() { - Some(best) => H256::from_slice(&best), + Some(best) => { + let best = H256::from_slice(&best); + let mut b = best.clone(); + while !bc.blocks_db.get(&b).unwrap().is_some() { + // track back to the best block we have in the blocks database + let extras: BlockDetails = bc.extras_db.read(&b).unwrap(); + type DetailsKey = Key; + bc.extras_db.delete(&(DetailsKey::key(&b))).unwrap(); + b = extras.parent; + } + if b != best { + info!("Restored mismatched best block. Was: {}, new: {}", best.hex(), b.hex()); + bc.extras_db.put(b"best", &b).unwrap(); + } + b + } None => { // best block does not exist // we need to insert genesis into the cache From 0242c66a1b53b269bd52cb5083b2d7e262c75076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 4 Jul 2016 19:21:40 +0200 Subject: [PATCH 12/21] Bump parity-dapps --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8fdbfa351..5071da96b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -898,7 +898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "parity-dapps" version = "0.3.0" -source = "git+https://github.com/ethcore/parity-dapps-rs.git#8cc812c26c903cf5764ce0f4cc3f2a7c3ddb0dc2" +source = "git+https://github.com/ethcore/parity-dapps-rs.git#8ce18c014d8b69fa31fb203b68ff240091d77a23" dependencies = [ "aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", From 1ab2060afac3ed8a3ff00502a5a7ca5fe057a406 Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Tue, 5 Jul 2016 12:11:09 +0400 Subject: [PATCH 13/21] add void method support (#1540) --- ipc/codegen/src/codegen.rs | 44 +++++++++++++++++++++++++++----------- ipc/tests/examples.rs | 16 ++++++++++++++ ipc/tests/service.rs.in | 5 +++++ 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/ipc/codegen/src/codegen.rs b/ipc/codegen/src/codegen.rs index 672132c9f..c07f0b37a 100644 --- a/ipc/codegen/src/codegen.rs +++ b/ipc/codegen/src/codegen.rs @@ -201,15 +201,20 @@ fn implement_dispatch_arm_invoke_stmt( { let _sp = ext_cx.call_site(); let mut tt = ::std::vec::Vec::new(); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Brace))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("ipc")))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("binary")))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize")))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And))); + + if dispatch.return_type_ty.is_some() { + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("ipc")))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("binary")))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("serialize")))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::BinOp(::syntax::parse::token::And))); + } + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("self")))); tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot)); tt.extend(::quasi::ToTokens::to_tokens(&function_name, ext_cx).into_iter()); @@ -221,12 +226,25 @@ fn implement_dispatch_arm_invoke_stmt( } tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot)); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap")))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); - tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); + + if dispatch.return_type_ty.is_some() { + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Dot)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("unwrap")))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); + } + else { + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Semi)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("Vec")))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::ModSep)); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::Ident(ext_cx.ident_of("new")))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::OpenDelim(::syntax::parse::token::Paren))); + tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Paren))); + + } tt.push(::syntax::ast::TokenTree::Token(_sp, ::syntax::parse::token::CloseDelim(::syntax::parse::token::Brace))); + tt })).unwrap() } diff --git a/ipc/tests/examples.rs b/ipc/tests/examples.rs index b4159f196..8b52be11c 100644 --- a/ipc/tests/examples.rs +++ b/ipc/tests/examples.rs @@ -190,4 +190,20 @@ mod tests { assert_eq!(struct_, new_struct); } + + #[test] + fn can_call_void_method() { + let mut socket = TestSocket::new(); + socket.read_buffer = vec![1]; + let service_client = ServiceClient::init(socket); + + service_client.void(99); + + assert_eq!(vec![ + 0, 19, + 0, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, + 99, 0, 0, 0, 0, 0, 0, 0], + service_client.socket().write().unwrap().write_buffer.clone()); + } } diff --git a/ipc/tests/service.rs.in b/ipc/tests/service.rs.in index 2c58122bf..6bee63133 100644 --- a/ipc/tests/service.rs.in +++ b/ipc/tests/service.rs.in @@ -39,12 +39,14 @@ impl Service { *lock = *lock + f as usize; f } + pub fn rollback(&self, a: Option, b: u32) -> i32 { let a_0 = a.unwrap_or_else(|| 0); let mut lock = self.rollbacks.write().unwrap(); *lock = *lock + a_0 as usize - b as usize; (a_0 - b) as i32 } + pub fn push_custom(&self, data: CustomData) -> bool { let mut clock = self.commits.write().unwrap(); let mut rlock = self.commits.write().unwrap(); @@ -54,6 +56,9 @@ impl Service { true } + + pub fn void(&self, a: u64) { + } } impl Service { From 4c1b74a42e513c8d8003a8099f62ced51d6db729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 5 Jul 2016 09:15:44 -0400 Subject: [PATCH 14/21] Calculating gas using usize (if supplied gaslimit fits in usize) (#1518) * Spliting gasometer out of interpreter * Choosing right gas calculations implementation based on supplied gas * Moving verification out of gasometer * MemGasCost benchmark. Conflicts: ethcore/src/evm/benches/mod.rs * Some simple benchmarks * Benchmark for simple loop * Calculating gas_for_memory only when it's actually needed * Removing superfluous newline [ci skip] --- ethcore/Cargo.toml | 1 + ethcore/src/evm/benches/mod.rs | 126 ++++ ethcore/src/evm/evm.rs | 55 ++ ethcore/src/evm/factory.rs | 25 +- ethcore/src/evm/instructions.rs | 2 +- ethcore/src/evm/interpreter/gasometer.rs | 261 ++++++++ ethcore/src/evm/interpreter/memory.rs | 150 +++++ .../{interpreter.rs => interpreter/mod.rs} | 590 +++--------------- ethcore/src/evm/interpreter/stack.rs | 106 ++++ ethcore/src/evm/mod.rs | 4 +- ethcore/src/evm/tests.rs | 80 +-- ethcore/src/executive.rs | 4 +- ethcore/src/json_tests/executive.rs | 2 +- ethcore/src/lib.rs | 1 + 14 files changed, 839 insertions(+), 568 deletions(-) create mode 100644 ethcore/src/evm/benches/mod.rs create mode 100644 ethcore/src/evm/interpreter/gasometer.rs create mode 100644 ethcore/src/evm/interpreter/memory.rs rename ethcore/src/evm/{interpreter.rs => interpreter/mod.rs} (57%) create mode 100644 ethcore/src/evm/interpreter/stack.rs diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 0ea0023d6..acdcd667a 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -43,3 +43,4 @@ json-tests = [] test-heavy = [] dev = ["clippy"] default = [] +benches = [] diff --git a/ethcore/src/evm/benches/mod.rs b/ethcore/src/evm/benches/mod.rs new file mode 100644 index 000000000..8ef730d88 --- /dev/null +++ b/ethcore/src/evm/benches/mod.rs @@ -0,0 +1,126 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! benchmarking for EVM +//! should be started with: +//! ```bash +//! multirust run nightly cargo bench +//! ``` + +extern crate test; + +use self::test::{Bencher, black_box}; + +use common::*; +use evm::{self, Factory, VMType}; +use evm::tests::FakeExt; + +#[bench] +fn simple_loop_log0_usize(b: &mut Bencher) { + simple_loop_log0(U256::from(::std::usize::MAX), b) +} + +#[bench] +fn simple_loop_log0_u256(b: &mut Bencher) { + simple_loop_log0(!U256::zero(), b) +} + +fn simple_loop_log0(gas: U256, b: &mut Bencher) { + let mut vm = Factory::new(VMType::Interpreter).create(gas); + let mut ext = FakeExt::new(); + + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let code = black_box( + "62ffffff5b600190036000600fa0600357".from_hex().unwrap() + ); + + b.iter(|| { + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = gas; + params.code = Some(code.clone()); + + result(vm.exec(params, &mut ext)) + }); +} + +#[bench] +fn mem_gas_calculation_same_usize(b: &mut Bencher) { + mem_gas_calculation_same(U256::from(::std::usize::MAX), b) +} + +#[bench] +fn mem_gas_calculation_same_u256(b: &mut Bencher) { + mem_gas_calculation_same(!U256::zero(), b) +} + +fn mem_gas_calculation_same(gas: U256, b: &mut Bencher) { + let mut vm = Factory::new(VMType::Interpreter).create(gas); + let mut ext = FakeExt::new(); + + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + + b.iter(|| { + let code = black_box( + "6110006001556001546000555b610fff805560016000540380600055600c57".from_hex().unwrap() + ); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = gas; + params.code = Some(code.clone()); + + result(vm.exec(params, &mut ext)) + }); +} + +#[bench] +fn mem_gas_calculation_increasing_usize(b: &mut Bencher) { + mem_gas_calculation_increasing(U256::from(::std::usize::MAX), b) +} + +#[bench] +fn mem_gas_calculation_increasing_u256(b: &mut Bencher) { + mem_gas_calculation_increasing(!U256::zero(), b) +} + +fn mem_gas_calculation_increasing(gas: U256, b: &mut Bencher) { + let mut vm = Factory::new(VMType::Interpreter).create(gas); + let mut ext = FakeExt::new(); + + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + + b.iter(|| { + let code = black_box( + "6110006001556001546000555b610fff60005401805560016000540380600055600c57".from_hex().unwrap() + ); + + let mut params = ActionParams::default(); + params.address = address.clone(); + params.gas = gas; + params.code = Some(code.clone()); + + result(vm.exec(params, &mut ext)) + }); +} + +fn result(r: evm::Result) -> U256 { + match r { + Ok(evm::GasLeft::Known(v)) => v, + Ok(evm::GasLeft::NeedsReturn(v, _)) => v, + _ => U256::zero(), + } +} diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs index 740774f38..43919034e 100644 --- a/ethcore/src/evm/evm.rs +++ b/ethcore/src/evm/evm.rs @@ -95,6 +95,61 @@ impl<'a> Finalize for Result> { } } +pub trait CostType: ops::Mul + ops::Div + ops::Add + ops::Sub + ops::Shr + ops::Shl + cmp::Ord + Sized + From + Copy { + fn as_u256(&self) -> U256; + fn from_u256(val: U256) -> Result; + fn as_usize(&self) -> usize; + fn overflow_add(self, other: Self) -> (Self, bool); + fn overflow_mul(self, other: Self) -> (Self, bool); +} + +impl CostType for U256 { + fn as_u256(&self) -> U256 { + *self + } + + fn from_u256(val: U256) -> Result { + Ok(val) + } + + fn as_usize(&self) -> usize { + self.as_u64() as usize + } + + fn overflow_add(self, other: Self) -> (Self, bool) { + Uint::overflowing_add(self, other) + } + + fn overflow_mul(self, other: Self) -> (Self, bool) { + Uint::overflowing_mul(self, other) + } +} + +impl CostType for usize { + fn as_u256(&self) -> U256 { + U256::from(*self) + } + + fn from_u256(val: U256) -> Result { + if U256::from(val.low_u64()) != val { + return Err(Error::OutOfGas); + } + Ok(val.low_u64() as usize) + } + + fn as_usize(&self) -> usize { + *self + } + + fn overflow_add(self, other: Self) -> (Self, bool) { + self.overflowing_add(other) + } + + fn overflow_mul(self, other: Self) -> (Self, bool) { + self.overflowing_mul(other) + } +} + /// Evm interface pub trait Evm { /// This function should be used to execute transaction. diff --git a/ethcore/src/evm/factory.rs b/ethcore/src/evm/factory.rs index f55e064c7..082b9d050 100644 --- a/ethcore/src/evm/factory.rs +++ b/ethcore/src/evm/factory.rs @@ -19,6 +19,7 @@ //! TODO: consider spliting it into two separate files. use std::fmt; use evm::Evm; +use util::{U256, Uint}; #[derive(Debug, Clone)] /// Type of EVM to use. @@ -85,24 +86,30 @@ pub struct Factory { impl Factory { /// Create fresh instance of VM + /// Might choose implementation depending on supplied gas. #[cfg(feature = "jit")] - pub fn create(&self) -> Box { + pub fn create(&self, gas: U256) -> Box { match self.evm { VMType::Jit => { Box::new(super::jit::JitEvm::default()) }, - VMType::Interpreter => { - Box::new(super::interpreter::Interpreter::default()) + VMType::Interpreter => if Self::can_fit_in_usize(gas) { + Box::new(super::interpreter::Interpreter::::default()) + } else { + Box::new(super::interpreter::Interpreter::::default()) } } } /// Create fresh instance of VM + /// Might choose implementation depending on supplied gas. #[cfg(not(feature = "jit"))] - pub fn create(&self) -> Box { + pub fn create(&self, gas: U256) -> Box { match self.evm { - VMType::Interpreter => { - Box::new(super::interpreter::Interpreter::default()) + VMType::Interpreter => if Self::can_fit_in_usize(gas) { + Box::new(super::interpreter::Interpreter::::default()) + } else { + Box::new(super::interpreter::Interpreter::::default()) } } } @@ -113,6 +120,10 @@ impl Factory { evm: evm } } + + fn can_fit_in_usize(gas: U256) -> bool { + gas == U256::from(gas.low_u64() as usize) + } } impl Default for Factory { @@ -135,7 +146,7 @@ impl Default for Factory { #[test] fn test_create_vm() { - let _vm = Factory::default().create(); + let _vm = Factory::default().create(U256::zero()); } /// Create tests by injecting different VM factories diff --git a/ethcore/src/evm/instructions.rs b/ethcore/src/evm/instructions.rs index c313726f1..9a1ea97cc 100644 --- a/ethcore/src/evm/instructions.rs +++ b/ethcore/src/evm/instructions.rs @@ -79,7 +79,7 @@ fn test_get_log_topics() { assert_eq!(get_log_topics(LOG4), 4); } -#[derive(PartialEq)] +#[derive(PartialEq, Clone, Copy)] pub enum GasPriceTier { /// 0 Zero Zero, diff --git a/ethcore/src/evm/interpreter/gasometer.rs b/ethcore/src/evm/interpreter/gasometer.rs new file mode 100644 index 000000000..069d70e19 --- /dev/null +++ b/ethcore/src/evm/interpreter/gasometer.rs @@ -0,0 +1,261 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use common::*; +use super::u256_to_address; +use evm::{self, CostType}; +use evm::instructions::{self, Instruction, InstructionInfo}; +use evm::interpreter::stack::Stack; + +macro_rules! overflowing { + ($x: expr) => {{ + let (v, overflow) = $x; + if overflow { return Err(evm::Error::OutOfGas); } + v + }} +} + +#[cfg_attr(feature="dev", allow(enum_variant_names))] +enum InstructionCost { + Gas(Cost), + GasMem(Cost, Cost), + GasMemCopy(Cost, Cost, Cost) +} + +pub struct Gasometer { + pub current_gas: Gas, +} + +impl Gasometer { + + pub fn new(current_gas: Gas) -> Self { + Gasometer { + current_gas: current_gas, + } + } + + pub fn verify_gas(&self, gas_cost: &Gas) -> evm::Result<()> { + match &self.current_gas < gas_cost { + true => Err(evm::Error::OutOfGas), + false => Ok(()) + } + } + + #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] + pub fn get_gas_cost_mem( + &mut self, + ext: &evm::Ext, + instruction: Instruction, + info: &InstructionInfo, + stack: &Stack, + current_mem_size: usize, + ) -> evm::Result<(Gas, usize)> { + let schedule = ext.schedule(); + let tier = instructions::get_tier_idx(info.tier); + let default_gas = Gas::from(schedule.tier_step_gas[tier]); + + let cost = match instruction { + instructions::SSTORE => { + let address = H256::from(stack.peek(0)); + let newval = stack.peek(1); + let val = U256::from(ext.storage_at(&address).as_slice()); + + let gas = if U256::zero() == val && &U256::zero() != newval { + schedule.sstore_set_gas + } else { + // Refund for below case is added when actually executing sstore + // !self.is_zero(&val) && self.is_zero(newval) + schedule.sstore_reset_gas + }; + InstructionCost::Gas(Gas::from(gas)) + }, + instructions::SLOAD => { + InstructionCost::Gas(Gas::from(schedule.sload_gas)) + }, + instructions::MSTORE | instructions::MLOAD => { + InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 32))) + }, + instructions::MSTORE8 => { + InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 1))) + }, + instructions::RETURN => { + InstructionCost::GasMem(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(1)))) + }, + instructions::SHA3 => { + let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31)); + let words = w >> 5; + let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words); + InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1)))) + }, + instructions::CALLDATACOPY | instructions::CODECOPY => { + InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2)))) + }, + instructions::EXTCODECOPY => { + InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3)))) + }, + instructions::JUMPDEST => { + InstructionCost::Gas(Gas::from(1)) + }, + instructions::LOG0...instructions::LOG4 => { + let no_of_topics = instructions::get_log_topics(instruction); + let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics; + + let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas))); + let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas))); + InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1)))) + }, + instructions::CALL | instructions::CALLCODE => { + let mut gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas)); + let mem = cmp::max( + try!(self.mem_needed(stack.peek(5), stack.peek(6))), + try!(self.mem_needed(stack.peek(3), stack.peek(4))) + ); + + let address = u256_to_address(stack.peek(1)); + + if instruction == instructions::CALL && !ext.exists(&address) { + gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_new_account_gas))); + }; + + if stack.peek(2) > &U256::zero() { + gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_value_transfer_gas))); + }; + + InstructionCost::GasMem(gas,mem) + }, + instructions::DELEGATECALL => { + let gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas)); + let mem = cmp::max( + try!(self.mem_needed(stack.peek(4), stack.peek(5))), + try!(self.mem_needed(stack.peek(2), stack.peek(3))) + ); + InstructionCost::GasMem(gas, mem) + }, + instructions::CREATE => { + let gas = Gas::from(schedule.create_gas); + let mem = try!(self.mem_needed(stack.peek(1), stack.peek(2))); + InstructionCost::GasMem(gas, mem) + }, + instructions::EXP => { + let expon = stack.peek(1); + let bytes = ((expon.bits() + 7) / 8) as usize; + let gas = Gas::from(schedule.exp_gas + schedule.exp_byte_gas * bytes); + InstructionCost::Gas(gas) + }, + _ => InstructionCost::Gas(default_gas) + }; + + match cost { + InstructionCost::Gas(gas) => { + Ok((gas, 0)) + }, + InstructionCost::GasMem(gas, mem_size) => { + let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size)); + let gas = overflowing!(gas.overflow_add(mem_gas)); + Ok((gas, new_mem_size)) + }, + InstructionCost::GasMemCopy(gas, mem_size, copy) => { + let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size)); + let copy = overflowing!(add_gas_usize(copy, 31)); + let copy_gas = Gas::from(schedule.copy_gas) * (copy / Gas::from(32 as usize)); + let gas = overflowing!(gas.overflow_add(copy_gas)); + let gas = overflowing!(gas.overflow_add(mem_gas)); + Ok((gas, new_mem_size)) + } + } + } + + fn is_zero(&self, val: &Gas) -> bool { + &Gas::from(0) == val + } + + fn mem_needed_const(&self, mem: &U256, add: usize) -> evm::Result { + Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add)))) + } + + fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result { + if self.is_zero(&try!(Gas::from_u256(*size))) { + return Ok(Gas::from(0)); + } + + Gas::from_u256(overflowing!(offset.overflowing_add(*size))) + } + + fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, usize)> { + let gas_for_mem = |mem_size: Gas| { + let s = mem_size >> 5; + // s * memory_gas + s * s / quad_coeff_div + let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas))); + // We need to go to U512 to calculate s*s/quad_coeff_div + let b = U512::from(s.as_u256()) * U512::from(s.as_u256()) / U512::from(schedule.quad_coeff_div); + if b > U512::from(!U256::zero()) { + Err(evm::Error::OutOfGas) + } else { + Ok(overflowing!(a.overflow_add(try!(Gas::from_u256(U256::from(b)))))) + } + }; + let current_mem_size = Gas::from(current_mem_size); + let req_mem_size_rounded = (overflowing!(mem_size.overflow_add(Gas::from(31 as usize))) >> 5) << 5; + + let mem_gas_cost = if req_mem_size_rounded > current_mem_size { + let new_mem_gas = try!(gas_for_mem(req_mem_size_rounded)); + let current_mem_gas = try!(gas_for_mem(current_mem_size)); + new_mem_gas - current_mem_gas + } else { + Gas::from(0) + }; + + Ok((mem_gas_cost, req_mem_size_rounded.as_usize())) + } +} + +#[inline] +fn add_gas_usize(value: Gas, num: usize) -> (Gas, bool) { + value.overflow_add(Gas::from(num)) +} + +#[test] +fn test_mem_gas_cost() { + // given + let gasometer = Gasometer::::new(U256::zero()); + let schedule = evm::Schedule::default(); + let current_mem_size = 5; + let mem_size = !U256::zero(); + + // when + let result = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size); + + // then + if let Ok(_) = result { + assert!(false, "Should fail with OutOfGas"); + } +} + +#[test] +fn test_calculate_mem_cost() { + // given + let gasometer = Gasometer::::new(0); + let schedule = evm::Schedule::default(); + let current_mem_size = 0; + let mem_size = 5; + + // when + let (mem_cost, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap(); + + // then + assert_eq!(mem_cost, 3); + assert_eq!(mem_size, 32); +} diff --git a/ethcore/src/evm/interpreter/memory.rs b/ethcore/src/evm/interpreter/memory.rs new file mode 100644 index 000000000..a77a6d494 --- /dev/null +++ b/ethcore/src/evm/interpreter/memory.rs @@ -0,0 +1,150 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use util::{U256, Uint}; + +pub trait Memory { + /// Retrieve current size of the memory + fn size(&self) -> usize; + /// Resize (shrink or expand) the memory to specified size (fills 0) + fn resize(&mut self, new_size: usize); + /// Resize the memory only if its smaller + fn expand(&mut self, new_size: usize); + /// Write single byte to memory + fn write_byte(&mut self, offset: U256, value: U256); + /// Write a word to memory. Does not resize memory! + fn write(&mut self, offset: U256, value: U256); + /// Read a word from memory + fn read(&self, offset: U256) -> U256; + /// Write slice of bytes to memory. Does not resize memory! + fn write_slice(&mut self, offset: U256, &[u8]); + /// Retrieve part of the memory between offset and offset + size + fn read_slice(&self, offset: U256, size: U256) -> &[u8]; + /// Retrieve writeable part of memory + fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8]; + fn dump(&self); +} + +/// Checks whether offset and size is valid memory range +fn is_valid_range(off: usize, size: usize) -> bool { + // When size is zero we haven't actually expanded the memory + let overflow = off.overflowing_add(size).1; + size > 0 && !overflow +} + +impl Memory for Vec { + fn dump(&self) { + println!("MemoryDump:"); + for i in self.iter() { + println!("{:02x} ", i); + } + println!(""); + } + + fn size(&self) -> usize { + self.len() + } + + fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] { + let off = init_off_u.low_u64() as usize; + let size = init_size_u.low_u64() as usize; + if !is_valid_range(off, size) { + &self[0..0] + } else { + &self[off..off+size] + } + } + + fn read(&self, offset: U256) -> U256 { + let off = offset.low_u64() as usize; + U256::from(&self[off..off+32]) + } + + fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] { + let off = offset.low_u64() as usize; + let s = size.low_u64() as usize; + if !is_valid_range(off, s) { + &mut self[0..0] + } else { + &mut self[off..off+s] + } + } + + fn write_slice(&mut self, offset: U256, slice: &[u8]) { + let off = offset.low_u64() as usize; + + // TODO [todr] Optimize? + for pos in off..off+slice.len() { + self[pos] = slice[pos - off]; + } + } + + fn write(&mut self, offset: U256, value: U256) { + let off = offset.low_u64() as usize; + let mut val = value; + + let end = off + 32; + for pos in 0..32 { + self[end - pos - 1] = val.low_u64() as u8; + val = val >> 8; + } + } + + fn write_byte(&mut self, offset: U256, value: U256) { + let off = offset.low_u64() as usize; + let val = value.low_u64() as u64; + self[off] = val as u8; + } + + fn resize(&mut self, new_size: usize) { + self.resize(new_size, 0); + } + + fn expand(&mut self, size: usize) { + if size > self.len() { + Memory::resize(self, size) + } + } +} + + +#[test] +fn test_memory_read_and_write() { + // given + let mem: &mut Memory = &mut vec![]; + mem.resize(0x80 + 32); + + // when + mem.write(U256::from(0x80), U256::from(0xabcdef)); + + // then + assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef)); +} + +#[test] +fn test_memory_read_and_write_byte() { + // given + let mem: &mut Memory = &mut vec![]; + mem.resize(32); + + // when + mem.write_byte(U256::from(0x1d), U256::from(0xab)); + mem.write_byte(U256::from(0x1e), U256::from(0xcd)); + mem.write_byte(U256::from(0x1f), U256::from(0xef)); + + // then + assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef)); +} diff --git a/ethcore/src/evm/interpreter.rs b/ethcore/src/evm/interpreter/mod.rs similarity index 57% rename from ethcore/src/evm/interpreter.rs rename to ethcore/src/evm/interpreter/mod.rs index 5b97b66af..2e03f44b8 100644 --- a/ethcore/src/evm/interpreter.rs +++ b/ethcore/src/evm/interpreter/mod.rs @@ -16,12 +16,6 @@ ///! Rust VM implementation -use common::*; -use super::instructions as instructions; -use super::instructions::{Instruction, get_info}; -use std::marker::Copy; -use evm::{self, MessageCallResult, ContractCreateResult, GasLeft}; - #[cfg(not(feature = "evm-debug"))] macro_rules! evm_debug { ($x: expr) => {} @@ -34,6 +28,19 @@ macro_rules! evm_debug { } } +mod gasometer; +mod stack; +mod memory; + +use self::gasometer::Gasometer; +use self::stack::{Stack, VecStack}; +use self::memory::Memory; + +use std::marker::PhantomData; +use common::*; +use super::instructions::{self, Instruction, InstructionInfo}; +use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType}; + #[cfg(feature = "evm-debug")] fn color(instruction: Instruction, name: &'static str) -> String { let c = instruction as usize % 6; @@ -41,209 +48,9 @@ fn color(instruction: Instruction, name: &'static str) -> String { format!("\x1B[1;{}m{}\x1B[0m", colors[c], name) } -macro_rules! overflowing { - ($x: expr) => {{ - let (v, overflow) = $x; - if overflow { return Err(evm::Error::OutOfGas); } - v - }} -} - type CodePosition = usize; -type Gas = U256; type ProgramCounter = usize; -/// Stack trait with VM-friendly API -trait Stack { - /// Returns `Stack[len(Stack) - no_from_top]` - fn peek(&self, no_from_top: usize) -> &T; - /// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top] - fn swap_with_top(&mut self, no_from_top: usize); - /// Returns true if Stack has at least `no_of_elems` elements - fn has(&self, no_of_elems: usize) -> bool; - /// Get element from top and remove it from Stack. Panics if stack is empty. - fn pop_back(&mut self) -> T; - /// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty. - fn pop_n(&mut self, no_of_elems: usize) -> &[T]; - /// Add element on top of the Stack - fn push(&mut self, elem: T); - /// Get number of elements on Stack - fn size(&self) -> usize; - /// Returns all data on stack. - fn peek_top(&mut self, no_of_elems: usize) -> &[T]; -} - -struct VecStack { - stack: Vec, - logs: [S; instructions::MAX_NO_OF_TOPICS] -} - -impl VecStack { - fn with_capacity(capacity: usize, zero: S) -> Self { - VecStack { - stack: Vec::with_capacity(capacity), - logs: [zero; instructions::MAX_NO_OF_TOPICS] - } - } -} - -impl Stack for VecStack { - fn peek(&self, no_from_top: usize) -> &S { - &self.stack[self.stack.len() - no_from_top - 1] - } - - fn swap_with_top(&mut self, no_from_top: usize) { - let len = self.stack.len(); - self.stack.swap(len - no_from_top - 1, len - 1); - } - - fn has(&self, no_of_elems: usize) -> bool { - self.stack.len() >= no_of_elems - } - - fn pop_back(&mut self) -> S { - let val = self.stack.pop(); - match val { - Some(x) => { - evm_debug!({ - println!(" POP: {}", x) - }); - x - }, - None => panic!("Tried to pop from empty stack.") - } - } - - fn pop_n(&mut self, no_of_elems: usize) -> &[S] { - assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS); - - for i in 0..no_of_elems { - self.logs[i] = self.pop_back(); - } - &self.logs[0..no_of_elems] - } - - fn push(&mut self, elem: S) { - evm_debug!({ - println!(" PUSH: {}", elem) - }); - self.stack.push(elem); - } - - fn size(&self) -> usize { - self.stack.len() - } - - fn peek_top(&mut self, no_from_top: usize) -> &[S] { - assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist."); - &self.stack[self.stack.len() - no_from_top .. self.stack.len()] - } -} - -trait Memory { - /// Retrieve current size of the memory - fn size(&self) -> usize; - /// Resize (shrink or expand) the memory to specified size (fills 0) - fn resize(&mut self, new_size: usize); - /// Resize the memory only if its smaller - fn expand(&mut self, new_size: usize); - /// Write single byte to memory - fn write_byte(&mut self, offset: U256, value: U256); - /// Write a word to memory. Does not resize memory! - fn write(&mut self, offset: U256, value: U256); - /// Read a word from memory - fn read(&self, offset: U256) -> U256; - /// Write slice of bytes to memory. Does not resize memory! - fn write_slice(&mut self, offset: U256, &[u8]); - /// Retrieve part of the memory between offset and offset + size - fn read_slice(&self, offset: U256, size: U256) -> &[u8]; - /// Retrieve writeable part of memory - fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut[u8]; - fn dump(&self); -} - -/// Checks whether offset and size is valid memory range -fn is_valid_range(off: usize, size: usize) -> bool { - // When size is zero we haven't actually expanded the memory - let overflow = off.overflowing_add(size).1; - size > 0 && !overflow -} - -impl Memory for Vec { - fn dump(&self) { - println!("MemoryDump:"); - for i in self.iter() { - println!("{:02x} ", i); - } - println!(""); - } - - fn size(&self) -> usize { - self.len() - } - - fn read_slice(&self, init_off_u: U256, init_size_u: U256) -> &[u8] { - let off = init_off_u.low_u64() as usize; - let size = init_size_u.low_u64() as usize; - if !is_valid_range(off, size) { - &self[0..0] - } else { - &self[off..off+size] - } - } - - fn read(&self, offset: U256) -> U256 { - let off = offset.low_u64() as usize; - U256::from(&self[off..off+32]) - } - - fn writeable_slice(&mut self, offset: U256, size: U256) -> &mut [u8] { - let off = offset.low_u64() as usize; - let s = size.low_u64() as usize; - if !is_valid_range(off, s) { - &mut self[0..0] - } else { - &mut self[off..off+s] - } - } - - fn write_slice(&mut self, offset: U256, slice: &[u8]) { - let off = offset.low_u64() as usize; - - // TODO [todr] Optimize? - for pos in off..off+slice.len() { - self[pos] = slice[pos - off]; - } - } - - fn write(&mut self, offset: U256, value: U256) { - let off = offset.low_u64() as usize; - let mut val = value; - - let end = off + 32; - for pos in 0..32 { - self[end - pos - 1] = val.low_u64() as u8; - val = val >> 8; - } - } - - fn write_byte(&mut self, offset: U256, value: U256) { - let off = offset.low_u64() as usize; - let val = value.low_u64() as u64; - self[off] = val as u8; - } - - fn resize(&mut self, new_size: usize) { - self.resize(new_size, 0); - } - - fn expand(&mut self, size: usize) { - if size > self.len() { - Memory::resize(self, size) - } - } -} - /// Abstraction over raw vector of Bytes. Easier state management of PC. struct CodeReader<'a> { position: ProgramCounter, @@ -265,38 +72,33 @@ impl<'a> CodeReader<'a> { } } -#[cfg_attr(feature="dev", allow(enum_variant_names))] -enum InstructionCost { - Gas(U256), - GasMem(U256, U256), - GasMemCopy(U256, U256, U256) -} - -enum InstructionResult { +enum InstructionResult { Ok, UseAllGas, - GasLeft(U256), - UnusedGas(U256), + GasLeft(Gas), + UnusedGas(Gas), JumpToPosition(U256), // gas left, init_orf, init_size - StopExecutionNeedsReturn(U256, U256, U256), + StopExecutionNeedsReturn(Gas, U256, U256), StopExecution, } + /// Intepreter EVM implementation #[derive(Default)] -pub struct Interpreter { +pub struct Interpreter { mem: Vec, + _type: PhantomData, } -impl evm::Evm for Interpreter { +impl evm::Evm for Interpreter { fn exec(&mut self, params: ActionParams, ext: &mut evm::Ext) -> evm::Result { self.mem.clear(); let code = ¶ms.code.as_ref().unwrap(); let valid_jump_destinations = self.find_jump_destinations(&code); - let mut current_gas = params.gas; + let mut gasometer = Gasometer::::new(try!(Cost::from_u256(params.gas))); let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero()); let mut reader = CodeReader { position: 0, @@ -305,26 +107,27 @@ impl evm::Evm for Interpreter { while reader.position < code.len() { let instruction = code[reader.position]; - - // Calculate gas cost - let (gas_cost, mem_size) = try!(self.get_gas_cost_mem(ext, instruction, &stack)); - - // TODO: make compile-time removable if too much of a performance hit. - let trace_executed = ext.trace_prepare_execute(reader.position, instruction, &gas_cost); - reader.position += 1; - try!(self.verify_gas(¤t_gas, &gas_cost)); + let info = instructions::get_info(instruction); + try!(self.verify_instruction(ext, instruction, &info, &stack)); + + // Calculate gas cost + let (gas_cost, mem_size) = try!(gasometer.get_gas_cost_mem(ext, instruction, &info, &stack, self.mem.size())); + // TODO: make compile-time removable if too much of a performance hit. + let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256()); + + try!(gasometer.verify_gas(&gas_cost)); self.mem.expand(mem_size); - current_gas = current_gas - gas_cost; //TODO: use operator -= + gasometer.current_gas = gasometer.current_gas - gas_cost; evm_debug!({ println!("[0x{:x}][{}(0x{:x}) Gas: {:x}\n Gas Before: {:x}", reader.position, - color(instruction, instructions::get_info(instruction).name), + color(instruction, info.name), instruction, gas_cost, - current_gas + gas_cost + gasometer.current_gas + gas_cost ); }); @@ -335,50 +138,44 @@ impl evm::Evm for Interpreter { // Execute instruction let result = try!(self.exec_instruction( - current_gas, ¶ms, ext, instruction, &mut reader, &mut stack + gasometer.current_gas, ¶ms, ext, instruction, &mut reader, &mut stack )); if trace_executed { - ext.trace_executed(current_gas, stack.peek_top(get_info(instruction).ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written); + ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written); } // Advance match result { InstructionResult::Ok => {}, InstructionResult::UnusedGas(gas) => { - current_gas = current_gas + gas; //TODO: use operator += + gasometer.current_gas = gasometer.current_gas + gas; }, InstructionResult::UseAllGas => { - current_gas = U256::zero(); + gasometer.current_gas = Cost::from(0); }, InstructionResult::GasLeft(gas_left) => { - current_gas = gas_left; + gasometer.current_gas = gas_left; }, InstructionResult::JumpToPosition(position) => { let pos = try!(self.verify_jump(position, &valid_jump_destinations)); reader.position = pos; }, InstructionResult::StopExecutionNeedsReturn(gas, off, size) => { - return Ok(GasLeft::NeedsReturn(gas, self.mem.read_slice(off, size))); + return Ok(GasLeft::NeedsReturn(gas.as_u256(), self.mem.read_slice(off, size))); }, InstructionResult::StopExecution => break, } } - Ok(GasLeft::Known(current_gas)) + Ok(GasLeft::Known(gasometer.current_gas.as_u256())) } } -impl Interpreter { - #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] - fn get_gas_cost_mem( - &mut self, - ext: &evm::Ext, - instruction: Instruction, - stack: &Stack - ) -> evm::Result<(U256, usize)> { +impl Interpreter { + + fn verify_instruction(&self, ext: &evm::Ext, instruction: Instruction, info: &InstructionInfo, stack: &Stack) -> evm::Result<()> { let schedule = ext.schedule(); - let info = instructions::get_info(instruction); if !schedule.have_delegate_call && instruction == instructions::DELEGATECALL { return Err(evm::Error::BadInstruction { @@ -391,119 +188,20 @@ impl Interpreter { }); } - try!(self.verify_instructions_requirements(&info, schedule.stack_limit, stack)); - - let tier = instructions::get_tier_idx(info.tier); - let default_gas = U256::from(schedule.tier_step_gas[tier]); - - let cost = match instruction { - instructions::SSTORE => { - let address = H256::from(stack.peek(0)); - let newval = stack.peek(1); - let val = U256::from(ext.storage_at(&address).as_slice()); - - let gas = if self.is_zero(&val) && !self.is_zero(newval) { - schedule.sstore_set_gas - } else { - // Refund for below case is added when actually executing sstore - // !self.is_zero(&val) && self.is_zero(newval) - schedule.sstore_reset_gas - }; - InstructionCost::Gas(U256::from(gas)) - }, - instructions::SLOAD => { - InstructionCost::Gas(U256::from(schedule.sload_gas)) - }, - instructions::MSTORE | instructions::MLOAD => { - InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 32))) - }, - instructions::MSTORE8 => { - InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 1))) - }, - instructions::RETURN => { - InstructionCost::GasMem(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(1)))) - }, - instructions::SHA3 => { - let w = overflowing!(add_u256_usize(stack.peek(1), 31)); - let words = w >> 5; - let gas = U256::from(schedule.sha3_gas) + (U256::from(schedule.sha3_word_gas) * words); - InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1)))) - }, - instructions::CALLDATACOPY | instructions::CODECOPY => { - InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(2))), stack.peek(2).clone()) - }, - instructions::EXTCODECOPY => { - InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), stack.peek(3).clone()) - }, - instructions::JUMPDEST => { - InstructionCost::Gas(U256::one()) - }, - instructions::LOG0...instructions::LOG4 => { - let no_of_topics = instructions::get_log_topics(instruction); - let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics; - - let data_gas = overflowing!(stack.peek(1).overflowing_mul(U256::from(schedule.log_data_gas))); - let gas = overflowing!(data_gas.overflowing_add(U256::from(log_gas))); - InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1)))) - }, - instructions::CALL | instructions::CALLCODE => { - let mut gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas)); - let mem = cmp::max( - try!(self.mem_needed(stack.peek(5), stack.peek(6))), - try!(self.mem_needed(stack.peek(3), stack.peek(4))) - ); - - let address = u256_to_address(stack.peek(1)); - - if instruction == instructions::CALL && !ext.exists(&address) { - gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_new_account_gas))); - }; - - if stack.peek(2).clone() > U256::zero() { - gas = overflowing!(gas.overflowing_add(U256::from(schedule.call_value_transfer_gas))); - }; - - InstructionCost::GasMem(gas,mem) - }, - instructions::DELEGATECALL => { - let gas = overflowing!(add_u256_usize(stack.peek(0), schedule.call_gas)); - let mem = cmp::max( - try!(self.mem_needed(stack.peek(4), stack.peek(5))), - try!(self.mem_needed(stack.peek(2), stack.peek(3))) - ); - InstructionCost::GasMem(gas, mem) - }, - instructions::CREATE => { - let gas = U256::from(schedule.create_gas); - let mem = try!(self.mem_needed(stack.peek(1), stack.peek(2))); - InstructionCost::GasMem(gas, mem) - }, - instructions::EXP => { - let expon = stack.peek(1); - let bytes = ((expon.bits() + 7) / 8) as usize; - let gas = U256::from(schedule.exp_gas + schedule.exp_byte_gas * bytes); - InstructionCost::Gas(gas) - }, - _ => InstructionCost::Gas(default_gas) - }; - - match cost { - InstructionCost::Gas(gas) => { - Ok((gas, 0)) - }, - InstructionCost::GasMem(gas, mem_size) => { - let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size)); - let gas = overflowing!(gas.overflowing_add(mem_gas)); - Ok((gas, new_mem_size)) - }, - InstructionCost::GasMemCopy(gas, mem_size, copy) => { - let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, self.mem.size(), &mem_size)); - let copy = overflowing!(add_u256_usize(©, 31)); - let copy_gas = U256::from(schedule.copy_gas) * (copy / U256::from(32)); - let gas = overflowing!(gas.overflowing_add(copy_gas)); - let gas = overflowing!(gas.overflowing_add(mem_gas)); - Ok((gas, new_mem_size)) - } + if !stack.has(info.args) { + Err(evm::Error::StackUnderflow { + instruction: info.name, + wanted: info.args, + on_stack: stack.size() + }) + } else if stack.size() - info.args + info.ret > schedule.stack_limit { + Err(evm::Error::OutOfStack { + instruction: info.name, + wanted: info.ret - info.args, + limit: schedule.stack_limit + }) + } else { + Ok(()) } } @@ -532,53 +230,16 @@ impl Interpreter { } } - fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &U256) -> evm::Result<(U256, usize)> { - let gas_for_mem = |mem_size: U256| { - let s = mem_size >> 5; - // s * memory_gas + s * s / quad_coeff_div - let a = overflowing!(s.overflowing_mul(U256::from(schedule.memory_gas))); - // We need to go to U512 to calculate s*s/quad_coeff_div - let b = U512::from(s) * U512::from(s) / U512::from(schedule.quad_coeff_div); - if b > U512::from(!U256::zero()) { - Err(evm::Error::OutOfGas) - } else { - Ok(overflowing!(a.overflowing_add(U256::from(b)))) - } - }; - let current_mem_size = U256::from(current_mem_size); - let req_mem_size_rounded = (overflowing!(mem_size.overflowing_add(U256::from(31))) >> 5) << 5; - let new_mem_gas = try!(gas_for_mem(U256::from(req_mem_size_rounded))); - let current_mem_gas = try!(gas_for_mem(current_mem_size)); - - Ok((if req_mem_size_rounded > current_mem_size { - new_mem_gas - current_mem_gas - } else { - U256::zero() - }, req_mem_size_rounded.low_u64() as usize)) - } - - fn mem_needed_const(&self, mem: &U256, add: usize) -> evm::Result { - Ok(overflowing!(mem.overflowing_add(U256::from(add)))) - } - - fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result { - if self.is_zero(size) { - return Ok(U256::zero()); - } - - Ok(overflowing!(offset.overflowing_add(size.clone()))) - } - #[cfg_attr(feature="dev", allow(too_many_arguments))] fn exec_instruction( &mut self, - gas: Gas, + gas: Cost, params: &ActionParams, ext: &mut evm::Ext, instruction: Instruction, code: &mut CodeReader, stack: &mut Stack - ) -> evm::Result { + ) -> evm::Result> { match instruction { instructions::JUMP => { let jump = stack.pop_back(); @@ -611,11 +272,11 @@ impl Interpreter { return Ok(InstructionResult::Ok); } - let create_result = ext.create(&gas, &endowment, &contract_code); + let create_result = ext.create(&gas.as_u256(), &endowment, &contract_code); return match create_result { ContractCreateResult::Created(address, gas_left) => { stack.push(address_to_u256(address)); - Ok(InstructionResult::GasLeft(gas_left)) + Ok(InstructionResult::GasLeft(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))) }, ContractCreateResult::Failed => { stack.push(U256::zero()); @@ -626,7 +287,7 @@ impl Interpreter { }, instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL => { assert!(ext.schedule().call_value_transfer_gas > ext.schedule().call_stipend, "overflow possible"); - let call_gas = stack.pop_back(); + let call_gas = Cost::from_u256(stack.pop_back()).expect("Gas is already validated."); let code_address = stack.pop_back(); let code_address = u256_to_address(&code_address); @@ -642,9 +303,9 @@ impl Interpreter { let out_size = stack.pop_back(); // Add stipend (only CALL|CALLCODE when value > 0) - let call_gas = call_gas + value.map_or_else(U256::zero, |val| match val > U256::zero() { - true => U256::from(ext.schedule().call_stipend), - false => U256::zero() + let call_gas = call_gas + value.map_or_else(|| Cost::from(0), |val| match val > U256::zero() { + true => Cost::from(ext.schedule().call_stipend), + false => Cost::from(0) }); // Get sender & receive addresses, check if we have balance @@ -672,13 +333,13 @@ impl Interpreter { // and we don't want to copy let input = unsafe { ::std::mem::transmute(self.mem.read_slice(in_off, in_size)) }; let output = self.mem.writeable_slice(out_off, out_size); - ext.call(&call_gas, sender_address, receive_address, value, input, &code_address, output) + ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, output) }; return match call_result { MessageCallResult::Success(gas_left) => { stack.push(U256::one()); - Ok(InstructionResult::UnusedGas(gas_left)) + Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater then current one"))) }, MessageCallResult::Failed => { stack.push(U256::zero()); @@ -759,7 +420,7 @@ impl Interpreter { stack.push(U256::from(code.position - 1)); }, instructions::GAS => { - stack.push(gas.clone()); + stack.push(gas.as_u256()); }, instructions::ADDRESS => { stack.push(address_to_u256(params.address.clone())); @@ -876,36 +537,6 @@ impl Interpreter { } } - fn verify_instructions_requirements( - &self, - info: &instructions::InstructionInfo, - stack_limit: usize, - stack: &Stack - ) -> evm::Result<()> { - if !stack.has(info.args) { - Err(evm::Error::StackUnderflow { - instruction: info.name, - wanted: info.args, - on_stack: stack.size() - }) - } else if stack.size() - info.args + info.ret > stack_limit { - Err(evm::Error::OutOfStack { - instruction: info.name, - wanted: info.ret - info.args, - limit: stack_limit - }) - } else { - Ok(()) - } - } - - fn verify_gas(&self, current_gas: &U256, gas_cost: &U256) -> evm::Result<()> { - match current_gas < gas_cost { - true => Err(evm::Error::OutOfGas), - false => Ok(()) - } - } - fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet) -> evm::Result { let jump = jump_u.low_u64() as usize; @@ -1163,11 +794,6 @@ fn set_sign(value: U256, sign: bool) -> U256 { } } -#[inline] -fn add_u256_usize(value: &U256, num: usize) -> (U256, bool) { - value.clone().overflowing_add(U256::from(num)) -} - #[inline] fn u256_to_address(value: &U256) -> Address { Address::from(H256::from(value)) @@ -1179,82 +805,14 @@ fn address_to_u256(value: Address) -> U256 { } #[test] -fn test_mem_gas_cost() { +fn test_find_jump_destinations() { // given - let interpreter = Interpreter::default(); - let schedule = evm::Schedule::default(); - let current_mem_size = 5; - let mem_size = !U256::zero(); + let interpreter = Interpreter::::default(); + let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap(); // when - let result = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size); + let valid_jump_destinations = interpreter.find_jump_destinations(&code); // then - if let Ok(_) = result { - assert!(false, "Should fail with OutOfGas"); - } -} - -#[cfg(test)] -mod tests { - use common::*; - use super::*; - use evm; - - #[test] - fn test_find_jump_destinations() { - // given - let interpreter = Interpreter::default(); - let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap(); - - // when - let valid_jump_destinations = interpreter.find_jump_destinations(&code); - - // then - assert!(valid_jump_destinations.contains(&66)); - } - - #[test] - fn test_calculate_mem_cost() { - // given - let interpreter = Interpreter::default(); - let schedule = evm::Schedule::default(); - let current_mem_size = 0; - let mem_size = U256::from(5); - - // when - let (mem_cost, mem_size) = interpreter.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap(); - - // then - assert_eq!(mem_cost, U256::from(3)); - assert_eq!(mem_size, 32); - } - - #[test] - fn test_memory_read_and_write() { - // given - let mem: &mut super::Memory = &mut vec![]; - mem.resize(0x80 + 32); - - // when - mem.write(U256::from(0x80), U256::from(0xabcdef)); - - // then - assert_eq!(mem.read(U256::from(0x80)), U256::from(0xabcdef)); - } - - #[test] - fn test_memory_read_and_write_byte() { - // given - let mem: &mut super::Memory = &mut vec![]; - mem.resize(32); - - // when - mem.write_byte(U256::from(0x1d), U256::from(0xab)); - mem.write_byte(U256::from(0x1e), U256::from(0xcd)); - mem.write_byte(U256::from(0x1f), U256::from(0xef)); - - // then - assert_eq!(mem.read(U256::from(0x00)), U256::from(0xabcdef)); - } + assert!(valid_jump_destinations.contains(&66)); } diff --git a/ethcore/src/evm/interpreter/stack.rs b/ethcore/src/evm/interpreter/stack.rs new file mode 100644 index 000000000..98adf8539 --- /dev/null +++ b/ethcore/src/evm/interpreter/stack.rs @@ -0,0 +1,106 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::fmt; +use evm::instructions; + +/// Stack trait with VM-friendly API +pub trait Stack { + /// Returns `Stack[len(Stack) - no_from_top]` + fn peek(&self, no_from_top: usize) -> &T; + /// Swaps Stack[len(Stack)] and Stack[len(Stack) - no_from_top] + fn swap_with_top(&mut self, no_from_top: usize); + /// Returns true if Stack has at least `no_of_elems` elements + fn has(&self, no_of_elems: usize) -> bool; + /// Get element from top and remove it from Stack. Panics if stack is empty. + fn pop_back(&mut self) -> T; + /// Get (up to `instructions::MAX_NO_OF_TOPICS`) elements from top and remove them from Stack. Panics if stack is empty. + fn pop_n(&mut self, no_of_elems: usize) -> &[T]; + /// Add element on top of the Stack + fn push(&mut self, elem: T); + /// Get number of elements on Stack + fn size(&self) -> usize; + /// Returns all data on stack. + fn peek_top(&mut self, no_of_elems: usize) -> &[T]; +} + +pub struct VecStack { + stack: Vec, + logs: [S; instructions::MAX_NO_OF_TOPICS] +} + +impl VecStack { + pub fn with_capacity(capacity: usize, zero: S) -> Self { + VecStack { + stack: Vec::with_capacity(capacity), + logs: [zero; instructions::MAX_NO_OF_TOPICS] + } + } +} + +impl Stack for VecStack { + fn peek(&self, no_from_top: usize) -> &S { + &self.stack[self.stack.len() - no_from_top - 1] + } + + fn swap_with_top(&mut self, no_from_top: usize) { + let len = self.stack.len(); + self.stack.swap(len - no_from_top - 1, len - 1); + } + + fn has(&self, no_of_elems: usize) -> bool { + self.stack.len() >= no_of_elems + } + + fn pop_back(&mut self) -> S { + let val = self.stack.pop(); + match val { + Some(x) => { + evm_debug!({ + println!(" POP: {}", x) + }); + x + }, + None => panic!("Tried to pop from empty stack.") + } + } + + fn pop_n(&mut self, no_of_elems: usize) -> &[S] { + assert!(no_of_elems <= instructions::MAX_NO_OF_TOPICS); + + for i in 0..no_of_elems { + self.logs[i] = self.pop_back(); + } + &self.logs[0..no_of_elems] + } + + fn push(&mut self, elem: S) { + evm_debug!({ + println!(" PUSH: {}", elem) + }); + self.stack.push(elem); + } + + fn size(&self) -> usize { + self.stack.len() + } + + fn peek_top(&mut self, no_from_top: usize) -> &[S] { + assert!(self.stack.len() >= no_from_top, "peek_top asked for more items than exist."); + &self.stack[self.stack.len() - no_from_top .. self.stack.len()] + } +} + diff --git a/ethcore/src/evm/mod.rs b/ethcore/src/evm/mod.rs index 5e7b67cfb..de208c6a4 100644 --- a/ethcore/src/evm/mod.rs +++ b/ethcore/src/evm/mod.rs @@ -28,8 +28,10 @@ mod jit; #[cfg(test)] mod tests; +#[cfg(all(feature="benches", test))] +mod benches; -pub use self::evm::{Evm, Error, Finalize, GasLeft, Result}; +pub use self::evm::{Evm, Error, Finalize, GasLeft, Result, CostType}; pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; pub use self::factory::{Factory, VMType}; pub use self::schedule::Schedule; diff --git a/ethcore/src/evm/tests.rs b/ethcore/src/evm/tests.rs index ba156e6dd..573ffd3b4 100644 --- a/ethcore/src/evm/tests.rs +++ b/ethcore/src/evm/tests.rs @@ -18,18 +18,18 @@ use common::*; use evm::{self, Ext, Schedule, Factory, GasLeft, VMType, ContractCreateResult, MessageCallResult}; use std::fmt::Debug; -struct FakeLogEntry { +pub struct FakeLogEntry { topics: Vec, data: Bytes } #[derive(PartialEq, Eq, Hash, Debug)] -enum FakeCallType { +pub enum FakeCallType { Call, Create } #[derive(PartialEq, Eq, Hash, Debug)] -struct FakeCall { +pub struct FakeCall { call_type: FakeCallType, gas: U256, sender_address: Option
, @@ -43,7 +43,7 @@ struct FakeCall { /// /// Can't do recursive calls. #[derive(Default)] -struct FakeExt { +pub struct FakeExt { sstore_clears: usize, depth: usize, store: HashMap, @@ -67,7 +67,7 @@ fn test_finalize(res: Result) -> Result { } impl FakeExt { - fn new() -> Self { + pub fn new() -> Self { FakeExt::default() } } @@ -181,7 +181,7 @@ fn test_stack_underflow() { let mut ext = FakeExt::new(); let err = { - let mut vm : Box = Box::new(super::interpreter::Interpreter::default()); + let mut vm : Box = Box::new(super::interpreter::Interpreter::::default()); test_finalize(vm.exec(params, &mut ext)).unwrap_err() }; @@ -208,7 +208,7 @@ fn test_add(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -228,7 +228,7 @@ fn test_sha3(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -248,7 +248,7 @@ fn test_address(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -270,7 +270,7 @@ fn test_origin(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -292,7 +292,7 @@ fn test_sender(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -327,7 +327,7 @@ fn test_extcodecopy(factory: super::Factory) { ext.codes.insert(sender, sender_code); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -347,7 +347,7 @@ fn test_log_empty(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -379,7 +379,7 @@ fn test_log_sender(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -404,7 +404,7 @@ fn test_blockhash(factory: super::Factory) { ext.blockhashes.insert(U256::zero(), blockhash.clone()); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -426,7 +426,7 @@ fn test_calldataload(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -447,7 +447,7 @@ fn test_author(factory: super::Factory) { ext.info.author = author; let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -467,7 +467,7 @@ fn test_timestamp(factory: super::Factory) { ext.info.timestamp = timestamp; let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -487,7 +487,7 @@ fn test_number(factory: super::Factory) { ext.info.number = number; let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -507,7 +507,7 @@ fn test_difficulty(factory: super::Factory) { ext.info.difficulty = difficulty; let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -527,7 +527,7 @@ fn test_gas_limit(factory: super::Factory) { ext.info.gas_limit = gas_limit; let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -545,7 +545,7 @@ fn test_mul(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -563,7 +563,7 @@ fn test_sub(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -581,7 +581,7 @@ fn test_div(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -599,7 +599,7 @@ fn test_div_zero(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -617,7 +617,7 @@ fn test_mod(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -636,7 +636,7 @@ fn test_smod(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -655,7 +655,7 @@ fn test_sdiv(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -674,7 +674,7 @@ fn test_exp(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -694,7 +694,7 @@ fn test_comparison(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -715,7 +715,7 @@ fn test_signed_comparison(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -736,7 +736,7 @@ fn test_bitops(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -759,7 +759,7 @@ fn test_addmod_mulmod(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -780,7 +780,7 @@ fn test_byte(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -799,7 +799,7 @@ fn test_signextend(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -819,7 +819,7 @@ fn test_badinstruction_int() { let mut ext = FakeExt::new(); let err = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap_err() }; @@ -839,7 +839,7 @@ fn test_pop(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -859,7 +859,7 @@ fn test_extops(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -882,7 +882,7 @@ fn test_jumps(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; @@ -911,7 +911,7 @@ fn test_calls(factory: super::Factory) { }; let gas_left = { - let mut vm = factory.create(); + let mut vm = factory.create(params.gas); test_finalize(vm.exec(params, &mut ext)).unwrap() }; diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 4525d6a24..f5ff95d71 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -211,7 +211,7 @@ impl<'a> Executive<'a> { let vm_factory = self.vm_factory; let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); - return vm_factory.create().exec(params, &mut ext).finalize(ext); + return vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext); } // Start in new thread to reset stack @@ -222,7 +222,7 @@ impl<'a> Executive<'a> { let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); scope.spawn(move || { - vm_factory.create().exec(params, &mut ext).finalize(ext) + vm_factory.create(params.gas).exec(params, &mut ext).finalize(ext) }) }).join() } diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index f4a34a33e..28dde2a38 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -208,7 +208,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8]) -> Vec { &mut tracer, &mut vm_tracer, ); - let mut evm = vm_factory.create(); + let mut evm = vm_factory.create(params.gas); let res = evm.exec(params, &mut ex); // a return in finalize will not alter callcreates let callcreates = ex.callcreates.clone(); diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 54a944331..183a3c251 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -31,6 +31,7 @@ #![cfg_attr(feature="dev", allow(needless_borrow))] #![cfg_attr(feature="dev", allow(assign_op_pattern))] +#![cfg_attr(feature="benches", feature(test))] //! Ethcore library //! From 62b9c1b14f8dbd0715e018a11355129eb8fc258c Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Tue, 5 Jul 2016 15:16:27 +0200 Subject: [PATCH 15/21] util cleanup (#1474) * removed old json-tests * simplify folds in triehash.rs * removed unused json_aid * removed unused squeeze.rs * json branching tests for trie * loading trie consensus tests --- Cargo.lock | 9 - ethcore/src/json_tests/mod.rs | 1 + ethcore/src/json_tests/trie.rs | 69 +++ json/src/bytes.rs | 43 +- json/src/lib.rs.in | 1 + json/src/trie/input.rs | 155 ++++++ .../src/util.rs => json/src/trie/mod.rs | 15 +- json/src/trie/test.rs | 43 ++ json/src/trie/trie.rs | 30 + util/Cargo.toml | 1 - util/json-tests/Cargo.toml | 8 - util/json-tests/README.md | 15 - util/json-tests/json/.DS_Store | Bin 6148 -> 0 bytes util/json-tests/json/rlp/README.md | 39 -- .../json/rlp/stream/bytestring0.json | 10 - .../json/rlp/stream/bytestring1.json | 10 - .../json/rlp/stream/bytestring7.json | 10 - util/json-tests/json/rlp/stream/catdog.json | 10 - util/json-tests/json/rlp/stream/empty.json | 9 - .../json/rlp/stream/empty_lists.json | 38 -- util/json-tests/json/rlp/stream/integer.json | 10 - .../json/rlp/stream/list_of_empty_data.json | 22 - .../json/rlp/stream/list_of_empty_data2.json | 19 - util/json-tests/json/rlp/stream/longlist.json | 521 ------------------ .../json/rlp/stream/longstring.json | 10 - util/json-tests/json/trie/README.md | 35 -- util/json-tests/json/trie/basic.json | 11 - util/json-tests/json/trie/branching.json | 229 -------- util/json-tests/json/trie/dogs.json | 21 - util/json-tests/json/trie/empty.json | 4 - util/json-tests/json/trie/empty_values.json | 44 -- util/json-tests/json/trie/foo.json | 16 - util/json-tests/json/trie/jeff.json | 58 -- util/json-tests/src/lib.rs | 82 --- util/json-tests/src/rlp.rs | 68 --- util/json-tests/src/trie.rs | 105 ---- util/src/json_aid.rs | 154 ------ util/src/lib.rs | 4 - util/src/rlp/tests.rs | 23 - util/src/squeeze.rs | 84 --- util/src/trie/triedbmut.rs | 90 ++- util/src/triehash.rs | 37 +- 42 files changed, 425 insertions(+), 1738 deletions(-) create mode 100644 ethcore/src/json_tests/trie.rs create mode 100644 json/src/trie/input.rs rename util/json-tests/src/util.rs => json/src/trie/mod.rs (80%) create mode 100644 json/src/trie/test.rs create mode 100644 json/src/trie/trie.rs delete mode 100644 util/json-tests/Cargo.toml delete mode 100644 util/json-tests/README.md delete mode 100644 util/json-tests/json/.DS_Store delete mode 100644 util/json-tests/json/rlp/README.md delete mode 100644 util/json-tests/json/rlp/stream/bytestring0.json delete mode 100644 util/json-tests/json/rlp/stream/bytestring1.json delete mode 100644 util/json-tests/json/rlp/stream/bytestring7.json delete mode 100644 util/json-tests/json/rlp/stream/catdog.json delete mode 100644 util/json-tests/json/rlp/stream/empty.json delete mode 100644 util/json-tests/json/rlp/stream/empty_lists.json delete mode 100644 util/json-tests/json/rlp/stream/integer.json delete mode 100644 util/json-tests/json/rlp/stream/list_of_empty_data.json delete mode 100644 util/json-tests/json/rlp/stream/list_of_empty_data2.json delete mode 100644 util/json-tests/json/rlp/stream/longlist.json delete mode 100644 util/json-tests/json/rlp/stream/longstring.json delete mode 100644 util/json-tests/json/trie/README.md delete mode 100644 util/json-tests/json/trie/basic.json delete mode 100644 util/json-tests/json/trie/branching.json delete mode 100644 util/json-tests/json/trie/dogs.json delete mode 100644 util/json-tests/json/trie/empty.json delete mode 100644 util/json-tests/json/trie/empty_values.json delete mode 100644 util/json-tests/json/trie/foo.json delete mode 100644 util/json-tests/json/trie/jeff.json delete mode 100644 util/json-tests/src/lib.rs delete mode 100644 util/json-tests/src/rlp.rs delete mode 100644 util/json-tests/src/trie.rs delete mode 100644 util/src/json_aid.rs delete mode 100644 util/src/squeeze.rs diff --git a/Cargo.lock b/Cargo.lock index 5071da96b..703935bff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,7 +390,6 @@ dependencies = [ "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "igd 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "json-tests 0.1.0", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -609,14 +608,6 @@ dependencies = [ "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "json-tests" -version = "0.1.0" -dependencies = [ - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "jsonrpc-core" version = "2.0.7" diff --git a/ethcore/src/json_tests/mod.rs b/ethcore/src/json_tests/mod.rs index df67de76d..841db229e 100644 --- a/ethcore/src/json_tests/mod.rs +++ b/ethcore/src/json_tests/mod.rs @@ -23,3 +23,4 @@ mod state; mod chain; mod homestead_state; mod homestead_chain; +mod trie; diff --git a/ethcore/src/json_tests/trie.rs b/ethcore/src/json_tests/trie.rs new file mode 100644 index 000000000..2d23ff7d2 --- /dev/null +++ b/ethcore/src/json_tests/trie.rs @@ -0,0 +1,69 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use ethjson; +use util::{H256, MemoryDB, TrieMut, TrieSpec, TrieFactory}; + +fn test_trie(json: &[u8], trie: TrieSpec) -> Vec { + let tests = ethjson::trie::Test::load(json).unwrap(); + let factory = TrieFactory::new(trie); + let mut result = vec![]; + + for (name, test) in tests.into_iter() { + let mut memdb = MemoryDB::new(); + let mut root = H256::default(); + let mut t = factory.create(&mut memdb, &mut root); + + for (key, value) in test.input.data.into_iter() { + let key: Vec = key.into(); + let value: Vec = value.map_or_else(Vec::new, Into::into); + t.insert(&key, &value); + } + + if *t.root() != test.root.into() { + result.push(format!("Trie test '{:?}' failed.", name)); + } + } + + for i in &result { + println!("FAILED: {}", i); + } + + result +} + +mod generic { + use util::TrieSpec; + + fn do_json_test(json: &[u8]) -> Vec { + super::test_trie(json, TrieSpec::Generic) + } + + declare_test!{TrieTests_trietest, "TrieTests/trietest"} + declare_test!{TrieTests_trieanyorder, "TrieTests/trieanyorder"} +} + +mod secure { + use util::TrieSpec; + + fn do_json_test(json: &[u8]) -> Vec { + super::test_trie(json, TrieSpec::Secure) + } + + declare_test!{TrieTests_hex_encoded_secure, "TrieTests/hex_encoded_securetrie_test"} + declare_test!{TrieTests_trietest_secure, "TrieTests/trietest_secureTrie"} + declare_test!{TrieTests_trieanyorder_secure, "TrieTests/trieanyorder_secureTrie"} +} diff --git a/json/src/bytes.rs b/json/src/bytes.rs index 18dc73844..7741b8d3b 100644 --- a/json/src/bytes.rs +++ b/json/src/bytes.rs @@ -16,15 +16,23 @@ //! Lenient bytes json deserialization for test json files. +use std::str::FromStr; +use std::ops::Deref; 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)] +#[derive(Default, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)] pub struct Bytes(Vec); +impl Bytes { + /// Creates bytes struct. + pub fn new(v: Vec) -> Self { + Bytes(v) + } +} + impl Into> for Bytes { fn into(self) -> Vec { self.0 @@ -39,6 +47,25 @@ impl Deref for Bytes { } } +impl FromStr for Bytes { + type Err = String; + + fn from_str(value: &str) -> Result { + let v = match value.len() { + 0 => vec![], + 2 if value.starts_with("0x") => vec![], + _ if value.starts_with("0x") && value.len() % 2 == 1 => { + let v = "0".to_owned() + &value[2..]; + FromHex::from_hex(v.as_ref() as &str).unwrap_or(vec![]), + }, + _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]), + _ => FromHex::from_hex(value).unwrap_or(vec![]), + }; + + Ok(Bytes(v)) + } +} + impl Deserialize for Bytes { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer { @@ -52,17 +79,7 @@ impl Visitor for BytesVisitor { type Value = Bytes; fn visit_str(&mut self, value: &str) -> Result where E: Error { - let v = match value.len() { - 0 => vec![], - 2 if value.starts_with("0x") => vec![], - _ if value.starts_with("0x") && value.len() % 2 == 1 => { - let v = "0".to_owned() + &value[2..]; - FromHex::from_hex(v.as_ref() as &str).unwrap_or(vec![]), - }, - _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]), - _ => FromHex::from_hex(value).unwrap_or(vec![]), - }; - Ok(Bytes(v)) + Bytes::from_str(value).map_err(Error::custom) } fn visit_string(&mut self, value: String) -> Result where E: Error { diff --git a/json/src/lib.rs.in b/json/src/lib.rs.in index 73adaf07a..541629305 100644 --- a/json/src/lib.rs.in +++ b/json/src/lib.rs.in @@ -24,6 +24,7 @@ pub mod uint; pub mod bytes; pub mod blockchain; pub mod spec; +pub mod trie; pub mod vm; pub mod maybe; pub mod state; diff --git a/json/src/trie/input.rs b/json/src/trie/input.rs new file mode 100644 index 000000000..326f42dba --- /dev/null +++ b/json/src/trie/input.rs @@ -0,0 +1,155 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Trie test input deserialization. + +use std::collections::BTreeMap; +use std::str::FromStr; +use bytes::Bytes; +use serde::{Deserialize, Deserializer, Error}; +use serde::de::{Visitor, MapVisitor, SeqVisitor}; + +/// Trie test input. +#[derive(Debug, PartialEq)] +pub struct Input { + /// Input params. + pub data: BTreeMap>, +} + +impl Deserialize for Input { + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer + { + deserializer.deserialize(InputVisitor) + } +} + +struct InputVisitor; + +impl Visitor for InputVisitor { + type Value = Input; + + fn visit_map(&mut self, mut visitor: V) -> Result where V: MapVisitor { + let mut result = BTreeMap::new(); + + loop { + let key_str: Option = try!(visitor.visit_key()); + let key = match key_str { + Some(ref k) if k.starts_with("0x") => try!(Bytes::from_str(k).map_err(Error::custom)), + Some(k) => Bytes::new(k.into_bytes()), + None => { break; } + }; + + let val_str: Option = try!(visitor.visit_value()); + let val = match val_str { + Some(ref v) if v.starts_with("0x") => Some(try!(Bytes::from_str(v).map_err(Error::custom))), + Some(v) => Some(Bytes::new(v.into_bytes())), + None => None, + }; + + result.insert(key, val); + } + + try!(visitor.end()); + + let input = Input { + data: result + }; + + Ok(input) + } + + fn visit_seq(&mut self, mut visitor: V) -> Result where V: SeqVisitor { + let mut result = BTreeMap::new(); + + loop { + let keyval: Option>> = try!(visitor.visit()); + let keyval = match keyval { + Some(k) => k, + _ => { break; }, + }; + + if keyval.len() != 2 { + return Err(Error::custom("Invalid key value pair.")); + } + + let ref key_str: Option = keyval[0]; + let ref val_str: Option = keyval[1]; + + let key = match *key_str { + Some(ref k) if k.starts_with("0x") => try!(Bytes::from_str(k).map_err(Error::custom)), + Some(ref k) => Bytes::new(k.clone().into_bytes()), + None => { break; } + }; + + let val = match *val_str { + Some(ref v) if v.starts_with("0x") => Some(try!(Bytes::from_str(v).map_err(Error::custom))), + Some(ref v) => Some(Bytes::new(v.clone().into_bytes())), + None => None, + }; + + result.insert(key, val); + } + + try!(visitor.end()); + + let input = Input { + data: result + }; + + Ok(input) + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + use serde_json; + use bytes::Bytes; + use super::Input; + + #[test] + fn input_deserialization_from_map() { + let s = r#"{ + "0x0045" : "0x0123456789", + "be" : "e", + "0x0a" : null + }"#; + + let input: Input = serde_json::from_str(s).unwrap(); + let mut map = BTreeMap::new(); + map.insert(Bytes::new(vec![0, 0x45]), Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89]))); + map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); + map.insert(Bytes::new(vec![0x0a]), None); + assert_eq!(input.data, map); + } + + #[test] + fn input_deserialization_from_array() { + let s = r#"[ + ["0x0045", "0x0123456789"], + ["be", "e"], + ["0x0a", null] + ]"#; + + let input: Input = serde_json::from_str(s).unwrap(); + let mut map = BTreeMap::new(); + map.insert(Bytes::new(vec![0, 0x45]), Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89]))); + map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); + map.insert(Bytes::new(vec![0x0a]), None); + assert_eq!(input.data, map); + } +} diff --git a/util/json-tests/src/util.rs b/json/src/trie/mod.rs similarity index 80% rename from util/json-tests/src/util.rs rename to json/src/trie/mod.rs index f8beb269a..1c5d30067 100644 --- a/util/json-tests/src/util.rs +++ b/json/src/trie/mod.rs @@ -14,11 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use rustc_serialize::hex::FromHex; +//! Trie test deserialization. -pub fn hex_or_string(s: &str) -> Vec { - match s.starts_with("0x") { - true => s[2..].from_hex().unwrap(), - false => From::from(s) - } -} +mod input; +mod trie; +mod test; + +pub use self::input::Input; +pub use self::trie::Trie; +pub use self::test::Test; diff --git a/json/src/trie/test.rs b/json/src/trie/test.rs new file mode 100644 index 000000000..19becd5ee --- /dev/null +++ b/json/src/trie/test.rs @@ -0,0 +1,43 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! TransactionTest test deserializer. + +use std::collections::BTreeMap; +use std::io::Read; +use serde_json; +use serde_json::Error; +use trie::Trie; + +/// TransactionTest test deserializer. +#[derive(Debug, PartialEq, Deserialize)] +pub struct Test(BTreeMap); + +impl IntoIterator for Test { + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl Test { + /// Loads test from json. + pub fn load(reader: R) -> Result where R: Read { + serde_json::from_reader(reader) + } +} diff --git a/json/src/trie/trie.rs b/json/src/trie/trie.rs new file mode 100644 index 000000000..c04498591 --- /dev/null +++ b/json/src/trie/trie.rs @@ -0,0 +1,30 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Trie test deserialization. + +use hash::H256; +use trie::Input; + +/// Trie test deserialization. +#[derive(Debug, Deserialize, PartialEq)] +pub struct Trie { + /// Trie test input. + #[serde(rename="in")] + pub input: Input, + /// Trie root hash. + pub root: H256, +} diff --git a/util/Cargo.toml b/util/Cargo.toml index 4f0f321f3..07a0713e3 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -29,7 +29,6 @@ slab = "0.2" sha3 = { path = "sha3" } serde = "0.7.0" clippy = { version = "0.0.78", optional = true} -json-tests = { path = "json-tests" } igd = "0.4.2" ethcore-devtools = { path = "../devtools" } libc = "0.2.7" diff --git a/util/json-tests/Cargo.toml b/util/json-tests/Cargo.toml deleted file mode 100644 index 3185e2e59..000000000 --- a/util/json-tests/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "json-tests" -version = "0.1.0" -authors = ["debris "] - -[dependencies] -rustc-serialize = "0.3" -glob = "*" diff --git a/util/json-tests/README.md b/util/json-tests/README.md deleted file mode 100644 index 9e6915ca3..000000000 --- a/util/json-tests/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# How to write json test file? - -Cause it's very hard to write generic json test files, each subdirectory should follow its own -convention. BUT all json files `within` same directory should be consistent. - -### Test files should always contain a single test with input and output. - -```json -{ - input: ..., - output: ... -} -``` - -As a reference, please use trietests. diff --git a/util/json-tests/json/.DS_Store b/util/json-tests/json/.DS_Store deleted file mode 100644 index 5ddc63de7e78f0f09186cf46a2ff1cf2b9cae2aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mVfW*>~F$)La1`&c2Z~+8}2?+#Z&(V4QSt!h?LeG-@#ZIlZZ)j=} z(e?AR66r-`1~vIYIqf#4$mI7QhFYo8^+Vg;}!TM!kPMk^St>SKu2 zy&WuhT}`%Nw2S8Op?PPuDF&v|E?SVlv^p5502LT0&_&+c`M-sKoBszbOsN1B_%j7` zy4h{kc&R*FKVHx3`>fi!!9l+q;q4~?i5. - -extern crate rustc_serialize; -extern crate glob; - -use std::str::from_utf8; -use std::path::*; -use std::io::prelude::*; -use std::fs::File; -use glob::glob; -use rustc_serialize::*; - -mod util; -pub mod trie; -pub mod rlp; - -pub trait JsonTest: Sized { - type Input; - type Output; - - fn new(data: &[u8]) -> Self; - fn input(&self) -> Self::Input; - fn output(&self) -> Self::Output; -} - -pub struct JsonLoader { - json: json::Json -} - -impl JsonTest for JsonLoader { - type Input = json::Json; - type Output = json::Json; - - fn new(data: &[u8]) -> Self { - JsonLoader { - json: json::Json::from_str(from_utf8(data).unwrap()).unwrap() - } - } - fn input(&self) -> Self::Input { - self.json.as_object().unwrap()["input"].clone() - } - - fn output(&self) -> Self::Output { - self.json.as_object().unwrap()["output"].clone() - } -} - -pub fn execute_test(data: &[u8], f: &mut F) where T: JsonTest, F: FnMut(T::Input, T::Output) { - let test = T::new(data); - f(test.input(), test.output()) -} - -pub fn execute_test_from_file(path: &Path, f: &mut F) where T: JsonTest, F: FnMut(T::Input, T::Output) { - let mut file = File::open(path).unwrap(); - let mut buffer = vec![]; - let _ = file.read_to_end(&mut buffer); - let test = T::new(&buffer); - f(test.input(), test.output()) -} - -pub fn execute_tests_from_directory(pattern: &str, f: &mut F) where T: JsonTest, F: FnMut(String, T::Input, T::Output) { - for path in glob(pattern).unwrap().filter_map(Result::ok) { - execute_test_from_file::(&path, &mut | input, output | { - f(path.to_str().unwrap().to_string(), input, output); - }); - } -} - diff --git a/util/json-tests/src/rlp.rs b/util/json-tests/src/rlp.rs deleted file mode 100644 index 0b8e4c904..000000000 --- a/util/json-tests/src/rlp.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -//! json rlp tests -use rustc_serialize::*; -use super::{JsonTest, JsonLoader}; -use util::*; - -pub enum Operation { - Append(Vec), - AppendList(usize), - AppendRaw(Vec, usize), - AppendEmpty -} - -impl Into for json::Json { - fn into(self) -> Operation { - let obj = self.as_object().unwrap(); - match obj["operation"].as_string().unwrap().as_ref() { - "append" => Operation::Append(hex_or_string(obj["value"].as_string().unwrap())), - "append_list" => Operation::AppendList(obj["len"].as_u64().unwrap() as usize), - "append_raw" => Operation::AppendRaw(hex_or_string(obj["value"].as_string().unwrap()), obj["len"].as_u64().unwrap() as usize), - "append_empty" => Operation::AppendEmpty, - other => { panic!("Unsupported opertation: {}", other); } - } - } -} - -pub struct RlpStreamTest { - loader: JsonLoader -} - -impl JsonTest for RlpStreamTest { - type Input = Vec; - type Output = Vec; - - fn new(data: &[u8]) -> Self { - RlpStreamTest { - loader: JsonLoader::new(data) - } - } - - fn input(&self) -> Self::Input { - self.loader.input().as_array().unwrap() - .iter() - .cloned() - .map(|i| i.into()) - .collect() - } - - fn output(&self) -> Self::Output { - hex_or_string(self.loader.output().as_string().unwrap()) - } -} - diff --git a/util/json-tests/src/trie.rs b/util/json-tests/src/trie.rs deleted file mode 100644 index a060167af..000000000 --- a/util/json-tests/src/trie.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -//! json trie tests -use std::collections::HashMap; -use rustc_serialize::*; -use super::{JsonTest, JsonLoader}; -use util::*; - -#[derive(RustcDecodable)] -struct RawOperation { - operation: String, - key: String, - value: Option -} - -pub enum Operation { - Insert(Vec, Vec), - Remove(Vec) -} - -impl Into for RawOperation { - fn into(self) -> Operation { - match self.operation.as_ref() { - "insert" => Operation::Insert(hex_or_string(&self.key), hex_or_string(&self.value.unwrap())), - "remove" => Operation::Remove(hex_or_string(&self.key)), - other => panic!("invalid operation type: {}", other) - } - } -} - -pub struct TrieTest { - loader: JsonLoader -} - -impl JsonTest for TrieTest { - type Input = Vec; - type Output = Vec; - - fn new(data: &[u8]) -> Self { - TrieTest { - loader: JsonLoader::new(data) - } - } - - fn input(&self) -> Self::Input { - let mut decoder = json::Decoder::new(self.loader.input()); - let raw: Vec = Decodable::decode(&mut decoder).unwrap(); - raw.into_iter() - .map(|i| i.into()) - .collect() - } - - fn output(&self) -> Self::Output { - hex_or_string(self.loader.output().as_string().unwrap()) - } -} - -pub struct TriehashTest { - trietest: TrieTest -} - -impl JsonTest for TriehashTest { - type Input = Vec<(Vec, Vec)>; - type Output = Vec; - - fn new(data: &[u8]) -> Self { - TriehashTest { - trietest: TrieTest::new(data) - } - } - - fn input(&self) -> Self::Input { - self.trietest.input() - .into_iter() - .fold(HashMap::new(), | mut map, o | { - match o { - Operation::Insert(k, v) => map.insert(k, v), - Operation::Remove(k) => map.remove(&k) - }; - map - }) - .into_iter() - .map(|p| { p }) - .collect() - } - - fn output(&self) -> Self::Output { - self.trietest.output() - } -} - diff --git a/util/src/json_aid.rs b/util/src/json_aid.rs deleted file mode 100644 index 7bf940b99..000000000 --- a/util/src/json_aid.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use common::*; - -/// Remove the `"0x"`, if present, from the left of `s`, returning the remaining slice. -pub fn clean(s: &str) -> &str { - if s.len() >= 2 && &s[0..2] == "0x" { - &s[2..] - } else { - s - } -} - -fn u256_from_str(s: &str) -> U256 { - if s.len() >= 2 && &s[0..2] == "0x" { - U256::from_str(&s[2..]).unwrap_or_else(|_| U256::zero()) - } else { - U256::from_dec_str(s).unwrap_or_else(|_| U256::zero()) - } -} - -impl FromJson for Bytes { - fn from_json(json: &Json) -> Self { - match *json { - Json::String(ref s) => match s.len() % 2 { - 0 => FromHex::from_hex(clean(s)).unwrap_or_else(|_| vec![]), - _ => FromHex::from_hex(&("0".to_owned() + &(clean(s).to_owned()))[..]).unwrap_or_else(|_| vec![]), - }, - _ => vec![], - } - } -} - -impl FromJson for BTreeMap { - fn from_json(json: &Json) -> Self { - match *json { - Json::Object(ref o) => o.iter().map(|(key, value)| (u256_from_str(key).into(), U256::from_json(value).into())).collect(), - _ => BTreeMap::new(), - } - } -} - -impl FromJson for Vec where T: FromJson { - fn from_json(json: &Json) -> Self { - match *json { - Json::Array(ref o) => o.iter().map(|x|T::from_json(x)).collect(), - _ => Vec::new(), - } - } -} - -impl FromJson for Option where T: FromJson { - fn from_json(json: &Json) -> Self { - match *json { - Json::String(ref o) if o.is_empty() => None, - Json::Null => None, - _ => Some(FromJson::from_json(json)), - } - } -} - -impl FromJson for u64 { - fn from_json(json: &Json) -> Self { - U256::from_json(json).low_u64() - } -} - -impl FromJson for u32 { - fn from_json(json: &Json) -> Self { - U256::from_json(json).low_u64() as u32 - } -} - -impl FromJson for u16 { - fn from_json(json: &Json) -> Self { - U256::from_json(json).low_u64() as u16 - } -} - -#[test] -fn u256_from_json() { - let j = Json::from_str("{ \"dec\": \"10\", \"hex\": \"0x0a\", \"int\": 10 }").unwrap(); - - let v: U256 = xjson!(&j["dec"]); - assert_eq!(U256::from(10), v); - let v: U256 = xjson!(&j["hex"]); - assert_eq!(U256::from(10), v); - let v: U256 = xjson!(&j["int"]); - assert_eq!(U256::from(10), v); -} - -#[test] -fn h256_from_json() { - let j = Json::from_str("{ \"with\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\", \"without\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\" }").unwrap(); - - let v: H256 = xjson!(&j["with"]); - assert_eq!(H256::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(), v); - let v: H256 = xjson!(&j["without"]); - assert_eq!(H256::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(), v); -} - -#[test] -fn vec_u256_from_json() { - let j = Json::from_str("{ \"array\": [ \"10\", \"0x0a\", 10] }").unwrap(); - - let v: Vec = xjson!(&j["array"]); - assert_eq!(vec![U256::from(10); 3], v); -} - -#[test] -fn vec_h256_from_json() { - let j = Json::from_str("{ \"array\": [ \"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\", \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"] }").unwrap(); - - let v: Vec = xjson!(&j["array"]); - assert_eq!(vec![H256::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(); 2], v); -} - -#[test] -fn simple_types() { - let j = Json::from_str("{ \"null\": null, \"empty\": \"\", \"int\": 42, \"dec\": \"42\", \"hex\": \"0x2a\" }").unwrap(); - let v: u16 = xjson!(&j["int"]); - assert_eq!(42u16, v); - let v: u32 = xjson!(&j["dec"]); - assert_eq!(42u32, v); - let v: u64 = xjson!(&j["hex"]); - assert_eq!(42u64, v); -} - -#[test] -fn option_types() { - let j = Json::from_str("{ \"null\": null, \"empty\": \"\", \"int\": 42, \"dec\": \"42\", \"hex\": \"0x2a\" }").unwrap(); - let v: Option = xjson!(&j["int"]); - assert_eq!(Some(42u16), v); - let v: Option = xjson!(&j["dec"]); - assert_eq!(Some(42u16), v); - let v: Option = xjson!(&j["null"]); - assert_eq!(None, v); - let v: Option = xjson!(&j["empty"]); - assert_eq!(None, v); -} diff --git a/util/src/lib.rs b/util/src/lib.rs index 009b50782..2e4561d83 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -131,7 +131,6 @@ pub mod bytes; pub mod rlp; pub mod misc; pub mod using_queue; -mod json_aid; pub mod vector; pub mod sha3; pub mod hashdb; @@ -147,7 +146,6 @@ pub mod trie; pub mod nibbleslice; pub mod nibblevec; mod heapsizeof; -pub mod squeeze; pub mod semantic_version; pub mod io; pub mod network; @@ -161,7 +159,6 @@ mod timer; pub use common::*; pub use misc::*; pub use using_queue::*; -pub use json_aid::*; pub use rlp::*; pub use hashdb::*; pub use memorydb::*; @@ -172,7 +169,6 @@ pub use crypto::*; pub use triehash::*; pub use trie::*; pub use nibbleslice::*; -pub use squeeze::*; pub use semantic_version::*; pub use network::*; pub use io::*; diff --git a/util/src/rlp/tests.rs b/util/src/rlp/tests.rs index 3df2f2d97..3d9ed40f1 100644 --- a/util/src/rlp/tests.rs +++ b/util/src/rlp/tests.rs @@ -14,9 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -extern crate json_tests; -use self::json_tests::execute_tests_from_directory; -use self::json_tests::rlp as rlptest; use std::{fmt, cmp}; use std::str::FromStr; use rlp; @@ -340,26 +337,6 @@ fn decode_untrusted_vector_of_vectors_str() { run_decode_tests(tests); } -#[test] -fn test_rlp_json() { - println!("Json rlp test: "); - execute_tests_from_directory::("json-tests/json/rlp/stream/*.json", &mut | file, input, output | { - println!("file: {}", file); - - let mut stream = RlpStream::new(); - for operation in input.into_iter() { - match operation { - rlptest::Operation::Append(ref v) => stream.append(v), - rlptest::Operation::AppendList(len) => stream.begin_list(len), - rlptest::Operation::AppendRaw(ref raw, len) => stream.append_raw(raw, len), - rlptest::Operation::AppendEmpty => stream.append_empty_data() - }; - } - - assert_eq!(stream.out(), output); - }); -} - #[test] fn test_decoding_array() { let v = vec![5u16, 2u16]; diff --git a/util/src/squeeze.rs b/util/src/squeeze.rs deleted file mode 100644 index 69aab9728..000000000 --- a/util/src/squeeze.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -//! Helper module that should be used to randomly squeeze -//! caches to a given size in bytes -//! -//! ``` -//! extern crate heapsize; -//! extern crate ethcore_util as util; -//! use std::collections::HashMap; -//! use std::mem::size_of; -//! use heapsize::HeapSizeOf; -//! use util::squeeze::Squeeze; -//! -//! fn main() { -//! let initial_size = 60; -//! let mut map: HashMap = HashMap::with_capacity(initial_size); -//! assert!(map.capacity() >= initial_size); -//! for i in 0..initial_size { -//! map.insert(i as u8, i as u8); -//! } -//! -//! assert_eq!(map.heap_size_of_children(), map.capacity() * 2 * size_of::()); -//! assert_eq!(map.len(), initial_size); -//! let initial_heap_size = map.heap_size_of_children(); -//! -//! // squeeze it to size of key and value -//! map.squeeze(2 * size_of::()); -//! assert_eq!(map.len(), 1); -//! -//! // its likely that heap size was reduced, but we can't be 100% sure -//! assert!(initial_heap_size >= map.heap_size_of_children()); -//! } -//! ``` - -use std::collections::HashMap; -use std::hash::Hash; -use heapsize::HeapSizeOf; - -/// Should be used to squeeze collections to certain size in bytes -pub trait Squeeze { - /// Try to reduce collection size to `size` bytes - fn squeeze(&mut self, size: usize); -} - -impl Squeeze for HashMap where K: Eq + Hash + Clone + HeapSizeOf, T: HeapSizeOf { - fn squeeze(&mut self, size: usize) { - if self.is_empty() { - return - } - - let size_of_entry = self.heap_size_of_children() / self.capacity(); - let all_entries = size_of_entry * self.len(); - let mut shrinked_size = all_entries; - - while !self.is_empty() && shrinked_size > size { - // could be optimized - let key = self.keys().next().unwrap().clone(); - self.remove(&key); - shrinked_size -= size_of_entry; - } - - self.shrink_to_fit(); - - // if we squeezed something, but not enough, squeeze again - if all_entries != shrinked_size && self.heap_size_of_children() > size { - self.squeeze(size); - } - } -} - diff --git a/util/src/trie/triedbmut.rs b/util/src/trie/triedbmut.rs index 4d46814c0..859bc52e1 100644 --- a/util/src/trie/triedbmut.rs +++ b/util/src/trie/triedbmut.rs @@ -676,8 +676,6 @@ impl<'db> fmt::Debug for TrieDBMut<'db> { #[cfg(test)] mod tests { - extern crate json_tests; - use self::json_tests::{trie, execute_tests_from_directory}; use triehash::*; use hash::*; use hashdb::*; @@ -858,6 +856,21 @@ mod tests { ])); } + #[test] + fn insert_out_of_order() { + let mut memdb = MemoryDB::new(); + let mut root = H256::new(); + let mut t = TrieDBMut::new(&mut memdb, &mut root); + t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]); + t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]); + t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]); + assert_eq!(*t.root(), trie_root(vec![ + (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), + (vec![0x81u8, 0x23], vec![0x81u8, 0x23]), + (vec![0xf1u8, 0x23], vec![0xf1u8, 0x23]), + ])); + } + #[test] fn insert_value_into_branch_root() { let mut memdb = MemoryDB::new(); @@ -1063,23 +1076,64 @@ mod tests { } #[test] - fn test_trie_json() { - println!("Json trie test: "); - execute_tests_from_directory::("json-tests/json/trie/*.json", &mut | file, input, output | { - println!("file: {}", file); + fn branching_test() { + use std::str::FromStr; + use rustc_serialize::hex::FromHex; - let mut memdb = MemoryDB::new(); - let mut root = H256::new(); - let mut t = TrieDBMut::new(&mut memdb, &mut root); - for operation in input.into_iter() { - match operation { - trie::Operation::Insert(key, value) => t.insert(&key, &value), - trie::Operation::Remove(key) => t.remove(&key) - } - } - - assert_eq!(*t.root(), H256::from_slice(&output)); - }); + let mut memdb = MemoryDB::new(); + let mut root = H256::new(); + let mut t = TrieDBMut::new(&mut memdb, &mut root); + t.insert(&"04110d816c380812a427968ece99b1c963dfbce6".from_hex().unwrap(), b"something"); + t.insert(&"095e7baea6a6c7c4c2dfeb977efac326af552d87".from_hex().unwrap(), b"something"); + t.insert(&"0a517d755cebbf66312b30fff713666a9cb917e0".from_hex().unwrap(), b"something"); + t.insert(&"24dd378f51adc67a50e339e8031fe9bd4aafab36".from_hex().unwrap(), b"something"); + t.insert(&"293f982d000532a7861ab122bdc4bbfd26bf9030".from_hex().unwrap(), b"something"); + t.insert(&"2cf5732f017b0cf1b1f13a1478e10239716bf6b5".from_hex().unwrap(), b"something"); + t.insert(&"31c640b92c21a1f1465c91070b4b3b4d6854195f".from_hex().unwrap(), b"something"); + t.insert(&"37f998764813b136ddf5a754f34063fd03065e36".from_hex().unwrap(), b"something"); + t.insert(&"37fa399a749c121f8a15ce77e3d9f9bec8020d7a".from_hex().unwrap(), b"something"); + t.insert(&"4f36659fa632310b6ec438dea4085b522a2dd077".from_hex().unwrap(), b"something"); + t.insert(&"62c01474f089b07dae603491675dc5b5748f7049".from_hex().unwrap(), b"something"); + t.insert(&"729af7294be595a0efd7d891c9e51f89c07950c7".from_hex().unwrap(), b"something"); + t.insert(&"83e3e5a16d3b696a0314b30b2534804dd5e11197".from_hex().unwrap(), b"something"); + t.insert(&"8703df2417e0d7c59d063caa9583cb10a4d20532".from_hex().unwrap(), b"something"); + t.insert(&"8dffcd74e5b5923512916c6a64b502689cfa65e1".from_hex().unwrap(), b"something"); + t.insert(&"95a4d7cccb5204733874fa87285a176fe1e9e240".from_hex().unwrap(), b"something"); + t.insert(&"99b2fcba8120bedd048fe79f5262a6690ed38c39".from_hex().unwrap(), b"something"); + t.insert(&"a4202b8b8afd5354e3e40a219bdc17f6001bf2cf".from_hex().unwrap(), b"something"); + t.insert(&"a94f5374fce5edbc8e2a8697c15331677e6ebf0b".from_hex().unwrap(), b"something"); + t.insert(&"a9647f4a0a14042d91dc33c0328030a7157c93ae".from_hex().unwrap(), b"something"); + t.insert(&"aa6cffe5185732689c18f37a7f86170cb7304c2a".from_hex().unwrap(), b"something"); + t.insert(&"aae4a2e3c51c04606dcb3723456e58f3ed214f45".from_hex().unwrap(), b"something"); + t.insert(&"c37a43e940dfb5baf581a0b82b351d48305fc885".from_hex().unwrap(), b"something"); + t.insert(&"d2571607e241ecf590ed94b12d87c94babe36db6".from_hex().unwrap(), b"something"); + t.insert(&"f735071cbee190d76b704ce68384fc21e389fbe7".from_hex().unwrap(), b"something"); + t.insert(&"04110d816c380812a427968ece99b1c963dfbce6".from_hex().unwrap(), &[]); + t.insert(&"095e7baea6a6c7c4c2dfeb977efac326af552d87".from_hex().unwrap(), &[]); + t.insert(&"0a517d755cebbf66312b30fff713666a9cb917e0".from_hex().unwrap(), &[]); + t.insert(&"24dd378f51adc67a50e339e8031fe9bd4aafab36".from_hex().unwrap(), &[]); + t.insert(&"293f982d000532a7861ab122bdc4bbfd26bf9030".from_hex().unwrap(), &[]); + t.insert(&"2cf5732f017b0cf1b1f13a1478e10239716bf6b5".from_hex().unwrap(), &[]); + t.insert(&"31c640b92c21a1f1465c91070b4b3b4d6854195f".from_hex().unwrap(), &[]); + t.insert(&"37f998764813b136ddf5a754f34063fd03065e36".from_hex().unwrap(), &[]); + t.insert(&"37fa399a749c121f8a15ce77e3d9f9bec8020d7a".from_hex().unwrap(), &[]); + t.insert(&"4f36659fa632310b6ec438dea4085b522a2dd077".from_hex().unwrap(), &[]); + t.insert(&"62c01474f089b07dae603491675dc5b5748f7049".from_hex().unwrap(), &[]); + t.insert(&"729af7294be595a0efd7d891c9e51f89c07950c7".from_hex().unwrap(), &[]); + t.insert(&"83e3e5a16d3b696a0314b30b2534804dd5e11197".from_hex().unwrap(), &[]); + t.insert(&"8703df2417e0d7c59d063caa9583cb10a4d20532".from_hex().unwrap(), &[]); + t.insert(&"8dffcd74e5b5923512916c6a64b502689cfa65e1".from_hex().unwrap(), &[]); + t.insert(&"95a4d7cccb5204733874fa87285a176fe1e9e240".from_hex().unwrap(), &[]); + t.insert(&"99b2fcba8120bedd048fe79f5262a6690ed38c39".from_hex().unwrap(), &[]); + t.insert(&"a4202b8b8afd5354e3e40a219bdc17f6001bf2cf".from_hex().unwrap(), &[]); + t.insert(&"a94f5374fce5edbc8e2a8697c15331677e6ebf0b".from_hex().unwrap(), &[]); + t.insert(&"a9647f4a0a14042d91dc33c0328030a7157c93ae".from_hex().unwrap(), &[]); + t.insert(&"aa6cffe5185732689c18f37a7f86170cb7304c2a".from_hex().unwrap(), &[]); + t.insert(&"aae4a2e3c51c04606dcb3723456e58f3ed214f45".from_hex().unwrap(), &[]); + t.insert(&"c37a43e940dfb5baf581a0b82b351d48305fc885".from_hex().unwrap(), &[]); + t.insert(&"d2571607e241ecf590ed94b12d87c94babe36db6".from_hex().unwrap(), &[]); + t.insert(&"f735071cbee190d76b704ce68384fc21e389fbe7".from_hex().unwrap(), &[]); + assert_eq!(*t.root(), H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap()); } #[test] diff --git a/util/src/triehash.rs b/util/src/triehash.rs index 54ea6f702..f5f4e2123 100644 --- a/util/src/triehash.rs +++ b/util/src/triehash.rs @@ -46,7 +46,8 @@ pub fn ordered_trie_root(input: Vec>) -> H256 { // optimize it later .into_iter() .enumerate() - .fold(BTreeMap::new(), | mut acc, (i, vec) | { acc.insert(rlp::encode(&i).to_vec(), vec); acc }) + .map(|(i, vec)| (rlp::encode(&i).to_vec(), vec)) + .collect::>() // then move them to a vector .into_iter() .map(|(k, v)| (as_nibbles(&k), v) ) @@ -78,10 +79,7 @@ pub fn trie_root(input: Vec<(Vec, Vec)>) -> H256 { let gen_input = input // first put elements into btree to sort them and to remove duplicates .into_iter() - .fold(BTreeMap::new(), | mut acc, (k, v) | { - acc.insert(k, v); - acc - }) + .collect::>() // then move them to a vector .into_iter() .map(|(k, v)| (as_nibbles(&k), v) ) @@ -97,7 +95,7 @@ pub fn trie_root(input: Vec<(Vec, Vec)>) -> H256 { /// use std::str::FromStr; /// use util::triehash::*; /// use util::hash::*; -/// +/// /// fn main() { /// let v = vec![ /// (From::from("doe"), From::from("reindeer")), @@ -113,10 +111,8 @@ pub fn sec_trie_root(input: Vec<(Vec, Vec)>) -> H256 { let gen_input = input // first put elements into btree to sort them and to remove duplicates .into_iter() - .fold(BTreeMap::new(), | mut acc, (k, v) | { - acc.insert(k.sha3().to_vec(), v); - acc - }) + .map(|(k, v)| (k.sha3().to_vec(), v)) + .collect::>() // then move them to a vector .into_iter() .map(|(k, v)| (as_nibbles(&k), v) ) @@ -324,10 +320,16 @@ fn test_hex_prefix_encode() { #[cfg(test)] mod tests { - extern crate json_tests; - use self::json_tests::*; - use hash::*; - use triehash::*; + use std::str::FromStr; + use hash::H256; + use super::trie_root; + + #[test] + fn simple_test() { + assert_eq!(trie_root(vec![ + (b"A".to_vec(), b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_vec()) + ]), H256::from_str("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab").unwrap()); + } #[test] fn test_triehash_out_of_order() { @@ -343,11 +345,4 @@ mod tests { ])); } - #[test] - fn test_triehash_json() { - execute_tests_from_directory::("json-tests/json/trie/*.json", &mut | file, input, output | { - println!("file: {}, output: {:?}", file, output); - assert_eq!(trie_root(input), H256::from_slice(&output)); - }); - } } From c26cfc1c5ab0223ec84ee2b6f8f9e231fe51fa2b Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 5 Jul 2016 17:50:46 +0200 Subject: [PATCH 16/21] Silent running operating modes (#1477) * Command=line options. * Keep alive for the eth protocol. * Wire up final pieces. * No network when dark. * Passive and dark mode work. * Ensure all RPCs keep alive. * Fix tests. * Fix minor bug. * Minor whitespace. * Split out some of the sleep-state. * Fix help text. --- ethcore/src/client/client.rs | 94 ++++++++++++++++++++++++-- ethcore/src/client/config.rs | 20 ++++++ ethcore/src/client/mod.rs | 7 +- parity/cli.rs | 17 ++++- parity/configuration.rs | 13 +++- parity/main.rs | 11 ++- parity/rpc_apis.rs | 2 +- rpc/src/v1/impls/eth.rs | 48 +++++++++++++ rpc/src/v1/impls/eth_filter.rs | 12 ++++ rpc/src/v1/impls/eth_signing.rs | 16 +++++ rpc/src/v1/impls/ethcore.rs | 21 ++++++ rpc/src/v1/impls/ethcore_set.rs | 33 +++++++-- rpc/src/v1/impls/personal.rs | 11 +++ rpc/src/v1/impls/personal_signer.rs | 9 +++ rpc/src/v1/impls/traces.rs | 11 +++ rpc/src/v1/tests/mocked/ethcore_set.rs | 25 +++++-- 16 files changed, 326 insertions(+), 24 deletions(-) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 6f3249f4c..f1c260970 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -17,7 +17,8 @@ //! Blockchain database client. use std::path::PathBuf; -use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; +use std::time::Instant; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}; use util::*; use util::panics::*; use views::BlockView; @@ -38,7 +39,7 @@ use filter::Filter; use log_entry::LocalizedLogEntry; use block_queue::{BlockQueue, BlockQueueInfo}; use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute}; -use client::{BlockID, TransactionID, UncleID, TraceId, ClientConfig, DatabaseCompactionProfile, +use client::{BlockID, TransactionID, UncleID, TraceId, Mode, ClientConfig, DatabaseCompactionProfile, BlockChainClient, MiningBlockChainClient, TraceFilter, CallAnalytics, TransactionImportError, BlockImportError, TransactionImportResult}; use client::Error as ClientError; @@ -54,6 +55,7 @@ use evm::Factory as EvmFactory; use miner::{Miner, MinerService, AccountDetails}; const MAX_TX_QUEUE_SIZE: usize = 4096; +const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2; impl fmt::Display for BlockChainInfo { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -83,9 +85,24 @@ impl ClientReport { } } +struct SleepState { + last_activity: Option, + last_autosleep: Option, +} + +impl SleepState { + fn new(awake: bool) -> Self { + SleepState { + last_activity: match awake { false => None, true => Some(Instant::now()) }, + last_autosleep: match awake { false => Some(Instant::now()), true => None }, + } + } +} + /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue. /// Call `import_block()` to import a block asynchronously; `flush_queue()` flushes the queue. pub struct Client { + mode: Mode, chain: Arc, tracedb: Arc>, engine: Arc>, @@ -98,6 +115,8 @@ pub struct Client { vm_factory: Arc, trie_factory: TrieFactory, miner: Arc, + sleep_state: Mutex, + liveness: AtomicBool, io_channel: IoChannel, queue_transactions: AtomicUsize, } @@ -134,9 +153,8 @@ impl Client { spec: Spec, path: &Path, miner: Arc, - message_channel: IoChannel) - -> Result, ClientError> - { + message_channel: IoChannel + ) -> Result, ClientError> { let path = get_db_path(path, config.pruning, spec.genesis_header().hash()); let gb = spec.genesis_block(); let chain = Arc::new(BlockChain::new(config.blockchain, &gb, &path)); @@ -167,7 +185,11 @@ impl Client { let panic_handler = PanicHandler::new_in_arc(); panic_handler.forward_from(&block_queue); + let awake = match config.mode { Mode::Dark(..) => false, _ => true }; let client = Client { + sleep_state: Mutex::new(SleepState::new(awake)), + liveness: AtomicBool::new(awake), + mode: config.mode, chain: chain, tracedb: tracedb, engine: engine, @@ -183,7 +205,6 @@ impl Client { io_channel: message_channel, queue_transactions: AtomicUsize::new(0), }; - Ok(Arc::new(client)) } @@ -449,9 +470,41 @@ impl Client { } /// Tick the client. + // TODO: manage by real events. pub fn tick(&self) { self.chain.collect_garbage(); self.block_queue.collect_garbage(); + + match self.mode { + Mode::Dark(timeout) => { + let mut ss = self.sleep_state.lock().unwrap(); + if let Some(t) = ss.last_activity { + if Instant::now() > t + timeout { + self.sleep(); + ss.last_activity = None; + } + } + } + Mode::Passive(timeout, wakeup_after) => { + let mut ss = self.sleep_state.lock().unwrap(); + let now = Instant::now(); + if let Some(t) = ss.last_activity { + if now > t + timeout { + self.sleep(); + ss.last_activity = None; + ss.last_autosleep = Some(now); + } + } + if let Some(t) = ss.last_autosleep { + if now > t + wakeup_after { + self.wake_up(); + ss.last_activity = Some(now); + ss.last_autosleep = None; + } + } + } + _ => {} + } } /// Set up the cache behaviour. @@ -487,6 +540,29 @@ impl Client { }) } } + + fn wake_up(&self) { + if !self.liveness.load(AtomicOrdering::Relaxed) { + self.liveness.store(true, AtomicOrdering::Relaxed); + self.io_channel.send(NetworkIoMessage::User(SyncMessage::StartNetwork)).unwrap(); + trace!(target: "mode", "wake_up: Waking."); + } + } + + fn sleep(&self) { + if self.liveness.load(AtomicOrdering::Relaxed) { + // only sleep if the import queue is mostly empty. + if self.queue_info().total_queue_size() <= MAX_QUEUE_SIZE_TO_SLEEP_ON { + self.liveness.store(false, AtomicOrdering::Relaxed); + self.io_channel.send(NetworkIoMessage::User(SyncMessage::StopNetwork)).unwrap(); + trace!(target: "mode", "sleep: Sleeping."); + } else { + trace!(target: "mode", "sleep: Cannot sleep - syncing ongoing."); + // TODO: Consider uncommenting. + //*self.last_activity.lock().unwrap() = Some(Instant::now()); + } + } + } } impl BlockChainClient for Client { @@ -528,6 +604,12 @@ impl BlockChainClient for Client { ret } + fn keep_alive(&self) { + if self.mode != Mode::Active { + self.wake_up(); + (*self.sleep_state.lock().unwrap()).last_activity = Some(Instant::now()); + } + } fn block_header(&self, id: BlockID) -> Option { Self::block_hash(&self.chain, id).and_then(|hash| self.chain.block(&hash).map(|bytes| BlockView::new(&bytes).rlp().at(0).as_raw().to_vec())) diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 6cb34c151..1010ce656 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +pub use std::time::Duration; pub use block_queue::BlockQueueConfig; pub use blockchain::Config as BlockChainConfig; pub use trace::{Config as TraceConfig, Switch}; @@ -35,6 +36,23 @@ impl Default for DatabaseCompactionProfile { fn default() -> Self { DatabaseCompactionProfile::Default } } +/// Operating mode for the client. +#[derive(Debug, Eq, PartialEq)] +pub enum Mode { + /// Always on. + Active, + /// Goes offline after RLP is inactive for some (given) time, but + /// comes back online after a while of inactivity. + Passive(Duration, Duration), + /// Goes offline after RLP is inactive for some (given) time and + /// stays inactive. + Dark(Duration), +} + +impl Default for Mode { + fn default() -> Self { Mode::Active } +} + /// Client configuration. Includes configs for all sub-systems. #[derive(Debug, Default)] pub struct ClientConfig { @@ -56,6 +74,8 @@ pub struct ClientConfig { pub db_cache_size: Option, /// State db compaction profile pub db_compaction: DatabaseCompactionProfile, + /// Operating mode + pub mode: Mode, /// Type of block verifier used by client. pub verifier_type: VerifierType, } diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index c09da450d..b81efe43a 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -23,7 +23,7 @@ mod test_client; mod trace; pub use self::client::*; -pub use self::config::{ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType}; +pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockQueueConfig, BlockChainConfig, Switch, VMType}; pub use self::error::Error; pub use types::ids::*; pub use self::test_client::{TestBlockChainClient, EachBlockWith}; @@ -63,6 +63,11 @@ pub struct CallAnalytics { /// Blockchain database client. Owns and manages a blockchain and a block queue. pub trait BlockChainClient : Sync + Send { + + /// Should be called by any external-facing interface when actively using the client. + /// To minimise chatter, there's no need to call more than once every 30s. + fn keep_alive(&self) {} + /// Get raw block header data by block id. fn block_header(&self, id: BlockID) -> Option; diff --git a/parity/cli.rs b/parity/cli.rs index 17954eb2a..4c5d4cbc9 100644 --- a/parity/cli.rs +++ b/parity/cli.rs @@ -32,7 +32,19 @@ Usage: parity [options] parity ui [options] -Protocol Options: +Operating Options: + --mode MODE Set the operating mode. MODE can be one of: + active - Parity continuously syncs the chain. + passive - Parity syncs initially, then sleeps and + wakes regularly to resync. + dark - Parity syncs only when an external interface + is active. [default: active]. + --mode-timeout SECS Specify the number of seconds before inactivity + timeout occurs when mode is dark or passive + [default: 300]. + --mode-alarm SECS Specify the number of seconds before auto sleep + reawake timeout occurs when mode is passive + [default: 3600]. --chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, or testnet @@ -269,6 +281,9 @@ pub struct Args { pub arg_pid_file: String, pub arg_file: Option, pub arg_path: Vec, + pub flag_mode: String, + pub flag_mode_timeout: u64, + pub flag_mode_alarm: u64, pub flag_chain: String, pub flag_db_path: String, pub flag_identity: String, diff --git a/parity/configuration.rs b/parity/configuration.rs index 14932423c..0a1a9c0a7 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -28,7 +28,7 @@ use util::*; use util::log::Colour::*; use ethcore::account_provider::AccountProvider; use util::network_settings::NetworkSettings; -use ethcore::client::{append_path, get_db_path, ClientConfig, DatabaseCompactionProfile, Switch, VMType}; +use ethcore::client::{append_path, get_db_path, Mode, ClientConfig, DatabaseCompactionProfile, Switch, VMType}; use ethcore::miner::{MinerOptions, PendingSet}; use ethcore::ethereum; use ethcore::spec::Spec; @@ -61,6 +61,15 @@ impl Configuration { } } + pub fn mode(&self) -> Mode { + match &(self.args.flag_mode[..]) { + "active" => Mode::Active, + "passive" => Mode::Passive(Duration::from_secs(self.args.flag_mode_timeout), Duration::from_secs(self.args.flag_mode_alarm)), + "dark" => Mode::Dark(Duration::from_secs(self.args.flag_mode_timeout)), + _ => die!("{}: Invalid address for --mode. Must be one of active, passive or dark.", self.args.flag_mode), + } + } + fn net_port(&self) -> u16 { self.args.flag_port } @@ -302,6 +311,8 @@ impl Configuration { pub fn client_config(&self, spec: &Spec) -> ClientConfig { let mut client_config = ClientConfig::default(); + client_config.mode = self.mode(); + match self.args.flag_cache { Some(mb) => { client_config.blockchain.max_cache_size = mb * 1024 * 1024; diff --git a/parity/main.rs b/parity/main.rs index b7a106181..0b26054d9 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -82,7 +82,7 @@ use rustc_serialize::hex::FromHex; use ctrlc::CtrlC; use util::{H256, ToPretty, NetworkConfiguration, PayloadInfo, Bytes, UtilError, paint, Colour, version}; use util::panics::{MayPanic, ForwardPanic, PanicHandler}; -use ethcore::client::{BlockID, BlockChainClient, ClientConfig, get_db_path, BlockImportError}; +use ethcore::client::{Mode, BlockID, BlockChainClient, ClientConfig, get_db_path, BlockImportError}; use ethcore::error::{ImportError}; use ethcore::service::ClientService; use ethcore::spec::Spec; @@ -213,7 +213,12 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) // Build client let mut service = ClientService::start( - client_config, spec, net_settings, Path::new(&conf.path()), miner.clone(), !conf.args.flag_no_network + client_config, + spec, + net_settings, + Path::new(&conf.path()), + miner.clone(), + match conf.mode() { Mode::Dark(..) => false, _ => !conf.args.flag_no_network } ).unwrap_or_else(|e| die_with_error("Client", e)); panic_handler.forward_from(&service); @@ -282,7 +287,7 @@ fn execute_client(conf: Configuration, spec: Spec, client_config: ClientConfig) }); // Register IO handler - let io_handler = Arc::new(ClientIoHandler { + let io_handler = Arc::new(ClientIoHandler { client: service.client(), info: Informant::new(conf.have_color()), sync: sync.clone(), diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index c0daaa926..0187f4058 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -166,7 +166,7 @@ pub fn setup_rpc(server: T, deps: Arc, apis: ApiSet server.add_delegate(EthcoreClient::new(&deps.client, &deps.miner, deps.logger.clone(), deps.settings.clone(), queue).to_delegate()) }, Api::EthcoreSet => { - server.add_delegate(EthcoreSetClient::new(&deps.miner, &deps.net_service).to_delegate()) + server.add_delegate(EthcoreSetClient::new(&deps.client, &deps.miner, &deps.net_service).to_delegate()) }, Api::Traces => { server.add_delegate(TracesClient::new(&deps.client, &deps.miner).to_delegate()) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index f2325de93..4ccefc0bf 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -241,6 +241,19 @@ fn no_author_err() -> Error { } } +impl EthClient where + C: MiningBlockChainClient + 'static, + S: SyncProvider + 'static, + M: MinerService + 'static, + EM: ExternalMinerService + 'static { + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } +} + impl Eth for EthClient where C: MiningBlockChainClient + 'static, S: SyncProvider + 'static, @@ -248,6 +261,7 @@ impl Eth for EthClient where EM: ExternalMinerService + 'static { fn protocol_version(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => Ok(Value::String(format!("{}", take_weak!(self.sync).status().protocol_version).to_owned())), _ => Err(Error::invalid_params()) @@ -255,6 +269,7 @@ impl Eth for EthClient where } fn syncing(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let status = take_weak!(self.sync).status(); @@ -281,6 +296,7 @@ impl Eth for EthClient where } fn author(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => to_value(&take_weak!(self.miner).author()), _ => Err(Error::invalid_params()), @@ -288,6 +304,7 @@ impl Eth for EthClient where } fn is_mining(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => to_value(&self.external_miner.is_mining()), _ => Err(Error::invalid_params()) @@ -295,6 +312,7 @@ impl Eth for EthClient where } fn hashrate(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => to_value(&self.external_miner.hashrate()), _ => Err(Error::invalid_params()) @@ -302,6 +320,7 @@ impl Eth for EthClient where } fn gas_price(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); @@ -312,11 +331,13 @@ impl Eth for EthClient where } fn accounts(&self, _: Params) -> Result { + try!(self.active()); let store = take_weak!(self.accounts); to_value(&store.accounts()) } fn block_number(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => to_value(&U256::from(take_weak!(self.client).chain_info().best_block_number)), _ => Err(Error::invalid_params()) @@ -324,6 +345,7 @@ impl Eth for EthClient where } fn balance(&self, params: Params) -> Result { + try!(self.active()); from_params_default_second(params) .and_then(|(address, block_number,)| match block_number { BlockNumber::Pending => to_value(&take_weak!(self.miner).balance(take_weak!(self.client).deref(), &address)), @@ -332,6 +354,7 @@ impl Eth for EthClient where } fn storage_at(&self, params: Params) -> Result { + try!(self.active()); from_params_default_third::(params) .and_then(|(address, position, block_number,)| match block_number { BlockNumber::Pending => to_value(&U256::from(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)))), @@ -343,6 +366,7 @@ impl Eth for EthClient where } fn transaction_count(&self, params: Params) -> Result { + try!(self.active()); from_params_default_second(params) .and_then(|(address, block_number,)| match block_number { BlockNumber::Pending => to_value(&take_weak!(self.miner).nonce(take_weak!(self.client).deref(), &address)), @@ -351,6 +375,7 @@ impl Eth for EthClient where } fn block_transaction_count_by_hash(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256,)>(params) .and_then(|(hash,)| // match take_weak!(self.client).block(BlockID::Hash(hash)) @@ -358,6 +383,7 @@ impl Eth for EthClient where } fn block_transaction_count_by_number(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber,)>(params) .and_then(|(block_number,)| match block_number { BlockNumber::Pending => to_value( @@ -369,6 +395,7 @@ impl Eth for EthClient where } fn block_uncles_count_by_hash(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256,)>(params) .and_then(|(hash,)| take_weak!(self.client).block(BlockID::Hash(hash)) @@ -376,6 +403,7 @@ impl Eth for EthClient where } fn block_uncles_count_by_number(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber,)>(params) .and_then(|(block_number,)| match block_number { BlockNumber::Pending => to_value(&U256::from(0)), @@ -385,6 +413,7 @@ impl Eth for EthClient where } fn code_at(&self, params: Params) -> Result { + try!(self.active()); from_params_default_second(params) .and_then(|(address, block_number,)| match block_number { BlockNumber::Pending => to_value(&take_weak!(self.miner).code(take_weak!(self.client).deref(), &address).map_or_else(Bytes::default, Bytes::new)), @@ -394,16 +423,19 @@ impl Eth for EthClient where } fn block_by_hash(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256, bool)>(params) .and_then(|(hash, include_txs)| self.block(BlockID::Hash(hash), include_txs)) } fn block_by_number(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber, bool)>(params) .and_then(|(number, include_txs)| self.block(number.into(), include_txs)) } fn transaction_by_hash(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256,)>(params) .and_then(|(hash,)| { let miner = take_weak!(self.miner); @@ -415,16 +447,19 @@ impl Eth for EthClient where } fn transaction_by_block_hash_and_index(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256, Index)>(params) .and_then(|(hash, index)| self.transaction(TransactionID::Location(BlockID::Hash(hash), index.value()))) } fn transaction_by_block_number_and_index(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber, Index)>(params) .and_then(|(number, index)| self.transaction(TransactionID::Location(number.into(), index.value()))) } fn transaction_receipt(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256,)>(params) .and_then(|(hash,)| { let miner = take_weak!(self.miner); @@ -440,16 +475,19 @@ impl Eth for EthClient where } fn uncle_by_block_hash_and_index(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256, Index)>(params) .and_then(|(hash, index)| self.uncle(UncleID { block: BlockID::Hash(hash), position: index.value() })) } fn uncle_by_block_number_and_index(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber, Index)>(params) .and_then(|(number, index)| self.uncle(UncleID { block: number.into(), position: index.value() })) } fn compilers(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => to_value(&vec![] as &Vec), _ => Err(Error::invalid_params()) @@ -457,6 +495,7 @@ impl Eth for EthClient where } fn logs(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Filter,)>(params) .and_then(|(filter,)| { let include_pending = filter.to_block == Some(BlockNumber::Pending); @@ -476,6 +515,7 @@ impl Eth for EthClient where } fn work(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let client = take_weak!(self.client); @@ -512,6 +552,7 @@ impl Eth for EthClient where } fn submit_work(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| { trace!(target: "miner", "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash); let miner = take_weak!(self.miner); @@ -523,6 +564,7 @@ impl Eth for EthClient where } fn submit_hashrate(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256, H256)>(params).and_then(|(rate, id)| { self.external_miner.submit_hashrate(rate, id); to_value(&true) @@ -530,6 +572,7 @@ impl Eth for EthClient where } fn send_raw_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Bytes, )>(params) .and_then(|(raw_transaction, )| { let raw_transaction = raw_transaction.to_vec(); @@ -541,6 +584,7 @@ impl Eth for EthClient where } fn call(&self, params: Params) -> Result { + try!(self.active()); trace!(target: "jsonrpc", "call: {:?}", params); from_params_default_second(params) .and_then(|(request, block_number,)| { @@ -555,6 +599,7 @@ impl Eth for EthClient where } fn estimate_gas(&self, params: Params) -> Result { + try!(self.active()); from_params_default_second(params) .and_then(|(request, block_number,)| { let signed = try!(self.sign_call(request)); @@ -568,14 +613,17 @@ impl Eth for EthClient where } fn compile_lll(&self, _: Params) -> Result { + try!(self.active()); rpc_unimplemented!() } fn compile_serpent(&self, _: Params) -> Result { + try!(self.active()); rpc_unimplemented!() } fn compile_solidity(&self, _: Params) -> Result { + try!(self.active()); rpc_unimplemented!() } } diff --git a/rpc/src/v1/impls/eth_filter.rs b/rpc/src/v1/impls/eth_filter.rs index b34a4f703..40ced7187 100644 --- a/rpc/src/v1/impls/eth_filter.rs +++ b/rpc/src/v1/impls/eth_filter.rs @@ -52,6 +52,12 @@ impl EthFilterClient where polls: Mutex::new(PollManager::new()), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl EthFilter for EthFilterClient where @@ -59,6 +65,7 @@ impl EthFilter for EthFilterClient where M: MinerService + 'static { fn new_filter(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Filter,)>(params) .and_then(|(filter,)| { let mut polls = self.polls.lock().unwrap(); @@ -69,6 +76,7 @@ impl EthFilter for EthFilterClient where } fn new_block_filter(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let mut polls = self.polls.lock().unwrap(); @@ -80,6 +88,7 @@ impl EthFilter for EthFilterClient where } fn new_pending_transaction_filter(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => { let mut polls = self.polls.lock().unwrap(); @@ -93,6 +102,7 @@ impl EthFilter for EthFilterClient where } fn filter_changes(&self, params: Params) -> Result { + try!(self.active()); let client = take_weak!(self.client); from_params::<(Index,)>(params) .and_then(|(index,)| { @@ -181,6 +191,7 @@ impl EthFilter for EthFilterClient where } fn filter_logs(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Index,)>(params) .and_then(|(index,)| { let mut polls = self.polls.lock().unwrap(); @@ -206,6 +217,7 @@ impl EthFilter for EthFilterClient where } fn uninstall_filter(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Index,)>(params) .and_then(|(index,)| { self.polls.lock().unwrap().remove_poll(&index.value()); diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs index fa4330f46..6dd01bdf1 100644 --- a/rpc/src/v1/impls/eth_signing.rs +++ b/rpc/src/v1/impls/eth_signing.rs @@ -61,6 +61,12 @@ impl EthSigningQueueClient where C: MiningBlockChainClient, M: Miner miner: Arc::downgrade(miner), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl EthSigning for EthSigningQueueClient @@ -68,12 +74,14 @@ impl EthSigning for EthSigningQueueClient { fn sign(&self, _params: Params) -> Result { + try!(self.active()); warn!("Invoking eth_sign is not yet supported with signer enabled."); // TODO [ToDr] Implement sign when rest of the signing queue is ready. rpc_unimplemented!() } fn send_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(TransactionRequest, )>(params) .and_then(|(mut request, )| { let accounts = take_weak!(self.accounts); @@ -118,6 +126,12 @@ impl EthSigningUnsafeClient where accounts: Arc::downgrade(accounts), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl EthSigning for EthSigningUnsafeClient where @@ -125,12 +139,14 @@ impl EthSigning for EthSigningUnsafeClient where M: MinerService + 'static { fn sign(&self, params: Params) -> Result { + try!(self.active()); 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 { + try!(self.active()); from_params::<(TransactionRequest, )>(params) .and_then(|(request, )| { let sender = request.from; diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 3a58d8672..fe2313c30 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -52,56 +52,74 @@ impl EthcoreClient where C: MiningBlockChainClient, M: MinerService confirmations_queue: queue, } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl Ethcore for EthcoreClient where M: MinerService + 'static, C: MiningBlockChainClient + 'static { fn transactions_limit(&self, _: Params) -> Result { + try!(self.active()); to_value(&take_weak!(self.miner).transactions_limit()) } fn min_gas_price(&self, _: Params) -> Result { + try!(self.active()); to_value(&take_weak!(self.miner).minimal_gas_price()) } fn extra_data(&self, _: Params) -> Result { + try!(self.active()); to_value(&Bytes::new(take_weak!(self.miner).extra_data())) } fn gas_floor_target(&self, _: Params) -> Result { + try!(self.active()); to_value(&take_weak!(self.miner).gas_floor_target()) } fn gas_ceil_target(&self, _: Params) -> Result { + try!(self.active()); to_value(&take_weak!(self.miner).gas_ceil_target()) } fn dev_logs(&self, _params: Params) -> Result { + try!(self.active()); let logs = self.logger.logs(); to_value(&logs.deref().as_slice()) } fn dev_logs_levels(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.logger.levels()) } fn net_chain(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.settings.chain) } fn net_max_peers(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.settings.max_peers) } fn net_port(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.settings.network_port) } fn node_name(&self, _params: Params) -> Result { + try!(self.active()); to_value(&self.settings.name) } fn rpc_settings(&self, _params: Params) -> Result { + try!(self.active()); let mut map = BTreeMap::new(); map.insert("enabled".to_owned(), Value::Bool(self.settings.rpc_enabled)); map.insert("interface".to_owned(), Value::String(self.settings.rpc_interface.clone())); @@ -110,6 +128,7 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static, C: M } fn default_extra_data(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => to_value(&Bytes::new(version_data())), _ => Err(Error::invalid_params()), @@ -117,6 +136,7 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static, C: M } fn gas_price_statistics(&self, params: Params) -> Result { + try!(self.active()); match params { Params::None => match take_weak!(self.client).gas_price_statistics(100, 8) { Ok(stats) => to_value(&stats @@ -130,6 +150,7 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static, C: M } fn unsigned_transactions_count(&self, _params: Params) -> Result { + try!(self.active()); match self.confirmations_queue { None => Err(Error { code: ErrorCode::ServerError(error_codes::SIGNER_DISABLED), diff --git a/rpc/src/v1/impls/ethcore_set.rs b/rpc/src/v1/impls/ethcore_set.rs index baf5bf134..1a41509f7 100644 --- a/rpc/src/v1/impls/ethcore_set.rs +++ b/rpc/src/v1/impls/ethcore_set.rs @@ -20,31 +20,46 @@ use util::network::{NetworkService, NonReservedPeerMode}; use std::sync::{Arc, Weak}; use jsonrpc_core::*; use ethcore::miner::MinerService; +use ethcore::client::MiningBlockChainClient; use ethcore::service::SyncMessage; use v1::traits::EthcoreSet; use v1::types::Bytes; /// Ethcore-specific rpc interface for operations altering the settings. -pub struct EthcoreSetClient where +pub struct EthcoreSetClient where + C: MiningBlockChainClient, M: MinerService { + client: Weak, miner: Weak, net: Weak>, } -impl EthcoreSetClient where M: MinerService { +impl EthcoreSetClient where + C: MiningBlockChainClient, + M: MinerService { /// Creates new `EthcoreSetClient`. - pub fn new(miner: &Arc, net: &Arc>) -> Self { + pub fn new(client: &Arc, miner: &Arc, net: &Arc>) -> Self { EthcoreSetClient { + client: Arc::downgrade(client), miner: Arc::downgrade(miner), net: Arc::downgrade(net), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } -impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { +impl EthcoreSet for EthcoreSetClient where + C: MiningBlockChainClient + 'static, + M: MinerService + 'static { fn set_min_gas_price(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256,)>(params).and_then(|(gas_price,)| { take_weak!(self.miner).set_minimal_gas_price(gas_price); to_value(&true) @@ -52,6 +67,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn set_gas_floor_target(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256,)>(params).and_then(|(target,)| { take_weak!(self.miner).set_gas_floor_target(target); to_value(&true) @@ -59,6 +75,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn set_gas_ceil_target(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256,)>(params).and_then(|(target,)| { take_weak!(self.miner).set_gas_ceil_target(target); to_value(&true) @@ -66,6 +83,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn set_extra_data(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Bytes,)>(params).and_then(|(extra_data,)| { take_weak!(self.miner).set_extra_data(extra_data.to_vec()); to_value(&true) @@ -73,6 +91,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn set_author(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Address,)>(params).and_then(|(author,)| { take_weak!(self.miner).set_author(author); to_value(&true) @@ -80,6 +99,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn set_transactions_limit(&self, params: Params) -> Result { + try!(self.active()); from_params::<(usize,)>(params).and_then(|(limit,)| { take_weak!(self.miner).set_transactions_limit(limit); to_value(&true) @@ -87,6 +107,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn set_tx_gas_limit(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256,)>(params).and_then(|(limit,)| { take_weak!(self.miner).set_tx_gas_limit(limit.into()); to_value(&true) @@ -94,6 +115,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn add_reserved_peer(&self, params: Params) -> Result { + try!(self.active()); from_params::<(String,)>(params).and_then(|(peer,)| { match take_weak!(self.net).add_reserved_peer(&peer) { Ok(()) => to_value(&true), @@ -103,6 +125,7 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn remove_reserved_peer(&self, params: Params) -> Result { + try!(self.active()); from_params::<(String,)>(params).and_then(|(peer,)| { match take_weak!(self.net).remove_reserved_peer(&peer) { Ok(()) => to_value(&true), @@ -112,11 +135,13 @@ impl EthcoreSet for EthcoreSetClient where M: MinerService + 'static { } fn drop_non_reserved_peers(&self, _: Params) -> Result { + try!(self.active()); take_weak!(self.net).set_non_reserved_mode(NonReservedPeerMode::Deny); to_value(&true) } fn accept_non_reserved_peers(&self, _: Params) -> Result { + try!(self.active()); take_weak!(self.net).set_non_reserved_mode(NonReservedPeerMode::Accept); to_value(&true) } diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 38191ed90..94636ae99 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -43,22 +43,31 @@ impl PersonalClient where C: MiningBlockChainClient, M: MinerService signer_port: signer_port, } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl Personal for PersonalClient where C: MiningBlockChainClient, M: MinerService { fn signer_enabled(&self, _: Params) -> Result { + try!(self.active()); self.signer_port .map(|v| to_value(&v)) .unwrap_or_else(|| to_value(&false)) } fn accounts(&self, _: Params) -> Result { + try!(self.active()); let store = take_weak!(self.accounts); to_value(&store.accounts()) } fn new_account(&self, params: Params) -> Result { + try!(self.active()); from_params::<(String, )>(params).and_then( |(pass, )| { let store = take_weak!(self.accounts); @@ -71,6 +80,7 @@ impl Personal for PersonalClient where C: MiningBl } fn unlock_account(&self, params: Params) -> Result { + try!(self.active()); from_params::<(Address, String, u64)>(params).and_then( |(account, account_pass, _)|{ let store = take_weak!(self.accounts); @@ -82,6 +92,7 @@ impl Personal for PersonalClient where C: MiningBl } fn sign_and_send_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(TransactionRequest, String)>(params) .and_then(|(request, password)| { let sender = request.from; diff --git a/rpc/src/v1/impls/personal_signer.rs b/rpc/src/v1/impls/personal_signer.rs index 89f15c787..97749657e 100644 --- a/rpc/src/v1/impls/personal_signer.rs +++ b/rpc/src/v1/impls/personal_signer.rs @@ -46,16 +46,24 @@ impl SignerClient where C: MiningBlockChainClient, miner: Arc::downgrade(miner), } } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl PersonalSigner for SignerClient where C: MiningBlockChainClient, M: MinerService { fn transactions_to_confirm(&self, _params: Params) -> Result { + try!(self.active()); let queue = take_weak!(self.queue); to_value(&queue.requests()) } fn confirm_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256, TransactionModification, String)>(params).and_then( |(id, modification, pass)| { let accounts = take_weak!(self.accounts); @@ -87,6 +95,7 @@ impl PersonalSigner for SignerClient where C: Mini } fn reject_transaction(&self, params: Params) -> Result { + try!(self.active()); from_params::<(U256, )>(params).and_then( |(id, )| { let queue = take_weak!(self.queue); diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index caf549c84..45daa2500 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -55,10 +55,17 @@ impl TracesClient where C: BlockChainClient, M: MinerService { data: request.data.map_or_else(Vec::new, |d| d.to_vec()) }.fake_sign(from)) } + + fn active(&self) -> Result<(), Error> { + // TODO: only call every 30s at most. + take_weak!(self.client).keep_alive(); + Ok(()) + } } impl Traces for TracesClient where C: BlockChainClient + 'static, M: MinerService + 'static { fn filter(&self, params: Params) -> Result { + try!(self.active()); from_params::<(TraceFilter,)>(params) .and_then(|(filter, )| { let client = take_weak!(self.client); @@ -69,6 +76,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: } fn block_traces(&self, params: Params) -> Result { + try!(self.active()); from_params::<(BlockNumber,)>(params) .and_then(|(block_number,)| { let client = take_weak!(self.client); @@ -79,6 +87,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: } fn transaction_traces(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256,)>(params) .and_then(|(transaction_hash,)| { let client = take_weak!(self.client); @@ -89,6 +98,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: } fn trace(&self, params: Params) -> Result { + try!(self.active()); from_params::<(H256, Vec)>(params) .and_then(|(transaction_hash, address)| { let client = take_weak!(self.client); @@ -103,6 +113,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: } fn call(&self, params: Params) -> Result { + try!(self.active()); trace!(target: "jsonrpc", "call: {:?}", params); from_params(params) .and_then(|(request, flags)| { diff --git a/rpc/src/v1/tests/mocked/ethcore_set.rs b/rpc/src/v1/tests/mocked/ethcore_set.rs index 19f52025f..f43233733 100644 --- a/rpc/src/v1/tests/mocked/ethcore_set.rs +++ b/rpc/src/v1/tests/mocked/ethcore_set.rs @@ -20,6 +20,7 @@ use jsonrpc_core::IoHandler; use v1::{EthcoreSet, EthcoreSetClient}; use ethcore::miner::MinerService; use ethcore::service::SyncMessage; +use ethcore::client::TestBlockChainClient; use v1::tests::helpers::TestMinerService; use util::numbers::*; use util::network::{NetworkConfiguration, NetworkService}; @@ -29,20 +30,25 @@ fn miner_service() -> Arc { Arc::new(TestMinerService::default()) } +fn client_service() -> Arc { + Arc::new(TestBlockChainClient::default()) +} + fn network_service() -> Arc> { Arc::new(NetworkService::new(NetworkConfiguration::new()).unwrap()) } -fn ethcore_set_client(miner: &Arc, net: &Arc>) -> EthcoreSetClient { - EthcoreSetClient::new(miner, net) +fn ethcore_set_client(client: &Arc, miner: &Arc, net: &Arc>) -> EthcoreSetClient { + EthcoreSetClient::new(client, miner, net) } #[test] fn rpc_ethcore_set_min_gas_price() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setMinGasPrice", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -50,12 +56,14 @@ fn rpc_ethcore_set_min_gas_price() { assert_eq!(io.handle_request(request), Some(response.to_owned())); assert_eq!(miner.minimal_gas_price(), U256::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); } + #[test] fn rpc_ethcore_set_gas_floor_target() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setGasFloorTarget", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -67,9 +75,10 @@ fn rpc_ethcore_set_gas_floor_target() { #[test] fn rpc_ethcore_set_extra_data() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setExtraData", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -81,9 +90,10 @@ fn rpc_ethcore_set_extra_data() { #[test] fn rpc_ethcore_set_author() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setAuthor", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; @@ -95,9 +105,10 @@ fn rpc_ethcore_set_author() { #[test] fn rpc_ethcore_set_transactions_limit() { let miner = miner_service(); + let client = client_service(); let network = network_service(); let io = IoHandler::new(); - io.add_delegate(ethcore_set_client(&miner, &network).to_delegate()); + io.add_delegate(ethcore_set_client(&client, &miner, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "ethcore_setTransactionsLimit", "params":[10240240], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; From b8b55be0beb2b97e4a5fb775ffcf1e86879b9fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Tue, 5 Jul 2016 11:51:41 -0400 Subject: [PATCH 17/21] Skipping transactions with invalid nonces when pushing to block. (#1545) * Changing some logging levels * Skipping invalid nonce errors --- ethcore/src/miner/miner.rs | 12 +++++++++--- ethcore/src/miner/transaction_queue.rs | 18 +++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 215506607..dac25addf 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -200,17 +200,23 @@ impl Miner { let hash = tx.hash(); match open_block.push_transaction(tx, None) { Err(Error::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, .. })) => { - trace!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash); + debug!(target: "miner", "Skipping adding transaction to block because of gas limit: {:?}", hash); // Exit early if gas left is smaller then min_tx_gas let min_tx_gas: U256 = 21000.into(); // TODO: figure this out properly. if gas_limit - gas_used < min_tx_gas { break; } }, - Err(Error::Transaction(TransactionError::AlreadyImported)) => {} // already have transaction - ignore + // Invalid nonce error can happen only if previous transaction is skipped because of gas limit. + // If there is errornous state of transaction queue it will be fixed when next block is imported. + Err(Error::Execution(ExecutionError::InvalidNonce { .. })) => { + debug!(target: "miner", "Skipping adding transaction to block because of invalid nonce: {:?}", hash); + }, + // already have transaction - ignore + Err(Error::Transaction(TransactionError::AlreadyImported)) => {}, Err(e) => { invalid_transactions.insert(hash); - trace!(target: "miner", + debug!(target: "miner", "Error adding transaction to block: number={}. transaction_hash={:?}, Error: {:?}", block_number, hash, e); }, diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 0e71b1d83..1fbc8774f 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -431,10 +431,10 @@ impl TransactionQueue { pub fn add(&mut self, tx: SignedTransaction, fetch_account: &T, origin: TransactionOrigin) -> Result where T: Fn(&Address) -> AccountDetails { - trace!(target: "miner", "Importing: {:?}", tx.hash()); + trace!(target: "txqueue", "Importing: {:?}", tx.hash()); if tx.gas_price < self.minimal_gas_price && origin != TransactionOrigin::Local { - trace!(target: "miner", + trace!(target: "txqueue", "Dropping transaction below minimal gas price threshold: {:?} (gp: {} < {})", tx.hash(), tx.gas_price, @@ -450,7 +450,7 @@ impl TransactionQueue { try!(tx.check_low_s()); if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit { - trace!(target: "miner", + trace!(target: "txqueue", "Dropping transaction above gas limit: {:?} ({} > min({}, {}))", tx.hash(), tx.gas, @@ -469,7 +469,7 @@ impl TransactionQueue { let cost = vtx.transaction.value + vtx.transaction.gas_price * vtx.transaction.gas; if client_account.balance < cost { - trace!(target: "miner", + trace!(target: "txqueue", "Dropping transaction without sufficient balance: {:?} ({} < {})", vtx.hash(), client_account.balance, @@ -557,7 +557,7 @@ impl TransactionQueue { if k >= current_nonce { self.future.insert(*sender, k, order.update_height(k, current_nonce)); } else { - trace!(target: "miner", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); + trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); // Remove the transaction completely self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`"); } @@ -578,7 +578,7 @@ impl TransactionQueue { if k >= current_nonce { self.future.insert(*sender, k, order.update_height(k, current_nonce)); } else { - trace!(target: "miner", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); + trace!(target: "txqueue", "Removing old transaction: {:?} (nonce: {} < {})", order.hash, k, current_nonce); self.by_hash.remove(&order.hash).expect("All transactions in `future` are also in `by_hash`"); } } @@ -666,7 +666,7 @@ impl TransactionQueue { if self.by_hash.get(&tx.hash()).is_some() { // Transaction is already imported. - trace!(target: "miner", "Dropping already imported transaction: {:?}", tx.hash()); + trace!(target: "txqueue", "Dropping already imported transaction: {:?}", tx.hash()); return Err(TransactionError::AlreadyImported); } @@ -683,7 +683,7 @@ impl TransactionQueue { // nonce height would result in overflow. if nonce < state_nonce { // Droping transaction - trace!(target: "miner", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, next_nonce); + trace!(target: "txqueue", "Dropping old transaction: {:?} (nonce: {} < {})", tx.hash(), nonce, next_nonce); return Err(TransactionError::Old); } else if nonce > next_nonce { // We have a gap - put to future. @@ -719,7 +719,7 @@ impl TransactionQueue { // Trigger error if the transaction we are importing was removed. try!(check_if_removed(&address, &nonce, removed)); - trace!(target: "miner", "status: {:?}", self.status()); + trace!(target: "txqueue", "status: {:?}", self.status()); Ok(TransactionImportResult::Current) } From eb6617f9d4bed9ae7aa80b4c54f04e54d7f0022b Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Tue, 5 Jul 2016 19:26:22 +0200 Subject: [PATCH 18/21] mention wiki in README [ci skip] --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d605fa87c..fa20af4d0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ # [Parity](https://ethcore.io/parity.html) ### Fast, light, and robust Ethereum implementation -[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/trogdoro/xiki][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url] +[![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Join the chat at https://gitter.im/ethcore/parity][gitter-image]][gitter-url] [![GPLv3][license-image]][license-url] + +[Internal Documentation][doc-url] + +Be sure to check out [our wiki][wiki-url] for more information. [travis-image]: https://travis-ci.org/ethcore/parity.svg?branch=master [travis-url]: https://travis-ci.org/ethcore/parity @@ -11,8 +15,8 @@ [gitter-url]: https://gitter.im/ethcore/parity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge [license-image]: https://img.shields.io/badge/license-GPL%20v3-green.svg [license-url]: http://www.gnu.org/licenses/gpl-3.0.en.html - -[Internal Documentation](http://ethcore.github.io/parity/ethcore/index.html) +[doc-url]: http://ethcore.github.io/parity/ethcore/index.html +[wiki-url]: https://github.com/ethcore/parity/wiki ---- From bcb63bce127d4ddb1ea398ebedc29caeba73b266 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 6 Jul 2016 11:23:29 +0200 Subject: [PATCH 19/21] serde is no longer util dependency (#1534) * removed old json-tests * simplify folds in triehash.rs * removed unused json_aid * removed unused squeeze.rs * json branching tests for trie * removing todos from util * separated UsingQueue and Table * further cleanup, removing unused code * serde serialization of hash moved to rpc module * uint wrapper for rpc in progress * serialization of uint moved to rpc module * updated eth-secp256k1 * updated igd, serde is no longer dependency of util * loading trie consensus tests * renamed aliases in rpc imports --- Cargo.lock | 92 ++------ ethcore/src/account.rs | 2 +- ethcore/src/header.rs | 10 +- ethcore/src/miner/miner.rs | 1 + ethcore/src/miner/transaction_queue.rs | 2 +- rpc/src/v1/helpers/mod.rs | 4 +- rpc/src/v1/helpers/requests.rs | 64 ++++++ rpc/src/v1/helpers/signing_queue.rs | 15 +- rpc/src/v1/impls/eth.rs | 208 ++++++++++-------- rpc/src/v1/impls/eth_filter.rs | 14 +- rpc/src/v1/impls/eth_signing.rs | 28 ++- rpc/src/v1/impls/ethcore.rs | 12 +- rpc/src/v1/impls/ethcore_set.rs | 15 +- rpc/src/v1/impls/mod.rs | 5 +- rpc/src/v1/impls/personal.rs | 17 +- rpc/src/v1/impls/personal_signer.rs | 16 +- rpc/src/v1/impls/traces.rs | 12 +- rpc/src/v1/impls/web3.rs | 8 +- rpc/src/v1/tests/eth.rs | 3 +- rpc/src/v1/tests/mocked/personal_signer.rs | 4 +- rpc/src/v1/types/block.rs | 28 ++- rpc/src/v1/types/bytes.rs | 6 + rpc/src/v1/types/call_request.rs | 49 +++-- rpc/src/v1/types/filter.rs | 19 +- rpc/src/v1/types/hash.rs | 145 ++++++++++++ rpc/src/v1/types/log.rs | 44 ++-- rpc/src/v1/types/mod.rs.in | 6 +- rpc/src/v1/types/optionals.rs | 81 ------- rpc/src/v1/types/receipt.rs | 47 ++-- rpc/src/v1/types/sync.rs | 4 +- rpc/src/v1/types/trace.rs | 105 +++++---- rpc/src/v1/types/trace_filter.rs | 15 +- rpc/src/v1/types/transaction.rs | 69 +++--- rpc/src/v1/types/transaction_request.rs | 70 ++++-- rpc/src/v1/types/uint.rs | 89 ++++++++ sync/src/lib.rs | 5 +- util/Cargo.toml | 5 +- util/bigint/Cargo.toml | 1 - util/bigint/src/lib.rs | 1 - util/bigint/src/uint.rs | 73 ++---- util/src/bytes.rs | 16 +- util/src/hash.rs | 80 +------ util/src/journaldb/archivedb.rs | 6 +- util/src/journaldb/earlymergedb.rs | 12 +- util/src/journaldb/overlayrecentdb.rs | 2 +- util/src/lib.rs | 6 +- util/src/memorydb.rs | 8 +- util/src/overlaydb.rs | 12 +- util/src/rlp/bytes.rs | 4 +- util/src/sha3.rs | 2 +- util/src/trie/standardmap.rs | 14 +- util/table/Cargo.toml | 6 + util/{src/table.rs => table/src/lib.rs} | 0 util/using_queue/Cargo.toml | 6 + .../using_queue.rs => using_queue/src/lib.rs} | 3 +- 55 files changed, 854 insertions(+), 717 deletions(-) create mode 100644 rpc/src/v1/helpers/requests.rs create mode 100644 rpc/src/v1/types/hash.rs delete mode 100644 rpc/src/v1/types/optionals.rs create mode 100644 rpc/src/v1/types/uint.rs create mode 100644 util/table/Cargo.toml rename util/{src/table.rs => table/src/lib.rs} (100%) create mode 100644 util/using_queue/Cargo.toml rename util/{src/using_queue.rs => using_queue/src/lib.rs} (99%) diff --git a/Cargo.lock b/Cargo.lock index 703935bff..a17bc4413 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,7 +70,6 @@ dependencies = [ "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -149,15 +148,6 @@ dependencies = [ "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "cookie" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "cookie" version = "0.2.4" @@ -225,15 +215,13 @@ dependencies = [ [[package]] name = "eth-secp256k1" version = "0.5.4" -source = "git+https://github.com/ethcore/rust-secp256k1#b6fdd43bbcf6d46adb72a92dd1632a0fc834cbf5" +source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8e55e205a753ff25c" dependencies = [ "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -388,7 +376,7 @@ dependencies = [ "eth-secp256k1 0.5.4 (git+https://github.com/ethcore/rust-secp256k1)", "ethcore-devtools 1.3.0", "heapsize 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "igd 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "igd 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -400,12 +388,13 @@ dependencies = [ "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "sha3 0.1.0", "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "table 0.1.0", "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "using_queue 0.1.0", "vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -505,26 +494,6 @@ name = "httparse" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "hyper" -version = "0.6.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cookie 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "language-tags 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 0.2.38 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "hyper" version = "0.8.1" @@ -578,10 +547,10 @@ dependencies = [ [[package]] name = "igd" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hyper 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", @@ -638,11 +607,6 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "language-tags" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "language-tags" version = "0.2.2" @@ -676,15 +640,6 @@ dependencies = [ "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "mime" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "mime" version = "0.2.0" @@ -1145,14 +1100,6 @@ dependencies = [ "nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "serde" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "serde" version = "0.7.9" @@ -1244,6 +1191,10 @@ dependencies = [ "unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "table" +version = "0.1.0" + [[package]] name = "target_info" version = "0.1.0" @@ -1350,16 +1301,6 @@ name = "unicode-xid" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "url" -version = "0.2.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "url" version = "0.5.9" @@ -1381,20 +1322,15 @@ dependencies = [ "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "using_queue" +version = "0.1.0" + [[package]] name = "utf8-ranges" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "uuid" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "uuid" version = "0.2.1" diff --git a/ethcore/src/account.rs b/ethcore/src/account.rs index 2db4ffcc0..5119b2e33 100644 --- a/ethcore/src/account.rs +++ b/ethcore/src/account.rs @@ -127,7 +127,7 @@ impl Account { SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \ using it will not fail."); - (Filth::Clean, H256::from(db.get(key.bytes()).map_or(U256::zero(), |v| -> U256 {decode(v)}))) + (Filth::Clean, H256::from(db.get(key).map_or(U256::zero(), |v| -> U256 {decode(v)}))) }).1.clone() } diff --git a/ethcore/src/header.rs b/ethcore/src/header.rs index 0941c9ca6..77ec4813b 100644 --- a/ethcore/src/header.rs +++ b/ethcore/src/header.rs @@ -92,10 +92,10 @@ impl PartialEq for Header { impl Default for Header { fn default() -> Self { Header { - parent_hash: ZERO_H256.clone(), + parent_hash: H256::default(), timestamp: 0, number: 0, - author: ZERO_ADDRESS.clone(), + author: Address::default(), transactions_root: SHA3_NULL_RLP, uncles_hash: SHA3_EMPTY_LIST_RLP, @@ -104,10 +104,10 @@ impl Default for Header { state_root: SHA3_NULL_RLP, receipts_root: SHA3_NULL_RLP, log_bloom: ZERO_LOGBLOOM.clone(), - gas_used: ZERO_U256, - gas_limit: ZERO_U256, + gas_used: U256::default(), + gas_limit: U256::default(), - difficulty: ZERO_U256, + difficulty: U256::default(), seal: vec![], hash: RefCell::new(None), bare_hash: RefCell::new(None), diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index dac25addf..3e5d3d445 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -19,6 +19,7 @@ use std::sync::atomic::AtomicBool; use std::time::{Instant, Duration}; use util::*; +use util::using_queue::{UsingQueue, GetAction}; use util::Colour::White; use account_provider::AccountProvider; use views::{BlockView, HeaderView}; diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 1fbc8774f..bdce9f504 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -87,7 +87,7 @@ use std::cmp; use std::collections::{HashMap, BTreeSet}; use util::numbers::{Uint, U256}; use util::hash::{Address, H256}; -use util::table::*; +use util::table::Table; use transaction::*; use error::{Error, TransactionError}; use client::TransactionImportResult; diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index 2acf98bf2..3df96b00e 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -16,8 +16,10 @@ mod poll_manager; mod poll_filter; +mod requests; mod signing_queue; pub use self::poll_manager::PollManager; pub use self::poll_filter::PollFilter; -pub use self::signing_queue::{ConfirmationsQueue, SigningQueue}; +pub use self::requests::{TransactionRequest, TransactionConfirmation, CallRequest}; +pub use self::signing_queue::{ConfirmationsQueue, SigningQueue, QueueEvent}; diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs new file mode 100644 index 000000000..d162774d9 --- /dev/null +++ b/rpc/src/v1/helpers/requests.rs @@ -0,0 +1,64 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use util::{Address, U256}; + +/// Transaction request coming from RPC +#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] +pub struct TransactionRequest { + /// Sender + pub from: Address, + /// Recipient + pub to: Option
, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value of transaction in wei + pub value: Option, + /// Additional data sent with transaction + pub data: Option>, + /// Transaction's nonce + pub nonce: Option, +} + +/// Transaction confirmation waiting in a queue +#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] +pub struct TransactionConfirmation { + /// Id of this confirmation + pub id: U256, + /// TransactionRequest + pub transaction: TransactionRequest, +} + +/// Call request +#[derive(Debug, Default, PartialEq)] +pub struct CallRequest { + /// From + pub from: Option
, + /// To + pub to: Option
, + /// Gas Price + pub gas_price: Option, + /// Gas + pub gas: Option, + /// Value + pub value: Option, + /// Data + pub data: Option>, + /// Nonce + pub nonce: Option, +} diff --git a/rpc/src/v1/helpers/signing_queue.rs b/rpc/src/v1/helpers/signing_queue.rs index 3d7f9e35b..756718000 100644 --- a/rpc/src/v1/helpers/signing_queue.rs +++ b/rpc/src/v1/helpers/signing_queue.rs @@ -18,9 +18,9 @@ use std::thread; use std::time::{Instant, Duration}; use std::sync::{mpsc, Mutex, RwLock, Arc}; use std::collections::HashMap; -use v1::types::{TransactionRequest, TransactionConfirmation}; -use util::U256; use jsonrpc_core; +use util::U256; +use v1::helpers::{TransactionRequest, TransactionConfirmation}; /// Result that can be returned from JSON RPC. pub type RpcResult = Result; @@ -301,10 +301,9 @@ mod test { use std::time::Duration; use std::thread; use std::sync::{Arc, Mutex}; - use util::hash::Address; - use util::numbers::{U256, H256}; - use v1::types::TransactionRequest; - use super::*; + use util::{Address, U256, H256}; + use v1::helpers::{SigningQueue, ConfirmationsQueue, QueueEvent, TransactionRequest}; + use v1::types::H256 as NH256; use jsonrpc_core::to_value; fn request() -> TransactionRequest { @@ -337,10 +336,10 @@ mod test { // Just wait for the other thread to start thread::sleep(Duration::from_millis(100)); } - queue.request_confirmed(id, to_value(&H256::from(1))); + queue.request_confirmed(id, to_value(&NH256::from(H256::from(1)))); // then - assert_eq!(handle.join().expect("Thread should finish nicely"), to_value(&H256::from(1))); + assert_eq!(handle.join().expect("Thread should finish nicely"), to_value(&NH256::from(H256::from(1)))); } #[test] diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 4ccefc0bf..13f54feea 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -38,7 +38,8 @@ use ethcore::log_entry::LogEntry; use ethcore::filter::Filter as EthcoreFilter; 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::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 ethcore::header::Header as BlockHeader; @@ -86,28 +87,28 @@ impl EthClient where let block_view = BlockView::new(&bytes); let view = block_view.header_view(); let block = Block { - hash: OptionalValue::Value(view.sha3()), - parent_hash: view.parent_hash(), - uncles_hash: view.uncles_hash(), - author: view.author(), - miner: view.author(), - state_root: view.state_root(), - transactions_root: view.transactions_root(), - receipts_root: view.receipts_root(), - number: OptionalValue::Value(U256::from(view.number())), - gas_used: view.gas_used(), - gas_limit: view.gas_limit(), - logs_bloom: view.log_bloom(), - timestamp: U256::from(view.timestamp()), - difficulty: view.difficulty(), - total_difficulty: total_difficulty, + hash: Some(view.sha3().into()), + parent_hash: view.parent_hash().into(), + uncles_hash: view.uncles_hash().into(), + author: view.author().into(), + miner: view.author().into(), + state_root: view.state_root().into(), + transactions_root: view.transactions_root().into(), + receipts_root: view.receipts_root().into(), + number: Some(view.number().into()), + gas_used: view.gas_used().into(), + gas_limit: view.gas_limit().into(), + logs_bloom: view.log_bloom().into(), + timestamp: view.timestamp().into(), + difficulty: view.difficulty().into(), + total_difficulty: total_difficulty.into(), seal_fields: view.seal().into_iter().map(|f| decode(&f)).map(Bytes::new).collect(), - uncles: block_view.uncle_hashes(), + uncles: block_view.uncle_hashes().into_iter().map(Into::into).collect(), transactions: { if include_txs { - BlockTransactions::Full(block_view.localized_transactions().into_iter().map(From::from).collect()) + BlockTransactions::Full(block_view.localized_transactions().into_iter().map(Into::into).collect()) } else { - BlockTransactions::Hashes(block_view.transaction_hashes()) + BlockTransactions::Hashes(block_view.transaction_hashes().into_iter().map(Into::into).collect()) } }, extra_data: Bytes::new(view.extra_data()) @@ -127,7 +128,6 @@ impl EthClient where fn uncle(&self, id: UncleID) -> Result { let client = take_weak!(self.client); - let uncle: BlockHeader = match client.uncle(id) { Some(rlp) => decode(&rlp), None => { return Ok(Value::Null); } @@ -138,22 +138,22 @@ impl EthClient where }; let block = Block { - hash: OptionalValue::Value(uncle.hash()), - parent_hash: uncle.parent_hash, - uncles_hash: uncle.uncles_hash, - author: uncle.author, - miner: uncle.author, - state_root: uncle.state_root, - transactions_root: uncle.transactions_root, - number: OptionalValue::Value(U256::from(uncle.number)), - gas_used: uncle.gas_used, - gas_limit: uncle.gas_limit, - logs_bloom: uncle.log_bloom, - timestamp: U256::from(uncle.timestamp), - difficulty: uncle.difficulty, - total_difficulty: uncle.difficulty + parent_difficulty, - receipts_root: uncle.receipts_root, - extra_data: Bytes::new(uncle.extra_data), + hash: Some(uncle.hash().into()), + parent_hash: uncle.parent_hash.into(), + uncles_hash: uncle.uncles_hash.into(), + author: uncle.author.into(), + miner: uncle.author.into(), + state_root: uncle.state_root.into(), + transactions_root: uncle.transactions_root.into(), + number: Some(uncle.number.into()), + gas_used: uncle.gas_used.into(), + gas_limit: uncle.gas_limit.into(), + logs_bloom: uncle.log_bloom.into(), + timestamp: uncle.timestamp.into(), + difficulty: uncle.difficulty.into(), + total_difficulty: (uncle.difficulty + parent_difficulty).into(), + receipts_root: uncle.receipts_root.into(), + extra_data: uncle.extra_data.into(), seal_fields: uncle.seal.into_iter().map(|f| decode(&f)).map(Bytes::new).collect(), uncles: vec![], transactions: BlockTransactions::Hashes(vec![]), @@ -161,7 +161,7 @@ impl EthClient where to_value(&block) } - fn sign_call(&self, request: CallRequest) -> Result { + fn sign_call(&self, request: CRequest) -> Result { let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); let from = request.from.unwrap_or(Address::zero()); Ok(EthTransaction { @@ -186,7 +186,7 @@ pub fn pending_logs(miner: &M, filter: &EthcoreFilter) -> Vec where M: M .filter(|pair| filter.matches(&pair.1)) .map(|pair| { let mut log = Log::from(pair.1); - log.transaction_hash = Some(pair.0); + log.transaction_hash = Some(pair.0.into()); log }) .collect(); @@ -277,15 +277,17 @@ impl Eth for EthClient where SyncState::Idle => SyncStatus::None, SyncState::Waiting | SyncState::Blocks | SyncState::NewBlocks | SyncState::ChainHead => { let current_block = U256::from(take_weak!(self.client).chain_info().best_block_number); + let highest_block = U256::from(status.highest_block_number.unwrap_or(status.start_block_number)); - let info = SyncInfo { - starting_block: U256::from(status.start_block_number), - current_block: current_block, - highest_block: U256::from(status.highest_block_number.unwrap_or(status.start_block_number)) - }; - match info.highest_block > info.current_block + U256::from(6) { - true => SyncStatus::Info(info), - false => SyncStatus::None, + if highest_block > current_block + U256::from(6) { + let info = SyncInfo { + starting_block: status.start_block_number.into(), + current_block: current_block.into(), + highest_block: highest_block.into(), + }; + SyncStatus::Info(info) + } else { + SyncStatus::None } } }; @@ -298,7 +300,7 @@ impl Eth for EthClient where fn author(&self, params: Params) -> Result { try!(self.active()); match params { - Params::None => to_value(&take_weak!(self.miner).author()), + Params::None => to_value(&RpcH160::from(take_weak!(self.miner).author())), _ => Err(Error::invalid_params()), } } @@ -314,7 +316,7 @@ impl Eth for EthClient where fn hashrate(&self, params: Params) -> Result { try!(self.active()); match params { - Params::None => to_value(&self.external_miner.hashrate()), + Params::None => to_value(&RpcU256::from(self.external_miner.hashrate())), _ => Err(Error::invalid_params()) } } @@ -324,7 +326,7 @@ impl Eth for EthClient where match params { Params::None => { let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); - to_value(&default_gas_price(&*client, &*miner)) + to_value(&RpcU256::from(default_gas_price(&*client, &*miner))) } _ => Err(Error::invalid_params()) } @@ -333,13 +335,13 @@ impl Eth for EthClient where fn accounts(&self, _: Params) -> Result { try!(self.active()); let store = take_weak!(self.accounts); - to_value(&store.accounts()) + to_value(&store.accounts().into_iter().map(Into::into).collect::>()) } fn block_number(&self, params: Params) -> Result { try!(self.active()); match params { - Params::None => to_value(&U256::from(take_weak!(self.client).chain_info().best_block_number)), + Params::None => to_value(&RpcU256::from(take_weak!(self.client).chain_info().best_block_number)), _ => Err(Error::invalid_params()) } } @@ -347,39 +349,50 @@ impl Eth for EthClient where fn balance(&self, params: Params) -> Result { try!(self.active()); from_params_default_second(params) - .and_then(|(address, block_number,)| match block_number { - BlockNumber::Pending => to_value(&take_weak!(self.miner).balance(take_weak!(self.client).deref(), &address)), - id => to_value(&try!(take_weak!(self.client).balance(&address, id.into()).ok_or_else(make_unsupported_err))), + .and_then(|(address, block_number,)| { + let address: Address = RpcH160::into(address); + match block_number { + BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).balance(take_weak!(self.client).deref(), &address))), + id => to_value(&RpcU256::from(try!(take_weak!(self.client).balance(&address, id.into()).ok_or_else(make_unsupported_err)))), + } }) } fn storage_at(&self, params: Params) -> Result { try!(self.active()); - from_params_default_third::(params) - .and_then(|(address, position, block_number,)| match block_number { - BlockNumber::Pending => to_value(&U256::from(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)))), - id => match take_weak!(self.client).storage_at(&address, &H256::from(position), id.into()) { - Some(s) => to_value(&U256::from(s)), - None => Err(make_unsupported_err()), // None is only returned on unsupported requests. + from_params_default_third::(params) + .and_then(|(address, position, block_number,)| { + let address: Address = RpcH160::into(address); + let position: U256 = RpcU256::into(position); + match block_number { + BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).storage_at(&*take_weak!(self.client), &address, &H256::from(position)))), + id => match take_weak!(self.client).storage_at(&address, &H256::from(position), id.into()) { + Some(s) => to_value(&RpcU256::from(s)), + None => Err(make_unsupported_err()), // None is only returned on unsupported requests. + } } }) + } fn transaction_count(&self, params: Params) -> Result { try!(self.active()); from_params_default_second(params) - .and_then(|(address, block_number,)| match block_number { - BlockNumber::Pending => to_value(&take_weak!(self.miner).nonce(take_weak!(self.client).deref(), &address)), - id => to_value(&take_weak!(self.client).nonce(&address, id.into())), + .and_then(|(address, block_number,)| { + let address: Address = RpcH160::into(address); + match block_number { + BlockNumber::Pending => to_value(&RpcU256::from(take_weak!(self.miner).nonce(take_weak!(self.client).deref(), &address))), + id => to_value(&take_weak!(self.client).nonce(&address, id.into()).map(RpcU256::from)), + } }) } fn block_transaction_count_by_hash(&self, params: Params) -> Result { try!(self.active()); - from_params::<(H256,)>(params) + from_params::<(RpcH256,)>(params) .and_then(|(hash,)| // match - take_weak!(self.client).block(BlockID::Hash(hash)) - .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).transactions_count())))) + take_weak!(self.client).block(BlockID::Hash(hash.into())) + .map_or(Ok(Value::Null), |bytes| to_value(&RpcU256::from(BlockView::new(&bytes).transactions_count())))) } fn block_transaction_count_by_number(&self, params: Params) -> Result { @@ -387,45 +400,48 @@ impl Eth for EthClient where from_params::<(BlockNumber,)>(params) .and_then(|(block_number,)| match block_number { BlockNumber::Pending => to_value( - &U256::from(take_weak!(self.miner).status().transactions_in_pending_block) + &RpcU256::from(take_weak!(self.miner).status().transactions_in_pending_block) ), _ => take_weak!(self.client).block(block_number.into()) - .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).transactions_count()))) + .map_or(Ok(Value::Null), |bytes| to_value(&RpcU256::from(BlockView::new(&bytes).transactions_count()))) }) } fn block_uncles_count_by_hash(&self, params: Params) -> Result { try!(self.active()); - from_params::<(H256,)>(params) + from_params::<(RpcH256,)>(params) .and_then(|(hash,)| - take_weak!(self.client).block(BlockID::Hash(hash)) - .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).uncles_count())))) + take_weak!(self.client).block(BlockID::Hash(hash.into())) + .map_or(Ok(Value::Null), |bytes| to_value(&RpcU256::from(BlockView::new(&bytes).uncles_count())))) } fn block_uncles_count_by_number(&self, params: Params) -> Result { try!(self.active()); from_params::<(BlockNumber,)>(params) .and_then(|(block_number,)| match block_number { - BlockNumber::Pending => to_value(&U256::from(0)), + BlockNumber::Pending => to_value(&RpcU256::from(0)), _ => take_weak!(self.client).block(block_number.into()) - .map_or(Ok(Value::Null), |bytes| to_value(&U256::from(BlockView::new(&bytes).uncles_count()))) + .map_or(Ok(Value::Null), |bytes| to_value(&RpcU256::from(BlockView::new(&bytes).uncles_count()))) }) } fn code_at(&self, params: Params) -> Result { try!(self.active()); from_params_default_second(params) - .and_then(|(address, block_number,)| match block_number { - BlockNumber::Pending => to_value(&take_weak!(self.miner).code(take_weak!(self.client).deref(), &address).map_or_else(Bytes::default, Bytes::new)), - BlockNumber::Latest => to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new)), - _ => Err(Error::invalid_params()), + .and_then(|(address, block_number,)| { + let address: Address = RpcH160::into(address); + match block_number { + BlockNumber::Pending => to_value(&take_weak!(self.miner).code(take_weak!(self.client).deref(), &address).map_or_else(Bytes::default, Bytes::new)), + BlockNumber::Latest => to_value(&take_weak!(self.client).code(&address).map_or_else(Bytes::default, Bytes::new)), + _ => Err(Error::invalid_params()), + } }) } fn block_by_hash(&self, params: Params) -> Result { try!(self.active()); - from_params::<(H256, bool)>(params) - .and_then(|(hash, include_txs)| self.block(BlockID::Hash(hash), include_txs)) + from_params::<(RpcH256, bool)>(params) + .and_then(|(hash, include_txs)| self.block(BlockID::Hash(hash.into()), include_txs)) } fn block_by_number(&self, params: Params) -> Result { @@ -436,9 +452,10 @@ impl Eth for EthClient where fn transaction_by_hash(&self, params: Params) -> Result { try!(self.active()); - from_params::<(H256,)>(params) + from_params::<(RpcH256,)>(params) .and_then(|(hash,)| { let miner = take_weak!(self.miner); + let hash: H256 = hash.into(); match miner.transaction(&hash) { Some(pending_tx) => to_value(&Transaction::from(pending_tx)), None => self.transaction(TransactionID::Hash(hash)) @@ -448,8 +465,8 @@ impl Eth for EthClient where fn transaction_by_block_hash_and_index(&self, params: Params) -> Result { try!(self.active()); - from_params::<(H256, Index)>(params) - .and_then(|(hash, index)| self.transaction(TransactionID::Location(BlockID::Hash(hash), index.value()))) + from_params::<(RpcH256, Index)>(params) + .and_then(|(hash, index)| self.transaction(TransactionID::Location(BlockID::Hash(hash.into()), index.value()))) } fn transaction_by_block_number_and_index(&self, params: Params) -> Result { @@ -460,9 +477,10 @@ impl Eth for EthClient where fn transaction_receipt(&self, params: Params) -> Result { try!(self.active()); - from_params::<(H256,)>(params) + from_params::<(RpcH256,)>(params) .and_then(|(hash,)| { let miner = take_weak!(self.miner); + let hash: H256 = hash.into(); match miner.pending_receipts().get(&hash) { Some(receipt) if self.allow_pending_receipt_query => to_value(&Receipt::from(receipt.clone())), _ => { @@ -476,8 +494,8 @@ impl Eth for EthClient where fn uncle_by_block_hash_and_index(&self, params: Params) -> Result { try!(self.active()); - from_params::<(H256, Index)>(params) - .and_then(|(hash, index)| self.uncle(UncleID { block: BlockID::Hash(hash), position: index.value() })) + from_params::<(RpcH256, Index)>(params) + .and_then(|(hash, index)| self.uncle(UncleID { block: BlockID::Hash(hash.into()), position: index.value() })) } fn uncle_by_block_number_and_index(&self, params: Params) -> Result { @@ -543,8 +561,9 @@ impl Eth for EthClient where miner.map_sealing_work(client.deref(), |b| { let pow_hash = b.hash(); let target = Ethash::difficulty_to_boundary(b.block().header().difficulty()); - let seed_hash = &self.seed_compute.lock().unwrap().get_seedhash(b.block().header().number()); - to_value(&(pow_hash, H256::from_slice(&seed_hash[..]), target, &U256::from(b.block().header().number()))) + let seed_hash = self.seed_compute.lock().unwrap().get_seedhash(b.block().header().number()); + let block_number = RpcU256::from(b.block().header().number()); + to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number)) }).unwrap_or(Err(Error::internal_error())) // no work found. }, _ => Err(Error::invalid_params()) @@ -553,7 +572,10 @@ impl Eth for EthClient where fn submit_work(&self, params: Params) -> Result { try!(self.active()); - from_params::<(H64, H256, H256)>(params).and_then(|(nonce, pow_hash, mix_hash)| { + from_params::<(RpcH64, RpcH256, RpcH256)>(params).and_then(|(nonce, pow_hash, mix_hash)| { + let nonce: H64 = nonce.into(); + let pow_hash: H256 = pow_hash.into(); + let mix_hash: H256 = mix_hash.into(); trace!(target: "miner", "submit_work: Decoded: nonce={}, pow_hash={}, mix_hash={}", nonce, pow_hash, mix_hash); let miner = take_weak!(self.miner); let client = take_weak!(self.client); @@ -565,8 +587,8 @@ impl Eth for EthClient where fn submit_hashrate(&self, params: Params) -> Result { try!(self.active()); - from_params::<(U256, H256)>(params).and_then(|(rate, id)| { - self.external_miner.submit_hashrate(rate, id); + from_params::<(RpcU256, RpcH256)>(params).and_then(|(rate, id)| { + self.external_miner.submit_hashrate(rate.into(), id.into()); to_value(&true) }) } @@ -578,7 +600,7 @@ impl Eth for EthClient where let raw_transaction = raw_transaction.to_vec(); match UntrustedRlp::new(&raw_transaction).as_val() { Ok(signed_transaction) => dispatch_transaction(&*take_weak!(self.client), &*take_weak!(self.miner), signed_transaction), - Err(_) => to_value(&H256::zero()), + Err(_) => to_value(&RpcH256::from(H256::from(0))), } }) } @@ -588,6 +610,7 @@ impl Eth for EthClient where trace!(target: "jsonrpc", "call: {:?}", params); from_params_default_second(params) .and_then(|(request, block_number,)| { + let request = CallRequest::into(request); 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()), @@ -602,13 +625,14 @@ impl Eth for EthClient where try!(self.active()); from_params_default_second(params) .and_then(|(request, block_number,)| { + let request = CallRequest::into(request); 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()), }; - to_value(&r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0))) + to_value(&RpcU256::from(r.map(|res| res.gas_used + res.refunded).unwrap_or(From::from(0)))) }) } diff --git a/rpc/src/v1/impls/eth_filter.rs b/rpc/src/v1/impls/eth_filter.rs index 40ced7187..cf2006788 100644 --- a/rpc/src/v1/impls/eth_filter.rs +++ b/rpc/src/v1/impls/eth_filter.rs @@ -25,7 +25,7 @@ use ethcore::miner::MinerService; use ethcore::filter::Filter as EthcoreFilter; use ethcore::client::{BlockChainClient, BlockID}; use v1::traits::EthFilter; -use v1::types::{BlockNumber, Index, Filter, Log}; +use v1::types::{BlockNumber, Index, Filter, Log, H256 as RpcH256, U256 as RpcU256}; use v1::helpers::{PollFilter, PollManager}; use v1::impls::eth::pending_logs; @@ -71,7 +71,7 @@ impl EthFilter for EthFilterClient where let mut polls = self.polls.lock().unwrap(); let block_number = take_weak!(self.client).chain_info().best_block_number; let id = polls.create_poll(PollFilter::Logs(block_number, Default::default(), filter)); - to_value(&U256::from(id)) + to_value(&RpcU256::from(id)) }) } @@ -81,7 +81,7 @@ impl EthFilter for EthFilterClient where Params::None => { let mut polls = self.polls.lock().unwrap(); let id = polls.create_poll(PollFilter::Block(take_weak!(self.client).chain_info().best_block_number)); - to_value(&U256::from(id)) + to_value(&RpcU256::from(id)) }, _ => Err(Error::invalid_params()) } @@ -95,7 +95,7 @@ impl EthFilter for EthFilterClient where let pending_transactions = take_weak!(self.miner).pending_transactions_hashes(); let id = polls.create_poll(PollFilter::PendingTransaction(pending_transactions)); - to_value(&U256::from(id)) + to_value(&RpcU256::from(id)) }, _ => Err(Error::invalid_params()) } @@ -116,7 +116,8 @@ impl EthFilter for EthFilterClient where let hashes = (*block_number..current_number).into_iter() .map(BlockID::Number) .filter_map(|id| client.block_hash(id)) - .collect::>(); + .map(Into::into) + .collect::>(); *block_number = current_number; @@ -135,7 +136,8 @@ impl EthFilter for EthFilterClient where .iter() .filter(|hash| !previous_hashes_set.contains(hash)) .cloned() - .collect::>() + .map(Into::into) + .collect::>() }; // save all hashes of pending transactions diff --git a/rpc/src/v1/impls/eth_signing.rs b/rpc/src/v1/impls/eth_signing.rs index 6dd01bdf1..4ba076358 100644 --- a/rpc/src/v1/impls/eth_signing.rs +++ b/rpc/src/v1/impls/eth_signing.rs @@ -20,17 +20,17 @@ use std::sync::{Arc, Weak}; use jsonrpc_core::*; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; -use util::numbers::*; +use util::{U256, Address, H256}; use ethcore::account_provider::AccountProvider; -use v1::helpers::{SigningQueue, ConfirmationsQueue}; +use v1::helpers::{SigningQueue, ConfirmationsQueue, TransactionRequest as TRequest}; use v1::traits::EthSigning; -use v1::types::{TransactionRequest, Bytes}; +use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520}; use v1::impls::{default_gas_price, sign_and_dispatch}; -fn fill_optional_fields(request: &mut TransactionRequest, client: &C, miner: &M) +fn fill_optional_fields(request: &mut TRequest, client: &C, miner: &M) where C: MiningBlockChainClient, M: MinerService { if request.value.is_none() { - request.value = Some(U256::zero()); + request.value = Some(U256::from(0)); } if request.gas.is_none() { request.gas = Some(miner.sensible_gas_limit()); @@ -39,7 +39,7 @@ fn fill_optional_fields(request: &mut TransactionRequest, client: &C, mine request.gas_price = Some(default_gas_price(client, miner)); } if request.data.is_none() { - request.data = Some(Bytes::new(Vec::new())); + request.data = Some(Vec::new()); } } @@ -83,7 +83,8 @@ impl EthSigning for EthSigningQueueClient fn send_transaction(&self, params: Params) -> Result { try!(self.active()); from_params::<(TransactionRequest, )>(params) - .and_then(|(mut request, )| { + .and_then(|(request, )| { + let mut request: TRequest = request.into(); let accounts = take_weak!(self.accounts); let (client, miner) = (take_weak!(self.client), take_weak!(self.miner)); @@ -91,7 +92,7 @@ impl EthSigning for EthSigningQueueClient let sender = request.from; return match sign_and_dispatch(&*client, &*miner, request, &*accounts, sender) { Ok(hash) => to_value(&hash), - _ => to_value(&H256::zero()), + _ => to_value(&RpcH256::default()), } } @@ -99,7 +100,7 @@ impl EthSigning for EthSigningQueueClient fill_optional_fields(&mut request, &*client, &*miner); let id = queue.add_request(request); let result = id.wait_with_timeout(); - result.unwrap_or_else(|| to_value(&H256::new())) + result.unwrap_or_else(|| to_value(&RpcH256::default())) }) } } @@ -140,8 +141,10 @@ impl EthSigning for EthSigningUnsafeClient where fn sign(&self, params: Params) -> Result { try!(self.active()); - from_params::<(Address, H256)>(params).and_then(|(addr, msg)| { - to_value(&take_weak!(self.accounts).sign(addr, msg).unwrap_or(H520::zero())) + from_params::<(RpcH160, RpcH256)>(params).and_then(|(address, msg)| { + let address: Address = address.into(); + let msg: H256 = msg.into(); + to_value(&take_weak!(self.accounts).sign(address, msg).ok().map_or_else(RpcH520::default, Into::into)) }) } @@ -149,10 +152,11 @@ impl EthSigning for EthSigningUnsafeClient where try!(self.active()); from_params::<(TransactionRequest, )>(params) .and_then(|(request, )| { + let request: TRequest = request.into(); let sender = request.from; match sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*take_weak!(self.accounts), sender) { Ok(hash) => to_value(&hash), - _ => to_value(&H256::zero()), + _ => to_value(&RpcH256::default()), } }) } diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index fe2313c30..b62e21b0c 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -25,7 +25,7 @@ use ethcore::client::{MiningBlockChainClient}; use jsonrpc_core::*; use ethcore::miner::MinerService; use v1::traits::Ethcore; -use v1::types::{Bytes}; +use v1::types::{Bytes, U256}; use v1::helpers::{SigningQueue, ConfirmationsQueue}; use v1::impls::error_codes; @@ -69,7 +69,7 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static, C: M fn min_gas_price(&self, _: Params) -> Result { try!(self.active()); - to_value(&take_weak!(self.miner).minimal_gas_price()) + to_value(&U256::from(take_weak!(self.miner).minimal_gas_price())) } fn extra_data(&self, _: Params) -> Result { @@ -79,12 +79,12 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static, C: M fn gas_floor_target(&self, _: Params) -> Result { try!(self.active()); - to_value(&take_weak!(self.miner).gas_floor_target()) + to_value(&U256::from(take_weak!(self.miner).gas_floor_target())) } fn gas_ceil_target(&self, _: Params) -> Result { try!(self.active()); - to_value(&take_weak!(self.miner).gas_ceil_target()) + to_value(&U256::from(take_weak!(self.miner).gas_ceil_target())) } fn dev_logs(&self, _params: Params) -> Result { @@ -140,8 +140,8 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static, C: M match params { Params::None => match take_weak!(self.client).gas_price_statistics(100, 8) { Ok(stats) => to_value(&stats - .iter() - .map(|x| to_value(&x).expect("x must be U256; qed")) + .into_iter() + .map(|x| to_value(&U256::from(x)).expect("x must be U256; qed")) .collect::>()), _ => Err(Error::internal_error()), }, diff --git a/rpc/src/v1/impls/ethcore_set.rs b/rpc/src/v1/impls/ethcore_set.rs index 1a41509f7..1b8dd474e 100644 --- a/rpc/src/v1/impls/ethcore_set.rs +++ b/rpc/src/v1/impls/ethcore_set.rs @@ -15,15 +15,14 @@ // along with Parity. If not, see . /// Ethcore-specific rpc interface for operations altering the settings. -use util::{U256, Address}; -use util::network::{NetworkService, NonReservedPeerMode}; use std::sync::{Arc, Weak}; use jsonrpc_core::*; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; use ethcore::service::SyncMessage; +use util::network::{NetworkService, NonReservedPeerMode}; use v1::traits::EthcoreSet; -use v1::types::Bytes; +use v1::types::{Bytes, H160, U256}; /// Ethcore-specific rpc interface for operations altering the settings. pub struct EthcoreSetClient where @@ -61,7 +60,7 @@ impl EthcoreSet for EthcoreSetClient where fn set_min_gas_price(&self, params: Params) -> Result { try!(self.active()); from_params::<(U256,)>(params).and_then(|(gas_price,)| { - take_weak!(self.miner).set_minimal_gas_price(gas_price); + take_weak!(self.miner).set_minimal_gas_price(gas_price.into()); to_value(&true) }) } @@ -69,7 +68,7 @@ impl EthcoreSet for EthcoreSetClient where fn set_gas_floor_target(&self, params: Params) -> Result { try!(self.active()); from_params::<(U256,)>(params).and_then(|(target,)| { - take_weak!(self.miner).set_gas_floor_target(target); + take_weak!(self.miner).set_gas_floor_target(target.into()); to_value(&true) }) } @@ -77,7 +76,7 @@ impl EthcoreSet for EthcoreSetClient where fn set_gas_ceil_target(&self, params: Params) -> Result { try!(self.active()); from_params::<(U256,)>(params).and_then(|(target,)| { - take_weak!(self.miner).set_gas_ceil_target(target); + take_weak!(self.miner).set_gas_ceil_target(target.into()); to_value(&true) }) } @@ -92,8 +91,8 @@ impl EthcoreSet for EthcoreSetClient where fn set_author(&self, params: Params) -> Result { try!(self.active()); - from_params::<(Address,)>(params).and_then(|(author,)| { - take_weak!(self.miner).set_author(author); + from_params::<(H160,)>(params).and_then(|(author,)| { + take_weak!(self.miner).set_author(author.into()); to_value(&true) }) } diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 1a6b1c398..d64619527 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -53,7 +53,8 @@ pub use self::ethcore_set::EthcoreSetClient; pub use self::traces::TracesClient; pub use self::rpc::RpcClient; -use v1::types::TransactionRequest; +use v1::helpers::TransactionRequest; +use v1::types::H256 as NH256; use ethcore::error::Error as EthcoreError; use ethcore::miner::{AccountDetails, MinerService}; use ethcore::client::MiningBlockChainClient; @@ -77,7 +78,7 @@ mod error_codes { fn dispatch_transaction(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result where C: MiningBlockChainClient, M: MinerService { - let hash = signed_transaction.hash(); + let hash = NH256::from(signed_transaction.hash()); let import = miner.import_own_transaction(client, signed_transaction, |a: &Address| { AccountDetails { diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index 94636ae99..d6286afb9 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -18,10 +18,11 @@ use std::sync::{Arc, Weak}; use jsonrpc_core::*; use v1::traits::Personal; -use v1::types::TransactionRequest; +use v1::types::{H160 as RpcH160, H256 as RpcH256, TransactionRequest}; use v1::impls::unlock_sign_and_dispatch; +use v1::helpers::{TransactionRequest as TRequest}; use ethcore::account_provider::AccountProvider; -use util::numbers::*; +use util::Address; use ethcore::client::MiningBlockChainClient; use ethcore::miner::MinerService; @@ -63,7 +64,7 @@ impl Personal for PersonalClient where C: MiningBl fn accounts(&self, _: Params) -> Result { try!(self.active()); let store = take_weak!(self.accounts); - to_value(&store.accounts()) + to_value(&store.accounts().into_iter().map(Into::into).collect::>()) } fn new_account(&self, params: Params) -> Result { @@ -72,7 +73,7 @@ impl Personal for PersonalClient where C: MiningBl |(pass, )| { let store = take_weak!(self.accounts); match store.new_account(&pass) { - Ok(address) => to_value(&address), + Ok(address) => to_value(&RpcH160::from(address)), Err(_) => Err(Error::internal_error()) } } @@ -81,8 +82,9 @@ impl Personal for PersonalClient where C: MiningBl fn unlock_account(&self, params: Params) -> Result { try!(self.active()); - from_params::<(Address, String, u64)>(params).and_then( + from_params::<(RpcH160, String, u64)>(params).and_then( |(account, account_pass, _)|{ + let account: Address = account.into(); let store = take_weak!(self.accounts); match store.unlock_account_temporarily(account, account_pass) { Ok(_) => Ok(Value::Bool(true)), @@ -95,12 +97,13 @@ impl Personal for PersonalClient where C: MiningBl try!(self.active()); from_params::<(TransactionRequest, String)>(params) .and_then(|(request, password)| { + let request: TRequest = request.into(); let sender = request.from; let accounts = take_weak!(self.accounts); match unlock_sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*accounts, sender, password) { - Ok(hash) => to_value(&hash), - _ => to_value(&H256::zero()), + Ok(hash) => Ok(hash), + _ => to_value(&RpcH256::default()), } }) } diff --git a/rpc/src/v1/impls/personal_signer.rs b/rpc/src/v1/impls/personal_signer.rs index 97749657e..a31a3820f 100644 --- a/rpc/src/v1/impls/personal_signer.rs +++ b/rpc/src/v1/impls/personal_signer.rs @@ -18,14 +18,13 @@ use std::sync::{Arc, Weak}; use jsonrpc_core::*; -use v1::traits::PersonalSigner; -use v1::types::TransactionModification; -use v1::impls::unlock_sign_and_dispatch; -use v1::helpers::{SigningQueue, ConfirmationsQueue}; use ethcore::account_provider::AccountProvider; -use util::numbers::*; use ethcore::client::MiningBlockChainClient; use ethcore::miner::MinerService; +use v1::traits::PersonalSigner; +use v1::types::{TransactionModification, TransactionConfirmation, U256}; +use v1::impls::unlock_sign_and_dispatch; +use v1::helpers::{SigningQueue, ConfirmationsQueue}; /// Transactions confirmation (personal) rpc implementation. pub struct SignerClient where C: MiningBlockChainClient, M: MinerService { @@ -59,13 +58,14 @@ impl PersonalSigner for SignerClient where C: Mini fn transactions_to_confirm(&self, _params: Params) -> Result { try!(self.active()); let queue = take_weak!(self.queue); - to_value(&queue.requests()) + to_value(&queue.requests().into_iter().map(From::from).collect::>()) } fn confirm_transaction(&self, params: Params) -> Result { try!(self.active()); from_params::<(U256, TransactionModification, String)>(params).and_then( |(id, modification, pass)| { + let id = id.into(); let accounts = take_weak!(self.accounts); let queue = take_weak!(self.queue); let client = take_weak!(self.client); @@ -74,7 +74,7 @@ impl PersonalSigner for SignerClient where C: Mini let mut request = confirmation.transaction; // apply modification if let Some(gas_price) = modification.gas_price { - request.gas_price = Some(gas_price); + request.gas_price = Some(gas_price.into()); } let sender = request.from; @@ -99,7 +99,7 @@ impl PersonalSigner for SignerClient where C: Mini from_params::<(U256, )>(params).and_then( |(id, )| { let queue = take_weak!(self.queue); - let res = queue.request_rejected(id); + let res = queue.request_rejected(id.into()); to_value(&res.is_some()) } ) diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index 45daa2500..71176df27 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -19,12 +19,13 @@ use std::sync::{Weak, Arc}; use jsonrpc_core::*; use std::collections::BTreeMap; -use util::H256; +//use util::H256; use ethcore::client::{BlockChainClient, CallAnalytics, TransactionID, TraceId}; use ethcore::miner::MinerService; use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action}; use v1::traits::Traces; -use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff, VMTrace}; +use v1::helpers::CallRequest as CRequest; +use v1::types::{TraceFilter, LocalizedTrace, Trace, BlockNumber, Index, CallRequest, Bytes, StateDiff, VMTrace, H256}; /// Traces api implementation. pub struct TracesClient where C: BlockChainClient, M: MinerService { @@ -42,7 +43,7 @@ impl TracesClient where C: BlockChainClient, M: MinerService { } // TODO: share with eth.rs - fn sign_call(&self, request: CallRequest) -> Result { + fn sign_call(&self, request: CRequest) -> Result { let client = take_weak!(self.client); let miner = take_weak!(self.miner); let from = request.from.unwrap_or(0.into()); @@ -91,7 +92,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: from_params::<(H256,)>(params) .and_then(|(transaction_hash,)| { let client = take_weak!(self.client); - let traces = client.transaction_traces(TransactionID::Hash(transaction_hash)); + let traces = client.transaction_traces(TransactionID::Hash(transaction_hash.into())); let traces = traces.map_or_else(Vec::new, |traces| traces.into_iter().map(LocalizedTrace::from).collect()); to_value(&traces) }) @@ -103,7 +104,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: .and_then(|(transaction_hash, address)| { let client = take_weak!(self.client); let id = TraceId { - transaction: TransactionID::Hash(transaction_hash), + transaction: TransactionID::Hash(transaction_hash.into()), address: address.into_iter().map(|i| i.value()).collect() }; let trace = client.trace(id); @@ -117,6 +118,7 @@ impl Traces for TracesClient where C: BlockChainClient + 'static, M: trace!(target: "jsonrpc", "call: {:?}", params); from_params(params) .and_then(|(request, flags)| { + let request = CallRequest::into(request); let flags: Vec = flags; let analytics = CallAnalytics { transaction_tracing: flags.contains(&("trace".to_owned())), diff --git a/rpc/src/v1/impls/web3.rs b/rpc/src/v1/impls/web3.rs index 87343a18a..3f993f21c 100644 --- a/rpc/src/v1/impls/web3.rs +++ b/rpc/src/v1/impls/web3.rs @@ -18,7 +18,7 @@ use jsonrpc_core::*; use util::version; use v1::traits::Web3; -use v1::types::Bytes; +use v1::types::{H256, Bytes}; use util::sha3::Hashable; /// Web3 rpc implementation. @@ -40,9 +40,9 @@ impl Web3 for Web3Client { fn sha3(&self, params: Params) -> Result { from_params::<(Bytes,)>(params).and_then( |(data,)| { - let Bytes(ref v) = data; - let sha3 = v.sha3(); - to_value(&sha3) + let Bytes(ref vec) = data; + let sha3 = vec.sha3(); + to_value(&H256::from(sha3)) } ) } diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 2965a62d2..739c91b17 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -34,6 +34,7 @@ use util::{U256, H256, Uint}; use jsonrpc_core::IoHandler; use ethjson::blockchain::BlockChain; +use v1::types::U256 as NU256; use v1::traits::eth::{Eth, EthSigning}; use v1::impls::{EthClient, EthSigningUnsafeClient}; use v1::tests::helpers::{TestSyncProvider, Config}; @@ -330,7 +331,7 @@ fn verify_transaction_counts(name: String, chain: BlockChain) { "jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "params": [ - "#.to_owned() + &::serde_json::to_string(&U256::from(num)).unwrap() + r#" + "#.to_owned() + &::serde_json::to_string(&NU256::from(num)).unwrap() + r#" ], "id": "# + format!("{}", *id).as_ref() + r#" }"#; diff --git a/rpc/src/v1/tests/mocked/personal_signer.rs b/rpc/src/v1/tests/mocked/personal_signer.rs index 6fa734cec..b2ef694a1 100644 --- a/rpc/src/v1/tests/mocked/personal_signer.rs +++ b/rpc/src/v1/tests/mocked/personal_signer.rs @@ -23,9 +23,7 @@ use ethcore::client::TestBlockChainClient; use ethcore::transaction::{Transaction, Action}; use v1::{SignerClient, PersonalSigner}; use v1::tests::helpers::TestMinerService; -use v1::helpers::{SigningQueue, ConfirmationsQueue}; -use v1::types::TransactionRequest; - +use v1::helpers::{SigningQueue, ConfirmationsQueue, TransactionRequest}; struct PersonalSignerTester { queue: Arc, diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index b86723357..63f596d4a 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -15,8 +15,7 @@ // along with Parity. If not, see . use serde::{Serialize, Serializer}; -use util::numbers::*; -use v1::types::{Bytes, Transaction, OptionalValue}; +use v1::types::{Bytes, Transaction, H160, H256, H2048, U256}; /// Block Transactions #[derive(Debug)] @@ -41,7 +40,7 @@ impl Serialize for BlockTransactions { #[derive(Debug, Serialize)] pub struct Block { /// Hash of the block - pub hash: OptionalValue, + pub hash: Option, /// Hash of the parent #[serde(rename="parentHash")] pub parent_hash: H256, @@ -49,10 +48,10 @@ pub struct Block { #[serde(rename="sha3Uncles")] pub uncles_hash: H256, /// Authors address - pub author: Address, + pub author: H160, // TODO: get rid of this one /// ? - pub miner: Address, + pub miner: H160, /// State root hash #[serde(rename="stateRoot")] pub state_root: H256, @@ -63,7 +62,7 @@ pub struct Block { #[serde(rename="receiptsRoot")] pub receipts_root: H256, /// Block number - pub number: OptionalValue, + pub number: Option, /// Gas Used #[serde(rename="gasUsed")] pub gas_used: U256, @@ -95,9 +94,8 @@ pub struct Block { #[cfg(test)] mod tests { use serde_json; - use util::numbers::*; - use v1::types::{Transaction, Bytes, OptionalValue}; - use super::*; + use v1::types::{Transaction, H160, H256, H2048, Bytes, U256}; + use super::{Block, BlockTransactions}; #[test] fn test_serialize_block_transactions() { @@ -105,7 +103,7 @@ mod tests { let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x","creates":null}]"#); - let t = BlockTransactions::Hashes(vec![H256::default()]); + let t = BlockTransactions::Hashes(vec![H256::default().into()]); let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"["0x0000000000000000000000000000000000000000000000000000000000000000"]"#); } @@ -113,15 +111,15 @@ mod tests { #[test] fn test_serialize_block() { let block = Block { - hash: OptionalValue::Value(H256::default()), + hash: Some(H256::default()), parent_hash: H256::default(), uncles_hash: H256::default(), - author: Address::default(), - miner: Address::default(), + author: H160::default(), + miner: H160::default(), state_root: H256::default(), transactions_root: H256::default(), receipts_root: H256::default(), - number: OptionalValue::Value(U256::default()), + number: Some(U256::default()), gas_used: U256::default(), gas_limit: U256::default(), extra_data: Bytes::default(), @@ -131,7 +129,7 @@ mod tests { total_difficulty: U256::default(), seal_fields: vec![Bytes::default(), Bytes::default()], uncles: vec![], - transactions: BlockTransactions::Hashes(vec![]) + transactions: BlockTransactions::Hashes(vec![].into()) }; let serialized = serde_json::to_string(&block).unwrap(); diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 1bf5deb75..09c899057 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -42,6 +42,12 @@ impl From> for Bytes { } } +impl Into> for Bytes { + fn into(self) -> Vec { + self.0 + } +} + impl Serialize for Bytes { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { diff --git a/rpc/src/v1/types/call_request.rs b/rpc/src/v1/types/call_request.rs index 50ebbd1f0..38721e2f8 100644 --- a/rpc/src/v1/types/call_request.rs +++ b/rpc/src/v1/types/call_request.rs @@ -14,17 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::hash::Address; -use util::numbers::U256; -use v1::types::Bytes; +use v1::helpers::CallRequest as Request; +use v1::types::{Bytes, H160, U256}; /// Call request #[derive(Debug, Default, PartialEq, Deserialize)] pub struct CallRequest { /// From - pub from: Option
, + pub from: Option, /// To - pub to: Option
, + pub to: Option, /// Gas Price #[serde(rename="gasPrice")] pub gas_price: Option, @@ -38,18 +37,30 @@ pub struct CallRequest { pub nonce: Option, } +impl Into for CallRequest { + fn into(self) -> Request { + Request { + from: self.from.map(Into::into), + to: self.to.map(Into::into), + gas_price: self.gas_price.map(Into::into), + gas: self.gas.map(Into::into), + value: self.value.map(Into::into), + data: self.data.map(Into::into), + nonce: self.nonce.map(Into::into), + } + } +} + #[cfg(test)] mod tests { use std::str::FromStr; use rustc_serialize::hex::FromHex; use serde_json; - use util::numbers::{U256}; - use util::hash::Address; - use v1::types::Bytes; - use super::*; + use v1::types::{U256, H160}; + use super::CallRequest; #[test] - fn transaction_request_deserialize() { + fn call_request_deserialize() { let s = r#"{ "from":"0x0000000000000000000000000000000000000001", "to":"0x0000000000000000000000000000000000000002", @@ -62,18 +73,18 @@ mod tests { let deserialized: CallRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, CallRequest { - from: Some(Address::from(1)), - to: Some(Address::from(2)), + from: Some(H160::from(1)), + to: Some(H160::from(2)), gas_price: Some(U256::from(1)), gas: Some(U256::from(2)), value: Some(U256::from(3)), - data: Some(Bytes::new(vec![0x12, 0x34, 0x56])), + data: Some(vec![0x12, 0x34, 0x56].into()), nonce: Some(U256::from(4)), }); } #[test] - fn transaction_request_deserialize2() { + fn call_request_deserialize2() { let s = r#"{ "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", @@ -85,23 +96,23 @@ mod tests { let deserialized: CallRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, CallRequest { - from: Some(Address::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), - to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), + to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()), gas: Some(U256::from_str("76c0").unwrap()), value: Some(U256::from_str("9184e72a").unwrap()), - data: Some(Bytes::new("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap())), + data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), nonce: None }); } #[test] - fn transaction_request_deserialize_empty() { + fn call_request_deserialize_empty() { let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#; let deserialized: CallRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, CallRequest { - from: Some(Address::from(1)), + from: Some(H160::from(1)), to: None, gas_price: None, gas: None, diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index 77a3f0500..a34bb9ae9 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -17,10 +17,9 @@ use serde::{Deserialize, Deserializer, Error}; use serde_json::value; use jsonrpc_core::Value; -use util::numbers::*; -use v1::types::BlockNumber; use ethcore::filter::Filter as EthFilter; use ethcore::client::BlockID; +use v1::types::{BlockNumber, H160, H256}; /// Variadic value #[derive(Debug, PartialEq, Clone)] @@ -49,7 +48,7 @@ impl Deserialize for VariadicValue where T: Deserialize { } /// Filter Address -pub type FilterAddress = VariadicValue
; +pub type FilterAddress = VariadicValue; /// Topic pub type Topic = VariadicValue; @@ -76,14 +75,14 @@ impl Into for Filter { to_block: self.to_block.map_or_else(|| BlockID::Latest, Into::into), address: self.address.and_then(|address| match address { VariadicValue::Null => None, - VariadicValue::Single(a) => Some(vec![a]), - VariadicValue::Multiple(a) => Some(a) + VariadicValue::Single(a) => Some(vec![a.into()]), + VariadicValue::Multiple(a) => Some(a.into_iter().map(Into::into).collect()) }), topics: { let mut iter = self.topics.map_or_else(Vec::new, |topics| topics.into_iter().take(4).map(|topic| match topic { VariadicValue::Null => None, - VariadicValue::Single(t) => Some(vec![t]), - VariadicValue::Multiple(t) => Some(t) + VariadicValue::Single(t) => Some(vec![t.into()]), + VariadicValue::Multiple(t) => Some(t.into_iter().map(Into::into).collect()) }).filter_map(|m| m).collect()).into_iter(); [iter.next(), iter.next(), iter.next(), iter.next()] } @@ -104,11 +103,11 @@ mod tests { let s = r#"["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", null, ["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"]]"#; let deserialized: Vec = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, vec![ - VariadicValue::Single(H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap()), + VariadicValue::Single(H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap().into()), VariadicValue::Null, VariadicValue::Multiple(vec![ - H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(), - H256::from_str("0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc").unwrap() + H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap().into(), + H256::from_str("0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc").unwrap().into(), ]) ]); } diff --git a/rpc/src/v1/types/hash.rs b/rpc/src/v1/types/hash.rs new file mode 100644 index 000000000..f0606ed51 --- /dev/null +++ b/rpc/src/v1/types/hash.rs @@ -0,0 +1,145 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::fmt; +use std::str::FromStr; +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; +use serde; +use rustc_serialize::hex::{ToHex, FromHex}; +use util::{H64 as Eth64, H256 as EthH256, H520 as EthH520, H2048 as Eth2048, Address}; + +macro_rules! impl_hash { + ($name: ident, $other: ident, $size: expr) => { + /// Hash serialization + #[derive(Eq)] + pub struct $name([u8; $size]); + + impl Default for $name { + fn default() -> Self { + $name([0; $size]) + } + } + + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self.0.to_hex()) + } + } + + impl From for $name where $other: From { + fn from(o: T) -> Self { + $name($other::from(o).0) + } + } + + impl FromStr for $name { + type Err = <$other as FromStr>::Err; + + fn from_str(s: &str) -> Result { + $other::from_str(s).map(|x| $name(x.0)) + } + } + + impl Into<$other> for $name { + fn into(self) -> $other { + $other(self.0) + } + } + + impl PartialEq for $name { + fn eq(&self, other: &Self) -> bool { + let self_ref: &[u8] = &self.0; + let other_ref: &[u8] = &other.0; + self_ref == other_ref + } + } + + impl PartialOrd for $name { + fn partial_cmp(&self, other: &Self) -> Option { + let self_ref: &[u8] = &self.0; + let other_ref: &[u8] = &other.0; + self_ref.partial_cmp(other_ref) + } + } + + impl Ord for $name { + fn cmp(&self, other: &Self) -> Ordering { + let self_ref: &[u8] = &self.0; + let other_ref: &[u8] = &other.0; + self_ref.cmp(other_ref) + } + } + + impl Hash for $name { + fn hash(&self, state: &mut H) where H: Hasher { + let self_ref: &[u8] = &self.0; + Hash::hash(self_ref, state) + } + } + + impl Clone for $name { + fn clone(&self) -> Self { + let mut r = [0; $size]; + r.copy_from_slice(&self.0); + $name(r) + } + } + + impl serde::Serialize for $name { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: serde::Serializer { + let mut hex = "0x".to_owned(); + hex.push_str(&self.0.to_hex()); + serializer.serialize_str(&hex) + } + } + + impl serde::Deserialize for $name { + fn deserialize(deserializer: &mut D) -> Result<$name, D::Error> where D: serde::Deserializer { + struct HashVisitor; + + impl serde::de::Visitor for HashVisitor { + type Value = $name; + + fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { + match value[2..].from_hex() { + Ok(ref v) if v.len() == $size => { + let mut result = [0u8; $size]; + result.copy_from_slice(v); + Ok($name(result)) + }, + Ok(_) => Err(serde::Error::custom("Invalid length.")), + _ => Err(serde::Error::custom("Invalid hex value.")) + } + } + + fn visit_string(&mut self, value: String) -> Result where E: serde::Error { + self.visit_str(value.as_ref()) + } + } + + deserializer.deserialize(HashVisitor) + } + } + } +} + +impl_hash!(H64, Eth64, 8); +impl_hash!(H160, Address, 20); +impl_hash!(H256, EthH256, 32); +impl_hash!(H520, EthH520, 65); +impl_hash!(H2048, Eth2048, 256); diff --git a/rpc/src/v1/types/log.rs b/rpc/src/v1/types/log.rs index 72a482d1b..9d9261d94 100644 --- a/rpc/src/v1/types/log.rs +++ b/rpc/src/v1/types/log.rs @@ -14,15 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::numbers::*; use ethcore::log_entry::{LocalizedLogEntry, LogEntry}; -use v1::types::Bytes; +use v1::types::{Bytes, H160, H256, U256}; /// Log #[derive(Debug, Serialize, PartialEq, Eq, Hash, Clone)] pub struct Log { - /// Address - pub address: Address, + /// H160 + pub address: H160, /// Topics pub topics: Vec, /// Data @@ -50,14 +49,14 @@ pub struct Log { impl From for Log { fn from(e: LocalizedLogEntry) -> Log { Log { - address: e.entry.address, - topics: e.entry.topics, - data: Bytes::new(e.entry.data), - block_hash: Some(e.block_hash), - block_number: Some(From::from(e.block_number)), - transaction_hash: Some(e.transaction_hash), - transaction_index: Some(From::from(e.transaction_index)), - log_index: Some(From::from(e.log_index)), + address: e.entry.address.into(), + topics: e.entry.topics.into_iter().map(Into::into).collect(), + data: e.entry.data.into(), + block_hash: Some(e.block_hash.into()), + block_number: Some(e.block_number.into()), + transaction_hash: Some(e.transaction_hash.into()), + transaction_index: Some(e.transaction_index.into()), + log_index: Some(e.log_index.into()), log_type: "mined".to_owned(), } } @@ -66,9 +65,9 @@ impl From for Log { impl From for Log { fn from(e: LogEntry) -> Log { Log { - address: e.address, - topics: e.topics, - data: Bytes::new(e.data), + address: e.address.into(), + topics: e.topics.into_iter().map(Into::into).collect(), + data: e.data.into(), block_hash: None, block_number: None, transaction_hash: None, @@ -83,25 +82,24 @@ impl From for Log { mod tests { use serde_json; use std::str::FromStr; - use util::numbers::*; - use v1::types::{Bytes, Log}; + use v1::types::{Log, H160, H256, U256}; #[test] fn log_serialization() { let s = r#"{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","logIndex":"0x01","type":"mined"}"#; let log = Log { - address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), + address: H160::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), topics: vec![ H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(), - H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap() + H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap(), ], - data: Bytes::new(vec![]), + data: vec![].into(), block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), block_number: Some(U256::from(0x4510c)), - transaction_hash: Some(H256::new()), - transaction_index: Some(U256::zero()), - log_index: Some(U256::one()), + transaction_hash: Some(H256::default()), + transaction_index: Some(U256::default()), + log_index: Some(U256::from(1)), log_type: "mined".to_owned(), }; diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index 3f07bfb31..ecbe6d9a0 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -18,9 +18,9 @@ mod bytes; mod block; mod block_number; mod filter; +mod hash; mod index; mod log; -mod optionals; mod sync; mod transaction; mod transaction_request; @@ -28,14 +28,15 @@ mod call_request; mod receipt; mod trace; mod trace_filter; +mod uint; pub use self::bytes::Bytes; pub use self::block::{Block, BlockTransactions}; pub use self::block_number::BlockNumber; pub use self::filter::Filter; +pub use self::hash::{H64, H160, H256, H520, H2048}; pub use self::index::Index; pub use self::log::Log; -pub use self::optionals::OptionalValue; pub use self::sync::{SyncStatus, SyncInfo}; pub use self::transaction::Transaction; pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification}; @@ -43,3 +44,4 @@ pub use self::call_request::CallRequest; pub use self::receipt::Receipt; pub use self::trace::{Trace, LocalizedTrace, StateDiff, VMTrace}; pub use self::trace_filter::TraceFilter; +pub use self::uint::U256; diff --git a/rpc/src/v1/types/optionals.rs b/rpc/src/v1/types/optionals.rs deleted file mode 100644 index 5f62dc4a0..000000000 --- a/rpc/src/v1/types/optionals.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde_json::Value; - -/// Optional value -#[derive(Debug)] -pub enum OptionalValue where T: Serialize { - /// Some - Value(T), - /// None - Null -} - -impl Default for OptionalValue where T: Serialize + Deserialize { - fn default() -> Self { - OptionalValue::Null - } -} - -impl Into> for OptionalValue where T: Serialize + Deserialize { - fn into(self) -> Option { - match self { - OptionalValue::Null => None, - OptionalValue::Value(t) => Some(t), - } - } -} - -impl Serialize for OptionalValue where T: Serialize + Deserialize { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: Serializer { - match *self { - OptionalValue::Value(ref value) => value.serialize(serializer), - OptionalValue::Null => Value::Null.serialize(serializer) - } - } -} - -impl Deserialize for OptionalValue where T: Serialize + Deserialize { - fn deserialize(deserializer: &mut D) -> Result, D::Error> - where D: Deserializer { - let deser_result: Result = Deserialize::deserialize(deserializer); - match deser_result { - Ok(t) => Ok(OptionalValue::Value(t)), - Err(_) => Ok(OptionalValue::Null), - } - } -} - -#[cfg(test)] -mod tests { - use serde_json; - use util::hash::*; - use super::*; - - #[test] - fn test_serialize_optional_value() { - let v: OptionalValue = OptionalValue::Null; - let serialized = serde_json::to_string(&v).unwrap(); - assert_eq!(serialized, r#"null"#); - - let v = OptionalValue::Value(H256::default()); - let serialized = serde_json::to_string(&v).unwrap(); - assert_eq!(serialized, r#""0x0000000000000000000000000000000000000000000000000000000000000000""#); - } -} diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index c56cdf3d3..afe0268ee 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -14,9 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::numbers::U256; -use util::hash::{Address, H256}; -use v1::types::Log; +use v1::types::{Log, H160, H256, U256}; use ethcore::receipt::{Receipt as EthReceipt, LocalizedReceipt}; /// Receipt @@ -42,7 +40,7 @@ pub struct Receipt { pub gas_used: U256, /// Contract address #[serde(rename="contractAddress")] - pub contract_address: Option
, + pub contract_address: Option, /// Logs pub logs: Vec, } @@ -50,14 +48,14 @@ pub struct Receipt { impl From for Receipt { fn from(r: LocalizedReceipt) -> Self { Receipt { - transaction_hash: Some(r.transaction_hash), - transaction_index: Some(U256::from(r.transaction_index)), - block_hash: Some(r.block_hash), - block_number: Some(U256::from(r.block_number)), - cumulative_gas_used: r.cumulative_gas_used, - gas_used: r.gas_used, - contract_address: r.contract_address, - logs: r.logs.into_iter().map(From::from).collect(), + transaction_hash: Some(r.transaction_hash.into()), + transaction_index: Some(r.transaction_index.into()), + block_hash: Some(r.block_hash.into()), + block_number: Some(r.block_number.into()), + cumulative_gas_used: r.cumulative_gas_used.into(), + gas_used: r.gas_used.into(), + contract_address: r.contract_address.map(Into::into), + logs: r.logs.into_iter().map(Into::into).collect(), } } } @@ -69,10 +67,10 @@ impl From for Receipt { transaction_index: None, block_hash: None, block_number: None, - cumulative_gas_used: r.gas_used, - gas_used: r.gas_used, + cumulative_gas_used: r.gas_used.into(), + gas_used: r.gas_used.into(), contract_address: None, - logs: r.logs.into_iter().map(From::from).collect(), + logs: r.logs.into_iter().map(Into::into).collect(), } } } @@ -81,33 +79,32 @@ impl From for Receipt { mod tests { use serde_json; use std::str::FromStr; - use util::numbers::*; - use v1::types::{Bytes, Log, Receipt}; + use v1::types::{Log, Receipt, U256, H256, H160}; #[test] fn receipt_serialization() { let s = r#"{"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","cumulativeGasUsed":"0x20","gasUsed":"0x10","contractAddress":null,"logs":[{"address":"0x33990122638b9132ca29c723bdf037f1a891a70c","topics":["0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc","0x4861736852656700000000000000000000000000000000000000000000000000"],"data":"0x","blockHash":"0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","blockNumber":"0x04510c","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x00","logIndex":"0x01","type":"mined"}]}"#; let receipt = Receipt { - transaction_hash: Some(H256::zero()), - transaction_index: Some(U256::zero()), + transaction_hash: Some(H256::from(0)), + transaction_index: Some(U256::from(0)), block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), block_number: Some(U256::from(0x4510c)), cumulative_gas_used: U256::from(0x20), gas_used: U256::from(0x10), contract_address: None, logs: vec![Log { - address: Address::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), + address: H160::from_str("33990122638b9132ca29c723bdf037f1a891a70c").unwrap(), topics: vec![ H256::from_str("a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc").unwrap(), - H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap() + H256::from_str("4861736852656700000000000000000000000000000000000000000000000000").unwrap(), ], - data: Bytes::new(vec![]), + data: vec![].into(), block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), block_number: Some(U256::from(0x4510c)), - transaction_hash: Some(H256::new()), - transaction_index: Some(U256::zero()), - log_index: Some(U256::one()), + transaction_hash: Some(H256::default()), + transaction_index: Some(U256::default()), + log_index: Some(U256::from(1)), log_type: "mined".to_owned(), }] }; diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index 6d750425e..49f422c86 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use serde::{Serialize, Serializer}; -use util::numbers::*; +use v1::types::U256; /// Sync info #[derive(Default, Debug, Serialize, PartialEq)] @@ -53,7 +53,7 @@ impl Serialize for SyncStatus { #[cfg(test)] mod tests { use serde_json; - use super::*; + use super::{SyncInfo, SyncStatus}; #[test] fn test_serialize_sync_info() { diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index d6226aea0..cc193c9fc 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -15,14 +15,14 @@ // along with Parity. If not, see . use std::collections::BTreeMap; -use util::{Address, U256, H256, Uint}; use serde::{Serialize, Serializer}; use ethcore::trace::trace; use ethcore::trace::{Trace as EthTrace, LocalizedTrace as EthLocalizedTrace}; use ethcore::trace as et; use ethcore::state_diff; use ethcore::account_diff; -use v1::types::Bytes; +use util::Uint; +use v1::types::{Bytes, H160, H256, U256}; #[derive(Debug, Serialize)] /// A diff of some chunk of memory. @@ -54,8 +54,8 @@ pub struct StorageDiff { impl From for StorageDiff { fn from(c: et::StorageDiff) -> Self { StorageDiff { - key: c.location, - val: c.value, + key: c.location.into(), + val: c.value.into(), } } } @@ -80,9 +80,9 @@ impl From for VMExecutedOperation { fn from(c: et::VMExecutedOperation) -> Self { VMExecutedOperation { used: c.gas_used.low_u64(), - push: c.stack_push, - mem: c.mem_diff.map(From::from), - store: c.store_diff.map(From::from), + push: c.stack_push.into_iter().map(Into::into).collect(), + mem: c.mem_diff.map(Into::into), + store: c.store_diff.map(Into::into), } } } @@ -105,8 +105,8 @@ impl From<(et::VMOperation, Option)> for VMOperation { VMOperation { pc: c.0.pc, cost: c.0.gas_cost.low_u64(), - ex: c.0.executed.map(From::from), - sub: c.1.map(From::from), + ex: c.0.executed.map(Into::into), + sub: c.1.map(Into::into), } } } @@ -188,13 +188,13 @@ impl From for AccountDiff { balance: c.balance.into(), nonce: c.nonce.into(), code: c.code.into(), - storage: c.storage.into_iter().map(|(k, v)| (k, v.into())).collect(), + storage: c.storage.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), } } } /// Serde-friendly `StateDiff` shadow. -pub struct StateDiff(BTreeMap); +pub struct StateDiff(BTreeMap); impl Serialize for StateDiff { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -205,7 +205,7 @@ impl Serialize for StateDiff { impl From for StateDiff { fn from(c: state_diff::StateDiff) -> Self { - StateDiff(c.0.into_iter().map(|(k, v)| (k, v.into())).collect()) + StateDiff(c.0.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) } } @@ -213,7 +213,7 @@ impl From for StateDiff { #[derive(Debug, Serialize)] pub struct Create { /// Sender - from: Address, + from: H160, /// Value value: U256, /// Gas @@ -225,9 +225,9 @@ pub struct Create { impl From for Create { fn from(c: trace::Create) -> Self { Create { - from: c.from, - value: c.value, - gas: c.gas, + from: c.from.into(), + value: c.value.into(), + gas: c.gas.into(), init: Bytes::new(c.init), } } @@ -237,9 +237,9 @@ impl From for Create { #[derive(Debug, Serialize)] pub struct Call { /// Sender - from: Address, + from: H160, /// Recipient - to: Address, + to: H160, /// Transfered Value value: U256, /// Gas @@ -251,11 +251,11 @@ pub struct Call { impl From for Call { fn from(c: trace::Call) -> Self { Call { - from: c.from, - to: c.to, - value: c.value, - gas: c.gas, - input: Bytes::new(c.input), + from: c.from.into(), + to: c.to.into(), + value: c.value.into(), + gas: c.gas.into(), + input: c.input.into(), } } } @@ -293,8 +293,8 @@ pub struct CallResult { impl From for CallResult { fn from(c: trace::CallResult) -> Self { CallResult { - gas_used: c.gas_used, - output: Bytes::new(c.output), + gas_used: c.gas_used.into(), + output: c.output.into(), } } } @@ -308,15 +308,15 @@ pub struct CreateResult { /// Code code: Bytes, /// Assigned address - address: Address, + address: H160, } impl From for CreateResult { fn from(c: trace::CreateResult) -> Self { CreateResult { - gas_used: c.gas_used, - code: Bytes::new(c.code), - address: c.address, + gas_used: c.gas_used.into(), + code: c.code.into(), + address: c.address.into(), } } } @@ -357,7 +357,7 @@ pub struct LocalizedTrace { /// Result result: Res, /// Trace address - #[serde(rename="traceAddress")] + #[serde(rename="traceH160")] trace_address: Vec, /// Subtraces subtraces: U256, @@ -378,14 +378,14 @@ pub struct LocalizedTrace { impl From for LocalizedTrace { fn from(t: EthLocalizedTrace) -> Self { LocalizedTrace { - action: From::from(t.action), - result: From::from(t.result), - trace_address: t.trace_address.into_iter().map(From::from).collect(), - subtraces: From::from(t.subtraces), - transaction_position: From::from(t.transaction_number), - transaction_hash: t.transaction_hash, - block_number: From::from(t.block_number), - block_hash: t.block_hash, + action: t.action.into(), + result: t.result.into(), + trace_address: t.trace_address.into_iter().map(Into::into).collect(), + subtraces: t.subtraces.into(), + transaction_position: t.transaction_number.into(), + transaction_hash: t.transaction_hash.into(), + block_number: t.block_number.into(), + block_hash: t.block_hash.into(), } } } @@ -409,7 +409,7 @@ impl From for Trace { depth: t.depth.into(), action: t.action.into(), result: t.result.into(), - subtraces: t.subs.into_iter().map(From::from).collect(), + subtraces: t.subs.into_iter().map(Into::into).collect(), } } } @@ -418,23 +418,22 @@ impl From for Trace { mod tests { use serde_json; use std::collections::BTreeMap; - use util::{U256, H256, Address}; - use v1::types::Bytes; + use v1::types::{Bytes, U256, H256, H160}; use super::*; #[test] fn test_trace_serialize() { let t = LocalizedTrace { action: Action::Call(Call { - from: Address::from(4), - to: Address::from(5), + from: H160::from(4), + to: H160::from(5), value: U256::from(6), gas: U256::from(7), input: Bytes::new(vec![0x12, 0x34]), }), result: Res::Call(CallResult { gas_used: U256::from(8), - output: Bytes::new(vec![0x56, 0x78]), + output: vec![0x56, 0x78].into(), }), trace_address: vec![U256::from(10)], subtraces: U256::from(1), @@ -444,7 +443,7 @@ mod tests { block_hash: H256::from(14), }; let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234"}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceAddress":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); + assert_eq!(serialized, r#"{"action":{"call":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x06","gas":"0x07","input":"0x1234"}},"result":{"call":{"gasUsed":"0x08","output":"0x5678"}},"traceH160":["0x0a"],"subtraces":"0x01","transactionPosition":"0x0b","transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":"0x0d","blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); } #[test] @@ -515,16 +514,16 @@ mod tests { #[test] fn test_action_serialize() { let actions = vec![Action::Call(Call { - from: Address::from(1), - to: Address::from(2), + from: H160::from(1), + to: H160::from(2), value: U256::from(3), gas: U256::from(4), - input: Bytes::new(vec![0x12, 0x34]), + input: vec![0x12, 0x34].into(), }), Action::Create(Create { - from: Address::from(5), + from: H160::from(5), value: U256::from(6), gas: U256::from(7), - init: Bytes::new(vec![0x56, 0x78]), + init: vec![0x56, 0x78].into(), })]; let serialized = serde_json::to_string(&actions).unwrap(); @@ -536,12 +535,12 @@ mod tests { let results = vec![ Res::Call(CallResult { gas_used: U256::from(1), - output: Bytes::new(vec![0x12, 0x34]), + output: vec![0x12, 0x34].into(), }), Res::Create(CreateResult { gas_used: U256::from(2), - code: Bytes::new(vec![0x45, 0x56]), - address: Address::from(3), + code: vec![0x45, 0x56].into(), + address: H160::from(3), }), Res::FailedCall, Res::FailedCreate, diff --git a/rpc/src/v1/types/trace_filter.rs b/rpc/src/v1/types/trace_filter.rs index ee2f231f0..21e50e175 100644 --- a/rpc/src/v1/types/trace_filter.rs +++ b/rpc/src/v1/types/trace_filter.rs @@ -16,10 +16,9 @@ //! Trace filter deserialization. -use util::Address; use ethcore::client::BlockID; use ethcore::client; -use super::BlockNumber; +use v1::types::{BlockNumber, H160}; /// Trace filter #[derive(Debug, PartialEq, Deserialize)] @@ -32,10 +31,10 @@ pub struct TraceFilter { pub to_block: Option, /// From address #[serde(rename="fromAddress")] - pub from_address: Option>, + pub from_address: Option>, /// To address #[serde(rename="toAddress")] - pub to_address: Option>, + pub to_address: Option>, } impl Into for TraceFilter { @@ -44,8 +43,8 @@ impl Into for TraceFilter { let end = self.to_block.map_or(BlockID::Latest, Into::into); client::TraceFilter { range: start..end, - from_address: self.from_address.unwrap_or_else(Vec::new), - to_address: self.to_address.unwrap_or_else(Vec::new), + from_address: self.from_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), + to_address: self.to_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), } } } @@ -80,8 +79,8 @@ mod tests { assert_eq!(deserialized, TraceFilter { from_block: Some(BlockNumber::Latest), to_block: Some(BlockNumber::Latest), - from_address: Some(vec![Address::from(3)]), - to_address: Some(vec![Address::from(5)]), + from_address: Some(vec![Address::from(3).into()]), + to_address: Some(vec![Address::from(5).into()]), }); } } diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 6a9f0e590..cb554b172 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use util::numbers::*; use ethcore::contract_address; use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; -use v1::types::{Bytes, OptionalValue}; +use v1::types::{Bytes, H160, H256, U256}; /// Transaction #[derive(Debug, Default, Serialize)] @@ -28,17 +27,17 @@ pub struct Transaction { pub nonce: U256, /// Block hash #[serde(rename="blockHash")] - pub block_hash: OptionalValue, + pub block_hash: Option, /// Block number #[serde(rename="blockNumber")] - pub block_number: OptionalValue, + pub block_number: Option, /// Transaction Index #[serde(rename="transactionIndex")] - pub transaction_index: OptionalValue, + pub transaction_index: Option, /// Sender - pub from: Address, + pub from: H160, /// Recipient - pub to: OptionalValue
, + pub to: Option, /// Transfered value pub value: U256, /// Gas Price @@ -49,29 +48,29 @@ pub struct Transaction { /// Data pub input: Bytes, /// Creates contract - pub creates: OptionalValue
, + pub creates: Option, } impl From for Transaction { fn from(t: LocalizedTransaction) -> Transaction { Transaction { - hash: t.hash(), - nonce: t.nonce, - block_hash: OptionalValue::Value(t.block_hash.clone()), - block_number: OptionalValue::Value(U256::from(t.block_number)), - transaction_index: OptionalValue::Value(U256::from(t.transaction_index)), - from: t.sender().unwrap(), + hash: t.hash().into(), + nonce: t.nonce.into(), + block_hash: Some(t.block_hash.clone().into()), + block_number: Some(t.block_number.into()), + transaction_index: Some(t.transaction_index.into()), + from: t.sender().unwrap().into(), to: match t.action { - Action::Create => OptionalValue::Null, - Action::Call(ref address) => OptionalValue::Value(address.clone()) + Action::Create => None, + Action::Call(ref address) => Some(address.clone().into()) }, - value: t.value, - gas_price: t.gas_price, - gas: t.gas, + value: t.value.into(), + gas_price: t.gas_price.into(), + gas: t.gas.into(), input: Bytes::new(t.data.clone()), creates: match t.action { - Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), - Action::Call(_) => OptionalValue::Null, + Action::Create => Some(contract_address(&t.sender().unwrap(), &t.nonce).into()), + Action::Call(_) => None, }, } } @@ -80,23 +79,23 @@ impl From for Transaction { impl From for Transaction { fn from(t: SignedTransaction) -> Transaction { Transaction { - hash: t.hash(), - nonce: t.nonce, - block_hash: OptionalValue::Null, - block_number: OptionalValue::Null, - transaction_index: OptionalValue::Null, - from: t.sender().unwrap(), + hash: t.hash().into(), + nonce: t.nonce.into(), + block_hash: None, + block_number: None, + transaction_index: None, + from: t.sender().unwrap().into(), to: match t.action { - Action::Create => OptionalValue::Null, - Action::Call(ref address) => OptionalValue::Value(address.clone()) + Action::Create => None, + Action::Call(ref address) => Some(address.clone().into()) }, - value: t.value, - gas_price: t.gas_price, - gas: t.gas, + value: t.value.into(), + gas_price: t.gas_price.into(), + gas: t.gas.into(), input: Bytes::new(t.data.clone()), creates: match t.action { - Action::Create => OptionalValue::Value(contract_address(&t.sender().unwrap(), &t.nonce)), - Action::Call(_) => OptionalValue::Null, + Action::Create => Some(contract_address(&t.sender().unwrap(), &t.nonce).into()), + Action::Call(_) => None, }, } } @@ -104,7 +103,7 @@ impl From for Transaction { #[cfg(test)] mod tests { - use super::*; + use super::Transaction; use serde_json; #[test] diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index e7237b2c6..f697144b3 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -16,17 +16,16 @@ //! `TransactionRequest` type -use util::hash::Address; -use util::numbers::U256; -use v1::types::bytes::Bytes; +use v1::types::{Bytes, H160, U256}; +use v1::helpers::{TransactionRequest as Request, TransactionConfirmation as Confirmation}; /// Transaction request coming from RPC #[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct TransactionRequest { /// Sender - pub from: Address, + pub from: H160, /// Recipient - pub to: Option
, + pub to: Option, /// Gas Price #[serde(rename="gasPrice")] pub gas_price: Option, @@ -40,6 +39,34 @@ pub struct TransactionRequest { pub nonce: Option, } +impl From for TransactionRequest { + fn from(r: Request) -> Self { + TransactionRequest { + from: r.from.into(), + to: r.to.map(Into::into), + gas_price: r.gas_price.map(Into::into), + gas: r.gas.map(Into::into), + value: r.value.map(Into::into), + data: r.data.map(Into::into), + nonce: r.nonce.map(Into::into), + } + } +} + +impl Into for TransactionRequest { + fn into(self) -> Request { + Request { + from: self.from.into(), + to: self.to.map(Into::into), + gas_price: self.gas_price.map(Into::into), + gas: self.gas.map(Into::into), + value: self.value.map(Into::into), + data: self.data.map(Into::into), + nonce: self.nonce.map(Into::into), + } + } +} + /// Transaction confirmation waiting in a queue #[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize)] pub struct TransactionConfirmation { @@ -49,6 +76,15 @@ pub struct TransactionConfirmation { pub transaction: TransactionRequest, } +impl From for TransactionConfirmation { + fn from(c: Confirmation) -> Self { + TransactionConfirmation { + id: c.id.into(), + transaction: c.transaction.into(), + } + } +} + /// Possible modifications to the confirmed transaction sent by `SignerUI` #[derive(Debug, PartialEq, Deserialize)] pub struct TransactionModification { @@ -63,9 +99,7 @@ mod tests { use std::str::FromStr; use rustc_serialize::hex::FromHex; use serde_json; - use util::numbers::{U256}; - use util::hash::Address; - use v1::types::bytes::Bytes; + use v1::types::{U256, H160}; use super::*; #[test] @@ -82,12 +116,12 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Address::from(1), - to: Some(Address::from(2)), + from: H160::from(1), + to: Some(H160::from(2)), gas_price: Some(U256::from(1)), gas: Some(U256::from(2)), value: Some(U256::from(3)), - data: Some(Bytes::new(vec![0x12, 0x34, 0x56])), + data: Some(vec![0x12, 0x34, 0x56].into()), nonce: Some(U256::from(4)), }); } @@ -105,12 +139,12 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Address::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), - to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + from: H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), + to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()), gas: Some(U256::from_str("76c0").unwrap()), value: Some(U256::from_str("9184e72a").unwrap()), - data: Some(Bytes::new("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap())), + data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), nonce: None }); } @@ -121,7 +155,7 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Address::from(1), + from: H160::from(1).into(), to: None, gas_price: None, gas: None, @@ -144,12 +178,12 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Address::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap(), - to: Some(Address::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()), + from: H160::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap(), + to: Some(H160::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()), gas_price: Some(U256::from_str("0ba43b7400").unwrap()), gas: Some(U256::from_str("2fd618").unwrap()), value: None, - data: Some(Bytes::new(vec![0x85, 0x95, 0xba, 0xb1])), + data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), nonce: None, }); } diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs new file mode 100644 index 000000000..5111f3a42 --- /dev/null +++ b/rpc/src/v1/types/uint.rs @@ -0,0 +1,89 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::cmp; +use std::str::FromStr; +use rustc_serialize::hex::ToHex; +use serde; +use util::{U256 as EthU256, Uint}; + +macro_rules! impl_uint { + ($name: ident, $other: ident, $size: expr) => { + /// Uint serialization. + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + pub struct $name($other); + + impl From for $name where $other: From { + fn from(o: T) -> Self { + $name($other::from(o)) + } + } + + impl FromStr for $name { + type Err = <$other as FromStr>::Err; + + fn from_str(s: &str) -> Result { + $other::from_str(s).map($name) + } + } + + impl Into<$other> for $name { + fn into(self) -> $other { + self.0 + } + } + + impl serde::Serialize for $name { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer { + let mut hex = "0x".to_owned(); + let mut bytes = [0u8; 8 * $size]; + self.0.to_raw_bytes(&mut bytes); + let len = cmp::max((self.0.bits() + 7) / 8, 1); + hex.push_str(&bytes[bytes.len() - len..].to_hex()); + serializer.serialize_str(&hex) + } + } + + impl serde::Deserialize for $name { + fn deserialize(deserializer: &mut D) -> Result<$name, D::Error> + where D: serde::Deserializer { + struct UintVisitor; + + impl serde::de::Visitor for UintVisitor { + type Value = $name; + + fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { + // 0x + len + if value.len() > 2 + $size * 16 || value.len() < 2 { + return Err(serde::Error::custom("Invalid length.")); + } + + $other::from_str(&value[2..]).map($name).map_err(|_| serde::Error::custom("Invalid hex value.")) + } + + fn visit_string(&mut self, value: String) -> Result where E: serde::Error { + self.visit_str(&value) + } + } + + deserializer.deserialize(UintVisitor) + } + } + + } +} + +impl_uint!(U256, EthU256, 4); diff --git a/sync/src/lib.rs b/sync/src/lib.rs index fa26e7d85..cf1906511 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -71,8 +71,7 @@ extern crate heapsize; use std::ops::*; use std::sync::*; use util::network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId}; -use util::TimerToken; -use util::{U256, ONE_U256}; +use util::{TimerToken, U256}; use ethcore::client::Client; use ethcore::service::{SyncMessage, NetSyncMessage}; use io::NetSyncIo; @@ -99,7 +98,7 @@ impl Default for SyncConfig { fn default() -> SyncConfig { SyncConfig { max_download_ahead_blocks: 20000, - network_id: ONE_U256, + network_id: U256::from(1), } } } diff --git a/util/Cargo.toml b/util/Cargo.toml index 07a0713e3..cfb7acaae 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -27,15 +27,16 @@ itertools = "0.4" crossbeam = "0.2" slab = "0.2" sha3 = { path = "sha3" } -serde = "0.7.0" clippy = { version = "0.0.78", optional = true} -igd = "0.4.2" +igd = "0.5.0" ethcore-devtools = { path = "../devtools" } libc = "0.2.7" vergen = "0.1" target_info = "0.1" bigint = { path = "bigint" } chrono = "0.2" +using_queue = { path = "using_queue" } +table = { path = "table" } ansi_term = "0.7" [features] diff --git a/util/bigint/Cargo.toml b/util/bigint/Cargo.toml index fe9101df7..357e2956c 100644 --- a/util/bigint/Cargo.toml +++ b/util/bigint/Cargo.toml @@ -12,7 +12,6 @@ rustc_version = "0.1" [dependencies] rustc-serialize = "0.3" -serde = "0.7.0" heapsize = "0.3" [features] diff --git a/util/bigint/src/lib.rs b/util/bigint/src/lib.rs index 149878538..1d97871e3 100644 --- a/util/bigint/src/lib.rs +++ b/util/bigint/src/lib.rs @@ -17,7 +17,6 @@ #![cfg_attr(asm_available, feature(asm))] extern crate rustc_serialize; -extern crate serde; #[macro_use] extern crate heapsize; pub mod uint; diff --git a/util/bigint/src/uint.rs b/util/bigint/src/uint.rs index 5304781d4..2f58061d4 100644 --- a/util/bigint/src/uint.rs +++ b/util/bigint/src/uint.rs @@ -39,7 +39,6 @@ #[cfg(all(asm_available, target_arch="x86_64"))] use std::mem; use std::fmt; -use std::cmp; use std::str::{FromStr}; use std::convert::From; @@ -47,14 +46,15 @@ use std::hash::Hash; use std::ops::*; use std::cmp::*; -use serde; -use rustc_serialize::hex::{FromHex, FromHexError, ToHex}; +use rustc_serialize::hex::{FromHex, FromHexError}; /// Conversion from decimal string error #[derive(Debug, PartialEq)] pub enum FromDecStrErr { + /// Char not from range 0-9 + InvalidCharacter, /// Value does not fit into type - InvalidLength + InvalidLength, } macro_rules! impl_map_from { @@ -562,8 +562,11 @@ macro_rules! construct_uint { impl Uint for $name { - /// TODO: optimize, throw appropriate err fn from_dec_str(value: &str) -> Result { + if value.bytes().any(|b| b < 48 && b > 57) { + return Err(FromDecStrErr::InvalidCharacter) + } + let mut res = Self::default(); for b in value.bytes().map(|b| b - 48) { let (r, overflow) = res.overflowing_mul_u32(10); @@ -649,7 +652,7 @@ macro_rules! construct_uint { fn exp10(n: usize) -> Self { match n { 0 => Self::from(1u64), - _ => Self::exp10(n - 1) * Self::from(10u64) + _ => Self::exp10(n - 1).mul_u32(10) } } @@ -757,16 +760,16 @@ macro_rules! construct_uint { } impl $name { - #[allow(dead_code)] // not used when multiplied with inline assembly /// Multiplication by u32 + #[allow(dead_code)] // not used when multiplied with inline assembly fn mul_u32(self, other: u32) -> Self { let (ret, overflow) = self.overflowing_mul_u32(other); panic_on_overflow!(overflow); ret } - #[allow(dead_code)] // not used when multiplied with inline assembly /// Overflowing multiplication by u32 + #[allow(dead_code)] // not used when multiplied with inline assembly fn overflowing_mul_u32(self, other: u32) -> (Self, bool) { let $name(ref arr) = self; let mut ret = [0u64; $n_words]; @@ -789,44 +792,6 @@ macro_rules! construct_uint { } } - impl serde::Serialize for $name { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: serde::Serializer { - let mut hex = "0x".to_owned(); - let mut bytes = [0u8; 8 * $n_words]; - self.to_raw_bytes(&mut bytes); - let len = cmp::max((self.bits() + 7) / 8, 1); - hex.push_str(bytes[bytes.len() - len..].to_hex().as_ref()); - serializer.serialize_str(hex.as_ref()) - } - } - - impl serde::Deserialize for $name { - fn deserialize(deserializer: &mut D) -> Result<$name, D::Error> - where D: serde::Deserializer { - struct UintVisitor; - - impl serde::de::Visitor for UintVisitor { - type Value = $name; - - fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { - // 0x + len - if value.len() > 2 + $n_words * 16 || value.len() < 2 { - return Err(serde::Error::custom("Invalid length.")); - } - - $name::from_str(&value[2..]).map_err(|_| serde::Error::custom("Invalid hex value.")) - } - - fn visit_string(&mut self, value: String) -> Result where E: serde::Error { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize(UintVisitor) - } - } - impl From for $name { fn from(value: u64) -> $name { let mut ret = [0; $n_words]; @@ -959,8 +924,6 @@ macro_rules! construct_uint { } } - // TODO: optimise and traitify. - impl BitAnd<$name> for $name { type Output = $name; @@ -1434,12 +1397,6 @@ impl From for u32 { } } -/// Constant value of `U256::zero()` that can be used for a reference saving an additional instance creation. -pub const ZERO_U256: U256 = U256([0x00u64; 4]); -/// Constant value of `U256::one()` that can be used for a reference saving an additional instance creation. -pub const ONE_U256: U256 = U256([0x01u64, 0x00u64, 0x00u64, 0x00u64]); - - known_heap_size!(0, U128, U256); #[cfg(test)] @@ -1582,7 +1539,13 @@ mod tests { assert_eq!(U256::from(105u8) / U256::from(5u8), U256::from(21u8)); let div = mult / U256::from(300u16); assert_eq!(div, U256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0])); - //// TODO: bit inversion + + let a = U256::from_str("ff000000000000000000000000000000000000000000000000000000000000d1").unwrap(); + let b = U256::from_str("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2e").unwrap(); + println!("{:x}", a); + println!("{:x}", b); + assert_eq!(!a, b); + assert_eq!(a, !b); } #[test] diff --git a/util/src/bytes.rs b/util/src/bytes.rs index 5ef2044ee..4e3d73da8 100644 --- a/util/src/bytes.rs +++ b/util/src/bytes.rs @@ -24,7 +24,7 @@ //! use util::bytes::BytesConvertable; //! //! let arr = [0; 5]; -//! let slice: &[u8] = arr.bytes(); +//! let slice: &[u8] = arr.as_slice(); //! } //! //! fn main() { @@ -120,12 +120,12 @@ impl<'a> ToPretty for &'a [u8] { impl<'a> ToPretty for &'a Bytes { fn pretty(&self) -> PrettySlice { - PrettySlice(self.bytes()) + PrettySlice(self.as_slice()) } } impl ToPretty for Bytes { fn pretty(&self) -> PrettySlice { - PrettySlice(self.bytes()) + PrettySlice(self.as_slice()) } } @@ -162,23 +162,19 @@ pub type Bytes = Vec; /// Slice of bytes to underlying memory pub trait BytesConvertable { - // TODO: rename to as_slice /// Get the underlying byte-wise representation of the value. - /// Deprecated - use `as_slice` instead. - fn bytes(&self) -> &[u8]; - /// Get the underlying byte-wise representation of the value. - fn as_slice(&self) -> &[u8] { self.bytes() } + fn as_slice(&self) -> &[u8]; /// Get a copy of the underlying byte-wise representation. fn to_bytes(&self) -> Bytes { self.as_slice().to_vec() } } impl BytesConvertable for T where T: AsRef<[u8]> { - fn bytes(&self) -> &[u8] { self.as_ref() } + fn as_slice(&self) -> &[u8] { self.as_ref() } } #[test] fn bytes_convertable() { - assert_eq!(vec![0x12u8, 0x34].bytes(), &[0x12u8, 0x34]); + assert_eq!(vec![0x12u8, 0x34].as_slice(), &[0x12u8, 0x34]); assert!([0u8; 0].as_slice().is_empty()); } diff --git a/util/src/hash.rs b/util/src/hash.rs index 16f0bde9e..95e8f8c8c 100644 --- a/util/src/hash.rs +++ b/util/src/hash.rs @@ -16,16 +16,18 @@ //! General hash types, a fixed-size raw-data type used as the output of hash functions. -use standard::*; +use rustc_serialize::hex::FromHex; +use std::{ops, fmt, cmp}; +use std::cmp::*; +use std::ops::*; +use std::hash::{Hash, Hasher}; +use std::str::FromStr; use math::log2; use error::UtilError; use rand::Rng; use rand::os::OsRng; use bytes::{BytesConvertable,Populatable}; -use from_json::*; use bigint::uint::{Uint, U256}; -use rustc_serialize::hex::ToHex; -use serde; /// Trait for a fixed-size byte array to be used as the output of hash functions. /// @@ -228,55 +230,6 @@ macro_rules! impl_hash { } } - impl serde::Serialize for $from { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: serde::Serializer { - let mut hex = "0x".to_owned(); - hex.push_str(self.to_hex().as_ref()); - serializer.serialize_str(hex.as_ref()) - } - } - - impl serde::Deserialize for $from { - fn deserialize(deserializer: &mut D) -> Result<$from, D::Error> - where D: serde::Deserializer { - struct HashVisitor; - - impl serde::de::Visitor for HashVisitor { - type Value = $from; - - fn visit_str(&mut self, value: &str) -> Result where E: serde::Error { - // 0x + len - if value.len() != 2 + $size * 2 { - return Err(serde::Error::custom("Invalid length.")); - } - - value[2..].from_hex().map(|ref v| $from::from_slice(v)).map_err(|_| serde::Error::custom("Invalid hex value.")) - } - - fn visit_string(&mut self, value: String) -> Result where E: serde::Error { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize(HashVisitor) - } - } - - impl FromJson for $from { - fn from_json(json: &Json) -> Self { - match *json { - Json::String(ref s) => { - match s.len() % 2 { - 0 => FromStr::from_str(clean_0x(s)).unwrap(), - _ => FromStr::from_str(&("0".to_owned() + &(clean_0x(s).to_owned()))[..]).unwrap() - } - }, - _ => Default::default(), - } - } - } - impl fmt::Debug for $from { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for i in &self.0[..] { @@ -285,6 +238,7 @@ macro_rules! impl_hash { Ok(()) } } + impl fmt::Display for $from { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for i in &self.0[0..2] { @@ -506,13 +460,13 @@ impl<'a> From<&'a U256> for H256 { impl From for U256 { fn from(value: H256) -> U256 { - U256::from(value.bytes()) + U256::from(value.as_slice()) } } impl<'a> From<&'a H256> for U256 { fn from(value: &'a H256) -> U256 { - U256::from(value.bytes()) + U256::from(value.as_slice()) } } @@ -532,17 +486,6 @@ impl From for H64 { } } -/* -impl<'a> From<&'a H256> for Address { - fn from(value: &'a H256) -> Address { - let mut ret = Address::new(); - ret.0.copy_from_slice(&value[12..32]); - ret - } - } -} -*/ - impl From
for H256 { fn from(value: Address) -> H256 { let mut ret = H256::new(); @@ -596,11 +539,6 @@ impl_hash!(H520, 65); impl_hash!(H1024, 128); impl_hash!(H2048, 256); -/// Constant address for point 0. Often used as a default. -pub static ZERO_ADDRESS: Address = Address([0x00; 20]); -/// Constant 256-bit datum for 0. Often used as a default. -pub static ZERO_H256: H256 = H256([0x00; 32]); - #[cfg(test)] mod tests { use hash::*; diff --git a/util/src/journaldb/archivedb.rs b/util/src/journaldb/archivedb.rs index 40172cccd..ca436d7ea 100644 --- a/util/src/journaldb/archivedb.rs +++ b/util/src/journaldb/archivedb.rs @@ -79,7 +79,7 @@ impl ArchiveDB { } fn payload(&self, key: &H256) -> Option { - self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) + self.backing.get(key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) } } @@ -177,7 +177,7 @@ impl JournalDB for ArchiveDB { let (key, (value, rc)) = i; if rc > 0 { assert!(rc == 1); - batch.put(&key.bytes(), &value).expect("Low-level database error. Some issue with your hard disk?"); + batch.put(&key, &value).expect("Low-level database error. Some issue with your hard disk?"); inserts += 1; } if rc < 0 { @@ -202,7 +202,7 @@ impl JournalDB for ArchiveDB { fn latest_era(&self) -> Option { self.latest_era } fn state(&self, id: &H256) -> Option { - self.backing.get_by_prefix(&id.bytes()[0..12]).and_then(|b| Some(b.to_vec())) + self.backing.get_by_prefix(&id[0..12]).and_then(|b| Some(b.to_vec())) } fn is_pruned(&self) -> bool { false } diff --git a/util/src/journaldb/earlymergedb.rs b/util/src/journaldb/earlymergedb.rs index 2b87f8345..b860ae598 100644 --- a/util/src/journaldb/earlymergedb.rs +++ b/util/src/journaldb/earlymergedb.rs @@ -106,7 +106,7 @@ impl EarlyMergeDB { } fn morph_key(key: &H256, index: u8) -> Bytes { - let mut ret = key.bytes().to_owned(); + let mut ret = key.to_bytes(); ret.push(index); ret } @@ -130,7 +130,7 @@ impl EarlyMergeDB { } // this is the first entry for this node in the journal. - if backing.get(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?").is_some() { + if backing.get(h).expect("Low-level database error. Some issue with your hard disk?").is_some() { // already in the backing DB. start counting, and remember it was already in. Self::set_already_in(batch, &h); refs.insert(h.clone(), RefInfo{queue_refs: 1, in_archive: true}); @@ -143,7 +143,7 @@ impl EarlyMergeDB { // Gets removed when a key leaves the journal, so should never be set when we're placing a new key. //Self::reset_already_in(&h); assert!(!Self::is_already_in(backing, &h)); - batch.put(&h.bytes(), d).expect("Low-level database error. Some issue with your hard disk?"); + batch.put(h, d).expect("Low-level database error. Some issue with your hard disk?"); refs.insert(h.clone(), RefInfo{queue_refs: 1, in_archive: false}); if trace { trace!(target: "jdb.fine", " insert({}): New to queue, not in DB: Inserting into queue and DB", h); @@ -204,7 +204,7 @@ impl EarlyMergeDB { } Some(RefInfo{queue_refs: 1, in_archive: false}) => { refs.remove(h); - batch.delete(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?"); + batch.delete(h).expect("Low-level database error. Some issue with your hard disk?"); if trace { trace!(target: "jdb.fine", " remove({}): Not in archive, only 1 ref in queue: Removing from queue and DB", h); } @@ -212,7 +212,7 @@ impl EarlyMergeDB { None => { // Gets removed when moving from 1 to 0 additional refs. Should never be here at 0 additional refs. //assert!(!Self::is_already_in(db, &h)); - batch.delete(&h.bytes()).expect("Low-level database error. Some issue with your hard disk?"); + batch.delete(h).expect("Low-level database error. Some issue with your hard disk?"); if trace { trace!(target: "jdb.fine", " remove({}): Not in queue - MUST BE IN ARCHIVE: Removing from DB", h); } @@ -237,7 +237,7 @@ impl EarlyMergeDB { } fn payload(&self, key: &H256) -> Option { - self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) + self.backing.get(key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) } fn read_refs(db: &Database) -> (Option, HashMap) { diff --git a/util/src/journaldb/overlayrecentdb.rs b/util/src/journaldb/overlayrecentdb.rs index 0ffea308b..7afe7e5ac 100644 --- a/util/src/journaldb/overlayrecentdb.rs +++ b/util/src/journaldb/overlayrecentdb.rs @@ -141,7 +141,7 @@ impl OverlayRecentDB { } fn payload(&self, key: &H256) -> Option { - self.backing.get(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) + self.backing.get(key).expect("Low-level database error. Some issue with your hard disk?").map(|v| v.to_vec()) } fn read_overlay(db: &Database) -> JournalOverlay { diff --git a/util/src/lib.rs b/util/src/lib.rs index 2e4561d83..08480d85f 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -108,7 +108,6 @@ extern crate secp256k1; extern crate arrayvec; extern crate elastic_array; extern crate crossbeam; -extern crate serde; #[macro_use] extern crate log as rlog; extern crate igd; @@ -117,6 +116,8 @@ extern crate libc; extern crate target_info; extern crate bigint; extern crate chrono; +pub extern crate using_queue; +pub extern crate table; extern crate ansi_term; pub mod standard; @@ -130,7 +131,6 @@ pub mod hash; pub mod bytes; pub mod rlp; pub mod misc; -pub mod using_queue; pub mod vector; pub mod sha3; pub mod hashdb; @@ -151,14 +151,12 @@ pub mod io; pub mod network; pub mod log; pub mod panics; -pub mod table; pub mod network_settings; pub mod path; mod timer; pub use common::*; pub use misc::*; -pub use using_queue::*; pub use rlp::*; pub use hashdb::*; pub use memorydb::*; diff --git a/util/src/memorydb.rs b/util/src/memorydb.rs index f63dfd992..73435e40a 100644 --- a/util/src/memorydb.rs +++ b/util/src/memorydb.rs @@ -198,14 +198,14 @@ impl HashDB for MemoryDB { let key = value.sha3(); if match self.data.get_mut(&key) { Some(&mut (ref mut old_value, ref mut rc @ -0x80000000i32 ... 0)) => { - *old_value = From::from(value.bytes()); + *old_value = From::from(value); *rc += 1; false }, Some(&mut (_, ref mut x)) => { *x += 1; false } , None => true, }{ // ... None falls through into... - self.data.insert(key.clone(), (From::from(value.bytes()), 1)); + self.data.insert(key.clone(), (From::from(value), 1)); } key } @@ -262,8 +262,8 @@ fn memorydb_denote() { for _ in 0..1000 { let r = H256::random(); let k = r.sha3(); - let &(ref v, ref rc) = m.denote(&k, r.bytes().to_vec()); - assert_eq!(v, &r.bytes()); + let &(ref v, ref rc) = m.denote(&k, r.to_bytes()); + assert_eq!(v.as_slice(), r.as_slice()); assert_eq!(*rc, 0); } diff --git a/util/src/overlaydb.rs b/util/src/overlaydb.rs index b1ea44dac..63ec3dd50 100644 --- a/util/src/overlaydb.rs +++ b/util/src/overlaydb.rs @@ -172,10 +172,10 @@ impl OverlayDB { /// Get the refs and value of the given key. fn payload(&self, key: &H256) -> Option<(Bytes, u32)> { - self.backing.get(&key.bytes()) + self.backing.get(key) .expect("Low-level database error. Some issue with your hard disk?") .map(|d| { - let r = Rlp::new(d.deref()); + let r = Rlp::new(&d); (r.at(1).as_val(), r.at(0).as_val()) }) } @@ -186,10 +186,10 @@ impl OverlayDB { let mut s = RlpStream::new_list(2); s.append(&payload.1); s.append(&payload.0); - batch.put(&key.bytes(), s.as_raw()).expect("Low-level database error. Some issue with your hard disk?"); + batch.put(key, s.as_raw()).expect("Low-level database error. Some issue with your hard disk?"); false } else { - batch.delete(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?"); + batch.delete(key).expect("Low-level database error. Some issue with your hard disk?"); true } } @@ -200,10 +200,10 @@ impl OverlayDB { let mut s = RlpStream::new_list(2); s.append(&payload.1); s.append(&payload.0); - self.backing.put(&key.bytes(), s.as_raw()).expect("Low-level database error. Some issue with your hard disk?"); + self.backing.put(key, s.as_raw()).expect("Low-level database error. Some issue with your hard disk?"); false } else { - self.backing.delete(&key.bytes()).expect("Low-level database error. Some issue with your hard disk?"); + self.backing.delete(key).expect("Low-level database error. Some issue with your hard disk?"); true } } diff --git a/util/src/rlp/bytes.rs b/util/src/rlp/bytes.rs index d252af828..479fc7261 100644 --- a/util/src/rlp/bytes.rs +++ b/util/src/rlp/bytes.rs @@ -148,9 +148,9 @@ impl_uint_to_bytes!(U128); impl ToBytes for T where T: FixedHash { fn to_bytes>(&self, out: &mut V) { - out.vec_extend(self.bytes()); + out.vec_extend(self.as_slice()); } - fn to_bytes_len(&self) -> usize { self.bytes().len() } + fn to_bytes_len(&self) -> usize { self.as_slice().len() } } /// Error returned when `FromBytes` conversation goes wrong diff --git a/util/src/sha3.rs b/util/src/sha3.rs index 7e8382250..d2a071759 100644 --- a/util/src/sha3.rs +++ b/util/src/sha3.rs @@ -58,7 +58,7 @@ impl Hashable for T where T: BytesConvertable { } fn sha3_into(&self, dest: &mut [u8]) { unsafe { - let input: &[u8] = self.bytes(); + let input: &[u8] = self.as_slice(); sha3_256(dest.as_mut_ptr(), dest.len(), input.as_ptr(), input.len()); } } diff --git a/util/src/trie/standardmap.rs b/util/src/trie/standardmap.rs index b7f3a9500..216d29ad0 100644 --- a/util/src/trie/standardmap.rs +++ b/util/src/trie/standardmap.rs @@ -64,16 +64,16 @@ impl StandardMap { fn random_bytes(min_count: usize, journal_count: usize, seed: &mut H256) -> Vec { assert!(min_count + journal_count <= 32); *seed = seed.sha3(); - let r = min_count + (seed.bytes()[31] as usize % (journal_count + 1)); - seed.bytes()[0..r].to_vec() + let r = min_count + (seed[31] as usize % (journal_count + 1)); + seed[0..r].to_vec() } /// Get a random value. Equal chance of being 1 byte as of 32. `seed` is mutated pseudoramdonly and used. fn random_value(seed: &mut H256) -> Bytes { *seed = seed.sha3(); - match seed.bytes()[0] % 2 { - 1 => vec![seed.bytes()[31];1], - _ => seed.bytes().to_vec(), + match seed[0] % 2 { + 1 => vec![seed[31];1], + _ => seed.to_vec(), } } @@ -82,10 +82,10 @@ impl StandardMap { fn random_word(alphabet: &[u8], min_count: usize, journal_count: usize, seed: &mut H256) -> Vec { assert!(min_count + journal_count <= 32); *seed = seed.sha3(); - let r = min_count + (seed.bytes()[31] as usize % (journal_count + 1)); + let r = min_count + (seed[31] as usize % (journal_count + 1)); let mut ret: Vec = Vec::with_capacity(r); for i in 0..r { - ret.push(alphabet[seed.bytes()[i] as usize % alphabet.len()]); + ret.push(alphabet[seed[i] as usize % alphabet.len()]); } ret } diff --git a/util/table/Cargo.toml b/util/table/Cargo.toml new file mode 100644 index 000000000..57d47a8e9 --- /dev/null +++ b/util/table/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "table" +version = "0.1.0" +authors = ["debris "] + +[dependencies] diff --git a/util/src/table.rs b/util/table/src/lib.rs similarity index 100% rename from util/src/table.rs rename to util/table/src/lib.rs diff --git a/util/using_queue/Cargo.toml b/util/using_queue/Cargo.toml new file mode 100644 index 000000000..0708cbd00 --- /dev/null +++ b/util/using_queue/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "using_queue" +version = "0.1.0" +authors = ["debris "] + +[dependencies] diff --git a/util/src/using_queue.rs b/util/using_queue/src/lib.rs similarity index 99% rename from util/src/using_queue.rs rename to util/using_queue/src/lib.rs index e5e1a5a58..0daa66f59 100644 --- a/util/src/using_queue.rs +++ b/util/using_queue/src/lib.rs @@ -13,6 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . + //! Queue-like datastructure including notion of usage. /// Special queue-like datastructure that includes the notion of @@ -50,7 +51,7 @@ impl UsingQueue where T: Clone { pub fn peek_last_ref(&self) -> Option<&T> { self.pending.as_ref().or(self.in_use.last()) } - + /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); /// this constitutes using the item and will remain in the queue for at least another /// `max_size` invocations of `push()`. From a8b26e2cb508b0d0c158bcc1ab758950c457605d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 6 Jul 2016 05:24:29 -0400 Subject: [PATCH 20/21] Refactoring dapps to support API endpoints. (#1542) * Refactoring dapps to support API endpoints. * Using ContentHandler for unauthorized requests --- dapps/build.rs | 4 +- dapps/src/api/api.rs | 66 +++++++++------- dapps/src/api/mod.rs | 11 ++- dapps/src/api/response.rs | 9 ++- dapps/src/api/{mod.rs.in => types.rs} | 10 ++- dapps/src/api/types.rs.in | 51 ++++++++++++ dapps/src/endpoint.rs | 60 +------------- dapps/src/handlers/auth.rs | 44 +++++++++++ dapps/src/handlers/content.rs | 92 ++++++++++++++++++++++ dapps/src/handlers/mod.rs | 25 ++++++ dapps/src/{router => handlers}/redirect.rs | 0 dapps/src/lib.rs | 1 + dapps/src/proxypac.rs | 5 +- dapps/src/router/auth.rs | 83 +++---------------- dapps/src/router/mod.rs | 3 +- 15 files changed, 291 insertions(+), 173 deletions(-) rename dapps/src/api/{mod.rs.in => types.rs} (82%) create mode 100644 dapps/src/api/types.rs.in create mode 100644 dapps/src/handlers/auth.rs create mode 100644 dapps/src/handlers/content.rs create mode 100644 dapps/src/handlers/mod.rs rename dapps/src/{router => handlers}/redirect.rs (100%) diff --git a/dapps/build.rs b/dapps/build.rs index 2bef33059..0776c03ec 100644 --- a/dapps/build.rs +++ b/dapps/build.rs @@ -25,8 +25,8 @@ mod inner { pub fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); - let src = Path::new("./src/api/mod.rs.in"); - let dst = Path::new(&out_dir).join("mod.rs"); + let src = Path::new("./src/api/types.rs.in"); + let dst = Path::new(&out_dir).join("types.rs"); let mut registry = syntex::Registry::new(); diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs index 95b01d442..ab3632074 100644 --- a/dapps/src/api/api.rs +++ b/dapps/src/api/api.rs @@ -15,38 +15,16 @@ // along with Parity. If not, see . use std::sync::Arc; -use endpoint::{Endpoint, Endpoints, EndpointInfo, Handler, EndpointPath}; - -use api::response::as_json; +use endpoint::{Endpoint, Endpoints, Handler, EndpointPath}; +use api::types::{App, ApiError}; +use api::response::{as_json, as_json_error}; +use hyper::{server, net, Decoder, Encoder, Next}; +#[derive(Clone)] pub struct RestApi { endpoints: Arc, } -#[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct App { - pub id: String, - pub name: String, - pub description: String, - pub version: String, - pub author: String, - #[serde(rename="iconUrl")] - pub icon_url: String, -} - -impl App { - fn from_info(id: &str, info: &EndpointInfo) -> Self { - App { - id: id.to_owned(), - name: info.name.to_owned(), - description: info.description.to_owned(), - version: info.version.to_owned(), - author: info.author.to_owned(), - icon_url: info.icon_url.to_owned(), - } - } -} - impl RestApi { pub fn new(endpoints: Arc) -> Box { Box::new(RestApi { @@ -63,7 +41,39 @@ impl RestApi { impl Endpoint for RestApi { fn to_handler(&self, _path: EndpointPath) -> Box { - as_json(&self.list_apps()) + Box::new(RestApiRouter { + api: self.clone(), + handler: as_json_error(&ApiError { + code: "404".into(), + title: "Not Found".into(), + detail: "Resource you requested has not been found.".into(), + }), + }) } } +struct RestApiRouter { + api: RestApi, + handler: Box, +} + +impl server::Handler for RestApiRouter { + + fn on_request(&mut self, _request: server::Request) -> Next { + self.handler = as_json(&self.api.list_apps()); + Next::write() + } + + fn on_request_readable(&mut self, decoder: &mut Decoder) -> Next { + self.handler.on_request_readable(decoder) + } + + fn on_response(&mut self, res: &mut server::Response) -> Next { + self.handler.on_response(res) + } + + fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { + self.handler.on_response_writable(encoder) + } + +} diff --git a/dapps/src/api/mod.rs b/dapps/src/api/mod.rs index 088d7f6b2..402e84257 100644 --- a/dapps/src/api/mod.rs +++ b/dapps/src/api/mod.rs @@ -16,13 +16,12 @@ //! REST API -#![warn(missing_docs)] #![cfg_attr(feature="nightly", feature(custom_derive, custom_attribute, plugin))] #![cfg_attr(feature="nightly", plugin(serde_macros, clippy))] -#[cfg(feature = "serde_macros")] -include!("mod.rs.in"); - -#[cfg(not(feature = "serde_macros"))] -include!(concat!(env!("OUT_DIR"), "/mod.rs")); +mod api; +mod response; +mod types; +pub use self::api::RestApi; +pub use self::types::App; diff --git a/dapps/src/api/response.rs b/dapps/src/api/response.rs index 345b8a6ee..ba0922e7a 100644 --- a/dapps/src/api/response.rs +++ b/dapps/src/api/response.rs @@ -16,8 +16,13 @@ use serde::Serialize; use serde_json; -use endpoint::{ContentHandler, Handler}; +use endpoint::Handler; +use handlers::ContentHandler; pub fn as_json(val: &T) -> Box { - Box::new(ContentHandler::new(serde_json::to_string(val).unwrap(), "application/json".to_owned())) + Box::new(ContentHandler::ok(serde_json::to_string(val).unwrap(), "application/json".to_owned())) +} + +pub fn as_json_error(val: &T) -> Box { + Box::new(ContentHandler::not_found(serde_json::to_string(val).unwrap(), "application/json".to_owned())) } diff --git a/dapps/src/api/mod.rs.in b/dapps/src/api/types.rs similarity index 82% rename from dapps/src/api/mod.rs.in rename to dapps/src/api/types.rs index a069c06b0..d99d767eb 100644 --- a/dapps/src/api/mod.rs.in +++ b/dapps/src/api/types.rs @@ -14,8 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -mod api; -mod response; +#[cfg(feature = "serde_macros")] +include!("types.rs.in"); + +#[cfg(not(feature = "serde_macros"))] +include!(concat!(env!("OUT_DIR"), "/types.rs")); + -pub use self::api::RestApi; -pub use self::api::App; diff --git a/dapps/src/api/types.rs.in b/dapps/src/api/types.rs.in new file mode 100644 index 000000000..72a8cd221 --- /dev/null +++ b/dapps/src/api/types.rs.in @@ -0,0 +1,51 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use endpoint::EndpointInfo; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct App { + pub id: String, + pub name: String, + pub description: String, + pub version: String, + pub author: String, + #[serde(rename="iconUrl")] + pub icon_url: String, +} + +impl App { + /// Creates `App` instance from `EndpointInfo` and `id`. + pub fn from_info(id: &str, info: &EndpointInfo) -> Self { + App { + id: id.to_owned(), + name: info.name.to_owned(), + description: info.description.to_owned(), + version: info.version.to_owned(), + author: info.author.to_owned(), + icon_url: info.icon_url.to_owned(), + } + } +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct ApiError { + pub code: String, + pub title: String, + pub detail: String, +} + + diff --git a/dapps/src/endpoint.rs b/dapps/src/endpoint.rs index 075211e58..ba2116e1c 100644 --- a/dapps/src/endpoint.rs +++ b/dapps/src/endpoint.rs @@ -16,11 +16,7 @@ //! URL Endpoint traits -use hyper::status::StatusCode; -use hyper::{header, server, Decoder, Encoder, Next}; -use hyper::net::HttpStream; - -use std::io::Write; +use hyper::{server, net}; use std::collections::BTreeMap; #[derive(Debug, PartialEq, Default, Clone)] @@ -42,58 +38,8 @@ pub struct EndpointInfo { pub trait Endpoint : Send + Sync { fn info(&self) -> Option<&EndpointInfo> { None } - fn to_handler(&self, path: EndpointPath) -> Box + Send>; + fn to_handler(&self, path: EndpointPath) -> Box + Send>; } pub type Endpoints = BTreeMap>; -pub type Handler = server::Handler + Send; - -pub struct ContentHandler { - content: String, - mimetype: String, - write_pos: usize, -} - -impl ContentHandler { - pub fn new(content: String, mimetype: String) -> Self { - ContentHandler { - content: content, - mimetype: mimetype, - write_pos: 0 - } - } -} - -impl server::Handler for ContentHandler { - fn on_request(&mut self, _request: server::Request) -> Next { - Next::write() - } - - fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next { - Next::write() - } - - fn on_response(&mut self, res: &mut server::Response) -> Next { - res.set_status(StatusCode::Ok); - res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap())); - Next::write() - } - - fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { - let bytes = self.content.as_bytes(); - if self.write_pos == bytes.len() { - return Next::end(); - } - - match encoder.write(&bytes[self.write_pos..]) { - Ok(bytes) => { - self.write_pos += bytes; - Next::write() - }, - Err(e) => match e.kind() { - ::std::io::ErrorKind::WouldBlock => Next::write(), - _ => Next::end() - }, - } - } -} +pub type Handler = server::Handler + Send; diff --git a/dapps/src/handlers/auth.rs b/dapps/src/handlers/auth.rs new file mode 100644 index 000000000..7f72f7cf8 --- /dev/null +++ b/dapps/src/handlers/auth.rs @@ -0,0 +1,44 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Authorization Handlers + +use hyper::{server, Decoder, Encoder, Next}; +use hyper::net::HttpStream; +use hyper::status::StatusCode; + +pub struct AuthRequiredHandler; + +impl server::Handler for AuthRequiredHandler { + fn on_request(&mut self, _request: server::Request) -> Next { + Next::write() + } + + fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next { + Next::write() + } + + fn on_response(&mut self, res: &mut server::Response) -> Next { + res.set_status(StatusCode::Unauthorized); + res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]); + Next::write() + } + + fn on_response_writable(&mut self, _encoder: &mut Encoder) -> Next { + Next::end() + } +} + diff --git a/dapps/src/handlers/content.rs b/dapps/src/handlers/content.rs new file mode 100644 index 000000000..a589e5492 --- /dev/null +++ b/dapps/src/handlers/content.rs @@ -0,0 +1,92 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Simple Content Handler + +use std::io::Write; +use hyper::{header, server, Decoder, Encoder, Next}; +use hyper::net::HttpStream; +use hyper::status::StatusCode; + +pub struct ContentHandler { + code: StatusCode, + content: String, + mimetype: String, + write_pos: usize, +} + +impl ContentHandler { + pub fn ok(content: String, mimetype: String) -> Self { + ContentHandler { + code: StatusCode::Ok, + content: content, + mimetype: mimetype, + write_pos: 0 + } + } + + pub fn not_found(content: String, mimetype: String) -> Self { + ContentHandler { + code: StatusCode::NotFound, + content: content, + mimetype: mimetype, + write_pos: 0 + } + } + + pub fn new(code: StatusCode, content: String, mimetype: String) -> Self { + ContentHandler { + code: code, + content: content, + mimetype: mimetype, + write_pos: 0, + } + } +} + +impl server::Handler for ContentHandler { + fn on_request(&mut self, _request: server::Request) -> Next { + Next::write() + } + + fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next { + Next::write() + } + + fn on_response(&mut self, res: &mut server::Response) -> Next { + res.set_status(self.code); + res.headers_mut().set(header::ContentType(self.mimetype.parse().unwrap())); + Next::write() + } + + fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { + let bytes = self.content.as_bytes(); + if self.write_pos == bytes.len() { + return Next::end(); + } + + match encoder.write(&bytes[self.write_pos..]) { + Ok(bytes) => { + self.write_pos += bytes; + Next::write() + }, + Err(e) => match e.kind() { + ::std::io::ErrorKind::WouldBlock => Next::write(), + _ => Next::end() + }, + } + } +} diff --git a/dapps/src/handlers/mod.rs b/dapps/src/handlers/mod.rs new file mode 100644 index 000000000..933538655 --- /dev/null +++ b/dapps/src/handlers/mod.rs @@ -0,0 +1,25 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Hyper handlers implementations. + +mod auth; +mod content; +mod redirect; + +pub use self::auth::AuthRequiredHandler; +pub use self::content::ContentHandler; +pub use self::redirect::Redirection; diff --git a/dapps/src/router/redirect.rs b/dapps/src/handlers/redirect.rs similarity index 100% rename from dapps/src/router/redirect.rs rename to dapps/src/handlers/redirect.rs diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 22b075afd..1c550fb07 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -59,6 +59,7 @@ mod endpoint; mod apps; mod page; mod router; +mod handlers; mod rpc; mod api; mod proxypac; diff --git a/dapps/src/proxypac.rs b/dapps/src/proxypac.rs index 9d91d58f0..aaa68dc0c 100644 --- a/dapps/src/proxypac.rs +++ b/dapps/src/proxypac.rs @@ -16,7 +16,8 @@ //! Serving ProxyPac file -use endpoint::{Endpoint, Handler, ContentHandler, EndpointPath}; +use endpoint::{Endpoint, Handler, EndpointPath}; +use handlers::ContentHandler; use apps::DAPPS_DOMAIN; pub struct ProxyPac; @@ -41,7 +42,7 @@ function FindProxyForURL(url, host) {{ }} "#, DAPPS_DOMAIN, path.host, path.port); - Box::new(ContentHandler::new(content, "application/javascript".to_owned())) + Box::new(ContentHandler::ok(content, "application/javascript".to_owned())) } } diff --git a/dapps/src/router/auth.rs b/dapps/src/router/auth.rs index 9459d0719..d18424a00 100644 --- a/dapps/src/router/auth.rs +++ b/dapps/src/router/auth.rs @@ -16,24 +16,23 @@ //! HTTP Authorization implementations -use std::io::Write; use std::collections::HashMap; -use hyper::{header, server, Decoder, Encoder, Next}; -use hyper::net::HttpStream; -use hyper::status::StatusCode; +use hyper::{server, net, header, status}; +use endpoint::Handler; +use handlers::{AuthRequiredHandler, ContentHandler}; /// Authorization result pub enum Authorized { /// Authorization was successful. Yes, /// Unsuccessful authorization. Handler for further work is returned. - No(Box + Send>), + No(Box), } /// Authorization interface pub trait Authorization : Send + Sync { /// Checks if authorization is valid. - fn is_authorized(&self, req: &server::Request)-> Authorized; + fn is_authorized(&self, req: &server::Request)-> Authorized; } /// HTTP Basic Authorization handler @@ -45,18 +44,22 @@ pub struct HttpBasicAuth { pub struct NoAuth; impl Authorization for NoAuth { - fn is_authorized(&self, _req: &server::Request)-> Authorized { + fn is_authorized(&self, _req: &server::Request)-> Authorized { Authorized::Yes } } impl Authorization for HttpBasicAuth { - fn is_authorized(&self, req: &server::Request) -> Authorized { + fn is_authorized(&self, req: &server::Request) -> Authorized { let auth = self.check_auth(&req); match auth { Access::Denied => { - Authorized::No(Box::new(UnauthorizedHandler { write_pos: 0 })) + Authorized::No(Box::new(ContentHandler::new( + status::StatusCode::Unauthorized, + "

Unauthorized

".into(), + "text/html".into(), + ))) }, Access::AuthRequired => { Authorized::No(Box::new(AuthRequiredHandler)) @@ -89,7 +92,7 @@ impl HttpBasicAuth { self.users.get(&username.to_owned()).map_or(false, |pass| pass == password) } - fn check_auth(&self, req: &server::Request) -> Access { + fn check_auth(&self, req: &server::Request) -> Access { match req.headers().get::>() { Some(&header::Authorization( header::Basic { ref username, password: Some(ref password) } @@ -99,63 +102,3 @@ impl HttpBasicAuth { } } } - -pub struct UnauthorizedHandler { - write_pos: usize, -} - -impl server::Handler for UnauthorizedHandler { - fn on_request(&mut self, _request: server::Request) -> Next { - Next::write() - } - - fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next { - Next::write() - } - - fn on_response(&mut self, res: &mut server::Response) -> Next { - res.set_status(StatusCode::Unauthorized); - Next::write() - } - - fn on_response_writable(&mut self, encoder: &mut Encoder) -> Next { - let response = "Unauthorized".as_bytes(); - - if self.write_pos == response.len() { - return Next::end(); - } - - match encoder.write(&response[self.write_pos..]) { - Ok(bytes) => { - self.write_pos += bytes; - Next::write() - }, - Err(e) => match e.kind() { - ::std::io::ErrorKind::WouldBlock => Next::write(), - _ => Next::end() - }, - } - } -} - -pub struct AuthRequiredHandler; - -impl server::Handler for AuthRequiredHandler { - fn on_request(&mut self, _request: server::Request) -> Next { - Next::write() - } - - fn on_request_readable(&mut self, _decoder: &mut Decoder) -> Next { - Next::write() - } - - fn on_response(&mut self, res: &mut server::Response) -> Next { - res.set_status(StatusCode::Unauthorized); - res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]); - Next::write() - } - - fn on_response_writable(&mut self, _encoder: &mut Encoder) -> Next { - Next::end() - } -} diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index 0cb0d38d0..f04c8b614 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -18,7 +18,6 @@ //! Processes request handling authorization and dispatching it to proper application. mod url; -mod redirect; pub mod auth; use DAPPS_DOMAIN; @@ -33,7 +32,7 @@ use apps; use endpoint::{Endpoint, Endpoints, EndpointPath}; use self::url::Url; use self::auth::{Authorization, Authorized}; -use self::redirect::Redirection; +use handlers::Redirection; /// Special endpoints are accessible on every domain (every dapp) #[derive(Debug, PartialEq, Hash, Eq)] From b7d243368b3fa79577712fae9a0fad05dbdeca3b Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 6 Jul 2016 12:05:23 +0200 Subject: [PATCH 21/21] Extend migration framework (#1546) * partially done alternate migration scheme * finish altering migration framework * migrate tests to new migration framework * address comments * remove superfluous newline [ci skip] * TempIdx -> TempIndex [ci skip] --- ethcore/src/migrations/extras/v6.rs | 4 +- parity/migration.rs | 55 +++----- util/src/migration/db_impl.rs | 55 -------- util/src/migration/manager.rs | 117 ---------------- util/src/migration/mod.rs | 205 ++++++++++++++++++++++++++-- util/src/migration/tests.rs | 107 +++++++++------ 6 files changed, 279 insertions(+), 264 deletions(-) delete mode 100644 util/src/migration/db_impl.rs delete mode 100644 util/src/migration/manager.rs diff --git a/ethcore/src/migrations/extras/v6.rs b/ethcore/src/migrations/extras/v6.rs index 9b97e594b..3b8160e67 100644 --- a/ethcore/src/migrations/extras/v6.rs +++ b/ethcore/src/migrations/extras/v6.rs @@ -1,4 +1,4 @@ -use util::migration::Migration; +use util::migration::SimpleMigration; /// This migration reduces the sizes of keys and moves `ExtrasIndex` byte from back to the front. pub struct ToV6; @@ -17,7 +17,7 @@ impl ToV6 { } } -impl Migration for ToV6 { +impl SimpleMigration for ToV6 { fn version(&self) -> u32 { 6 } diff --git a/parity/migration.rs b/parity/migration.rs index df2c2116f..6b5c7de99 100644 --- a/parity/migration.rs +++ b/parity/migration.rs @@ -19,8 +19,7 @@ use std::fs::File; use std::io::{Read, Write, Error as IoError, ErrorKind}; use std::path::PathBuf; use std::fmt::{Display, Formatter, Error as FmtError}; -use util::migration::{Manager as MigrationManager, Config as MigrationConfig, MigrationIterator}; -use util::kvdb::{Database, DatabaseConfig, CompactionProfile}; +use util::migration::{Manager as MigrationManager, Config as MigrationConfig, Error as MigrationError}; use ethcore::migrations; /// Database is assumed to be at default version, when no version file is found. @@ -65,6 +64,15 @@ impl From for Error { } } +impl From for Error { + fn from(err: MigrationError) -> Self { + match err { + MigrationError::Io(e) => Error::Io(e), + _ => Error::MigrationFailed, + } + } +} + /// Returns the version file path. fn version_file_path(path: &PathBuf) -> PathBuf { let mut file_path = path.clone(); @@ -109,14 +117,6 @@ fn extras_database_path(path: &PathBuf) -> PathBuf { extras_path } -/// Temporary database path used for migration. -fn temp_database_path(path: &PathBuf) -> PathBuf { - let mut temp_path = path.clone(); - temp_path.pop(); - temp_path.push("temp_migration"); - temp_path -} - /// Database backup fn backup_database_path(path: &PathBuf) -> PathBuf { let mut backup_path = path.clone(); @@ -146,44 +146,27 @@ fn extras_database_migrations() -> Result { } /// Migrates database at given position with given migration rules. -fn migrate_database(version: u32, path: PathBuf, migrations: MigrationManager) -> Result<(), Error> { +fn migrate_database(version: u32, db_path: PathBuf, migrations: MigrationManager) -> Result<(), Error> { // check if migration is needed if !migrations.is_needed(version) { return Ok(()) } - let temp_path = temp_database_path(&path); - let backup_path = backup_database_path(&path); - // remote the dir if it exists - let _ = fs::remove_dir_all(&temp_path); + let backup_path = backup_database_path(&db_path); + // remove the backup dir if it exists let _ = fs::remove_dir_all(&backup_path); - { - let db_config = DatabaseConfig { - prefix_size: None, - max_open_files: 64, - cache_size: None, - compaction: CompactionProfile::default(), - }; - - // open old database - let old = try!(Database::open(&db_config, path.to_str().unwrap()).map_err(|_| Error::MigrationFailed)); - - // create new database - let mut temp = try!(Database::open(&db_config, temp_path.to_str().unwrap()).map_err(|_| Error::MigrationFailed)); - - // migrate old database to the new one - try!(migrations.execute(MigrationIterator::from(old.iter()), version, &mut temp).map_err(|_| Error::MigrationFailed)); - } + // migrate old database to the new one + let temp_path = try!(migrations.execute(&db_path, version)); // create backup - try!(fs::rename(&path, &backup_path)); + try!(fs::rename(&db_path, &backup_path)); // replace the old database with the new one - if let Err(err) = fs::rename(&temp_path, &path) { + if let Err(err) = fs::rename(&temp_path, &db_path) { // if something went wrong, bring back backup - try!(fs::rename(&backup_path, path)); - return Err(From::from(err)); + try!(fs::rename(&backup_path, &db_path)); + return Err(err.into()); } // remove backup diff --git a/util/src/migration/db_impl.rs b/util/src/migration/db_impl.rs deleted file mode 100644 index 9adf22a4a..000000000 --- a/util/src/migration/db_impl.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -//! `kvdb::Database` as `migration::Destination` - -use std::collections::BTreeMap; -use kvdb::{Database, DatabaseIterator, DBTransaction}; -use migration::{Destination, Error}; - -/// Database iterator with `Item` complient with migration `Manager` interface. -pub struct MigrationIterator { - iter: DatabaseIterator, -} - -impl From for MigrationIterator { - fn from(iter: DatabaseIterator) -> Self { - MigrationIterator { - iter: iter - } - } -} - -impl Iterator for MigrationIterator { - type Item = (Vec, Vec); - - fn next(&mut self) -> Option { - self.iter.next().map(|(k, v)| (k.to_vec(), v.to_vec())) - } -} - -impl Destination for Database { - fn commit(&mut self, batch: BTreeMap, Vec>) -> Result<(), Error> { - let transaction = DBTransaction::new(); - - for keypair in &batch { - try!(transaction.put(&keypair.0, &keypair.1).map_err(Error::Custom)) - } - - self.write(transaction).map_err(Error::Custom) - } -} - diff --git a/util/src/migration/manager.rs b/util/src/migration/manager.rs deleted file mode 100644 index bd21d1637..000000000 --- a/util/src/migration/manager.rs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2015, 2016 Ethcore (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -//! Migration manager - -use std::collections::BTreeMap; -use migration::{Migration, Destination}; - -/// Migration error. -#[derive(Debug)] -pub enum Error { - /// Error returned when it is impossible to add new migration rules. - CannotAddMigration, - /// Error returned when migration from specific version can not be performed. - MigrationImpossible, - /// Custom error. - Custom(String), -} - -/// Migration config. -pub struct Config { - /// Defines how many elements should be migrated at once. - pub batch_size: usize, -} - -impl Default for Config { - fn default() -> Self { - Config { - batch_size: 1024, - } - } -} - -/// Manages database migration. -pub struct Manager { - config: Config, - migrations: Vec>, -} - -impl Manager { - /// Creates new migration manager with given configuration. - pub fn new(config: Config) -> Self { - Manager { - config: config, - migrations: vec![] - } - } - - /// Adds new migration rules. - pub fn add_migration(&mut self, migration: T) -> Result<(), Error> where T: Migration { - let version_match = match self.migrations.last() { - Some(last) => last.version() + 1 == migration.version(), - None => true, - }; - - match version_match { - true => Ok(self.migrations.push(Box::new(migration))), - false => Err(Error::CannotAddMigration), - } - } - - /// Performs migration to destination. - pub fn execute(&self, db_iter: D, version: u32, destination: &mut Destination) -> Result<(), Error> where - D: Iterator, Vec)> { - - let migrations = try!(self.migrations_from(version).ok_or(Error::MigrationImpossible)); - - let mut batch: BTreeMap, Vec> = BTreeMap::new(); - - for keypair in db_iter { - let migrated = migrations.iter().fold(Some(keypair), |migrated, migration| { - migrated.and_then(|(key, value)| migration.simple_migrate(key, value)) - }); - - if let Some((key, value)) = migrated { - batch.insert(key, value); - } - - if batch.len() == self.config.batch_size { - try!(destination.commit(batch)); - batch = BTreeMap::new(); - } - } - - try!(destination.commit(batch)); - - Ok(()) - } - - /// Returns true if migration is needed. - pub fn is_needed(&self, version: u32) -> bool { - match self.migrations.last() { - Some(last) => version < last.version(), - None => false, - } - } - - fn migrations_from(&self, version: u32) -> Option<&[Box]> { - // index of the first required migration - let position = self.migrations.iter().position(|m| m.version() == version + 1); - position.map(|p| &self.migrations[p..]) - } -} - diff --git a/util/src/migration/mod.rs b/util/src/migration/mod.rs index 83d42d098..4a51893c0 100644 --- a/util/src/migration/mod.rs +++ b/util/src/migration/mod.rs @@ -15,19 +15,58 @@ // along with Parity. If not, see . //! DB Migration module. - -mod db_impl; -mod manager; - #[cfg(test)] mod tests; -pub use self::manager::{Error, Config, Manager}; -pub use self::db_impl::MigrationIterator; use std::collections::BTreeMap; +use std::fs; +use std::path::{Path, PathBuf}; -/// Single migration. +use ::kvdb::{CompactionProfile, Database, DatabaseConfig, DBTransaction}; + +/// Migration config. +pub struct Config { + /// Defines how many elements should be migrated at once. + pub batch_size: usize, +} + +impl Default for Config { + fn default() -> Self { + Config { + batch_size: 1024, + } + } +} + +/// Migration error. +#[derive(Debug)] +pub enum Error { + /// Error returned when it is impossible to add new migration rules. + CannotAddMigration, + /// Error returned when migration from specific version can not be performed. + MigrationImpossible, + /// Io Error. + Io(::std::io::Error), + /// Custom error. + Custom(String), +} + +impl From<::std::io::Error> for Error { + fn from(e: ::std::io::Error) -> Self { + Error::Io(e) + } +} + +/// A generalized migration from the given db to a destination db. pub trait Migration: 'static { + /// Version of the database after the migration. + fn version(&self) -> u32; + /// Migrate a source to a destination. + fn migrate(&self, source: &Database, config: &Config, destination: &mut Database) -> Result<(), Error>; +} + +/// A simple migration over key-value pairs. +pub trait SimpleMigration: 'static { /// Version of database after the migration. fn version(&self) -> u32; /// Should migrate existing object to new database. @@ -35,8 +74,152 @@ pub trait Migration: 'static { fn simple_migrate(&self, key: Vec, value: Vec) -> Option<(Vec, Vec)>; } -/// Migration destination. -pub trait Destination { - /// Called on destination to commit batch of migrated entries. - fn commit(&mut self, batch: BTreeMap, Vec>) -> Result<(), Error>; +impl Migration for T { + fn version(&self) -> u32 { SimpleMigration::version(self) } + + fn migrate(&self, source: &Database, config: &Config, dest: &mut Database) -> Result<(), Error> { + let mut batch: BTreeMap, Vec> = BTreeMap::new(); + + for (key, value) in source.iter() { + + if let Some((key, value)) = self.simple_migrate(key.to_vec(), value.to_vec()) { + batch.insert(key, value); + } + + if batch.len() == config.batch_size { + try!(commit_batch(dest, &batch)); + batch.clear(); + } + } + + if batch.len() != 0 { + try!(commit_batch(dest, &batch)); + } + + Ok(()) + } } + +/// Commit a batch of writes to a database. +pub fn commit_batch(db: &mut Database, batch: &BTreeMap, Vec>) -> Result<(), Error> { + let transaction = DBTransaction::new(); + + for keypair in batch { + try!(transaction.put(&keypair.0, &keypair.1).map_err(Error::Custom)); + } + + db.write(transaction).map_err(Error::Custom) +} + +/// Get the path where all databases reside. +fn database_path(path: &Path) -> PathBuf { + let mut temp_path = path.to_owned(); + temp_path.pop(); + temp_path +} + +enum TempIndex { + One, + Two, +} + +impl TempIndex { + fn swap(&mut self) { + match *self { + TempIndex::One => *self = TempIndex::Two, + TempIndex::Two => *self = TempIndex::One, + } + } + + // given the path to the old database, get the path of this one. + fn path(&self, db_root: &Path) -> PathBuf { + let mut buf = db_root.to_owned(); + + match *self { + TempIndex::One => buf.push("temp_migration_1"), + TempIndex::Two => buf.push("temp_migration_2"), + }; + + buf + } +} + +/// Manages database migration. +pub struct Manager { + config: Config, + migrations: Vec>, +} + +impl Manager { + /// Creates new migration manager with given configuration. + pub fn new(config: Config) -> Self { + Manager { + config: config, + migrations: vec![], + } + } + + /// Adds new migration rules. + pub fn add_migration(&mut self, migration: T) -> Result<(), Error> where T: Migration { + let version_match = match self.migrations.last() { + Some(last) => last.version() + 1 == migration.version(), + None => true, + }; + + match version_match { + true => Ok(self.migrations.push(Box::new(migration))), + false => Err(Error::CannotAddMigration), + } + } + + /// Performs migration in order, starting with a source path, migrating between two temporary databases, + /// and producing a path where the final migration lives. + pub fn execute(&self, old_path: &Path, version: u32) -> Result { + let migrations = try!(self.migrations_from(version).ok_or(Error::MigrationImpossible)); + let db_config = DatabaseConfig { + prefix_size: None, + max_open_files: 64, + cache_size: None, + compaction: CompactionProfile::default(), + }; + + let db_root = database_path(old_path); + let mut temp_idx = TempIndex::One; + let mut temp_path = temp_idx.path(&db_root); + + // start with the old db. + let old_path_str = try!(old_path.to_str().ok_or(Error::MigrationImpossible)); + let mut cur_db = try!(Database::open(&db_config, old_path_str).map_err(|s| Error::Custom(s))); + for migration in migrations { + // open the target temporary database. + temp_path = temp_idx.path(&db_root); + let temp_path_str = try!(temp_path.to_str().ok_or(Error::MigrationImpossible)); + let mut new_db = try!(Database::open(&db_config, temp_path_str).map_err(|s| Error::Custom(s))); + + // perform the migration from cur_db to new_db. + try!(migration.migrate(&cur_db, &self.config, &mut new_db)); + // next iteration, we will migrate from this db into the other temp. + cur_db = new_db; + temp_idx.swap(); + + // remove the other temporary migration database. + let _ = fs::remove_dir_all(temp_idx.path(&db_root)); + } + Ok(temp_path) + } + + /// Returns true if migration is needed. + pub fn is_needed(&self, version: u32) -> bool { + match self.migrations.last() { + Some(last) => version < last.version(), + None => false, + } + } + + fn migrations_from(&self, version: u32) -> Option<&[Box]> { + // index of the first required migration + let position = self.migrations.iter().position(|m| m.version() == version + 1); + position.map(|p| &self.migrations[p..]) + } +} + diff --git a/util/src/migration/tests.rs b/util/src/migration/tests.rs index b20e18cc9..f441b31e4 100644 --- a/util/src/migration/tests.rs +++ b/util/src/migration/tests.rs @@ -14,19 +14,50 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::BTreeMap; -use migration::{Error, Destination, Migration, Manager, Config}; +//! Tests for migrations. +//! A random temp directory is created. A database is created within it, and migrations +//! are performed in temp sub-directories. -impl Destination for BTreeMap, Vec> { - fn commit(&mut self, batch: BTreeMap, Vec>) -> Result<(), Error> { - self.extend(batch); - Ok(()) +use common::*; +use migration::{Config, SimpleMigration, Manager}; +use kvdb::{Database, DBTransaction}; + +use devtools::RandomTempPath; +use std::path::PathBuf; + +fn db_path(path: &Path) -> PathBuf { + let mut p = path.to_owned(); + p.push("db"); + p +} + +// initialize a database at the given directory with the given values. +fn make_db(path: &Path, pairs: BTreeMap, Vec>) { + let db = Database::open_default(path.to_str().unwrap()).expect("failed to open temp database"); + { + let transaction = DBTransaction::new(); + for (k, v) in pairs { + transaction.put(&k, &v).expect("failed to add pair to transaction"); + } + + db.write(transaction).expect("failed to write db transaction"); + } +} + +// helper for verifying a migrated database. +fn verify_migration(path: &Path, pairs: BTreeMap, Vec>) { + let db = Database::open_default(path.to_str().unwrap()).unwrap(); + + for (k, v) in pairs { + let x = db.get(&k).unwrap().unwrap(); + + assert_eq!(&x[..], &v[..]); } } struct Migration0; -impl Migration for Migration0 { +impl SimpleMigration for Migration0 { fn version(&self) -> u32 { 1 } @@ -42,7 +73,7 @@ impl Migration for Migration0 { struct Migration1; -impl Migration for Migration1 { +impl SimpleMigration for Migration1 { fn version(&self) -> u32 { 2 } @@ -54,68 +85,58 @@ impl Migration for Migration1 { #[test] fn one_simple_migration() { + let dir = RandomTempPath::create_dir(); + let db_path = db_path(dir.as_path()); let mut manager = Manager::new(Config::default()); - let keys = vec![vec![], vec![1u8]]; - let values = vec![vec![], vec![1u8]]; - let db = keys.into_iter().zip(values.into_iter()); + make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); + let expected = map![vec![0x11] => vec![0x22], vec![1, 0x11] => vec![1, 0x22]]; - let expected_keys = vec![vec![0x11u8], vec![1, 0x11]]; - let expected_values = vec![vec![0x22u8], vec![1, 0x22]]; - let expected_db = expected_keys.into_iter().zip(expected_values.into_iter()).collect::>(); - - let mut result = BTreeMap::new(); manager.add_migration(Migration0).unwrap(); - manager.execute(db, 0, &mut result).unwrap(); - assert_eq!(expected_db, result); + let end_path = manager.execute(&db_path, 0).unwrap(); + + verify_migration(&end_path, expected); } #[test] #[should_panic] fn no_migration_needed() { + let dir = RandomTempPath::create_dir(); + let db_path = db_path(dir.as_path()); let mut manager = Manager::new(Config::default()); - let keys = vec![vec![], vec![1u8]]; - let values = vec![vec![], vec![1u8]]; - let db = keys.into_iter().zip(values.into_iter()); + make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); - let mut result = BTreeMap::new(); manager.add_migration(Migration0).unwrap(); - manager.execute(db, 1, &mut result).unwrap(); + manager.execute(&db_path, 1).unwrap(); } #[test] fn multiple_migrations() { + let dir = RandomTempPath::create_dir(); + let db_path = db_path(dir.as_path()); let mut manager = Manager::new(Config::default()); - let keys = vec![vec![], vec![1u8]]; - let values = vec![vec![], vec![1u8]]; - let db = keys.into_iter().zip(values.into_iter()); + make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); + let expected = map![vec![0x11] => vec![], vec![1, 0x11] => vec![]]; - let expected_keys = vec![vec![0x11u8], vec![1, 0x11]]; - let expected_values = vec![vec![], vec![]]; - let expected_db = expected_keys.into_iter().zip(expected_values.into_iter()).collect::>(); - - let mut result = BTreeMap::new(); manager.add_migration(Migration0).unwrap(); manager.add_migration(Migration1).unwrap(); - manager.execute(db, 0, &mut result).unwrap(); - assert_eq!(expected_db, result); + let end_path = manager.execute(&db_path, 0).unwrap(); + + verify_migration(&end_path, expected); } #[test] fn second_migration() { + let dir = RandomTempPath::create_dir(); + let db_path = db_path(dir.as_path()); let mut manager = Manager::new(Config::default()); - let keys = vec![vec![], vec![1u8]]; - let values = vec![vec![], vec![1u8]]; - let db = keys.into_iter().zip(values.into_iter()); + make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); + let expected = map![vec![] => vec![], vec![1] => vec![]]; - let expected_keys = vec![vec![], vec![1u8]]; - let expected_values = vec![vec![], vec![]]; - let expected_db = expected_keys.into_iter().zip(expected_values.into_iter()).collect::>(); - - let mut result = BTreeMap::new(); manager.add_migration(Migration0).unwrap(); manager.add_migration(Migration1).unwrap(); - manager.execute(db, 1, &mut result).unwrap(); - assert_eq!(expected_db, result); + let end_path = manager.execute(&db_path, 1).unwrap(); + + verify_migration(&end_path, expected); } #[test]