diff --git a/ethcore/src/blockchain.rs b/ethcore/src/blockchain.rs index c88a375a8..febadfc52 100644 --- a/ethcore/src/blockchain.rs +++ b/ethcore/src/blockchain.rs @@ -111,17 +111,22 @@ pub trait BlockProvider { } /// Get transaction with given transaction hash. - fn transaction(&self, hash: &H256) -> Option { - self.transaction_address(hash).and_then(|address| self.transaction_at(&address)) - } - - /// Get transaction at given address. - fn transaction_at(&self, address: &TransactionAddress) -> Option { - self.block(&address.block_hash).and_then(|bytes| BlockView::new(&bytes).localized_transaction_at(address.index)) + fn transaction(&self, id: TransactionId) -> Option { + match id { + TransactionId::Hash(ref hash) => self.transaction_address(hash), + TransactionId::BlockPosition(BlockId::Hash(hash), index) => Some(TransactionAddress { + block_hash: hash, + index: index + }), + TransactionId::BlockPosition(BlockId::Number(number), index) => self.block_hash(number).map(|hash| TransactionAddress { + block_hash: hash, + index: index + }) + }.and_then(|address| self.block(&address.block_hash).and_then(|bytes| BlockView::new(&bytes).localized_transaction_at(address.index))) } /// Get a list of transactions for a given block. - /// Returns None if block deos not exist. + /// Returns None if block does not exist. fn transactions(&self, hash: &H256) -> Option> { self.block(hash).map(|bytes| BlockView::new(&bytes).localized_transactions()) } @@ -669,6 +674,7 @@ mod tests { use util::hash::*; use blockchain::*; use tests::helpers::*; + use views::TransactionId; #[test] fn valid_tests_extra32() { @@ -864,7 +870,7 @@ mod tests { let transactions = bc.transactions(&b1_hash).unwrap(); assert_eq!(transactions.len(), 7); for t in transactions { - assert_eq!(bc.transaction(&t.hash()).unwrap(), t); + assert_eq!(bc.transaction(TransactionId::Hash(t.hash())).unwrap(), t); } } } diff --git a/ethcore/src/client.rs b/ethcore/src/client.rs index 002f5bffe..93038af48 100644 --- a/ethcore/src/client.rs +++ b/ethcore/src/client.rs @@ -19,7 +19,7 @@ use util::*; use rocksdb::{Options, DB, DBCompactionStyle}; use blockchain::{BlockChain, BlockProvider, CacheSize}; -use views::BlockView; +use views::{BlockView, TransactionId}; use error::*; use header::BlockNumber; use state::State; @@ -106,7 +106,7 @@ pub trait BlockChainClient : Sync + Send { fn block_total_difficulty_at(&self, n: BlockNumber) -> Option; /// Get transaction with given hash. - fn transaction(&self, hash: &H256) -> Option; + fn transaction(&self, id: TransactionId) -> Option; /// Get a tree route between `from` and `to`. /// See `BlockChain::tree_route`. @@ -392,8 +392,8 @@ impl BlockChainClient for Client { self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_total_difficulty(&h)) } - fn transaction(&self, hash: &H256) -> Option { - self.chain.read().unwrap().transaction(hash) + fn transaction(&self, id: TransactionId) -> Option { + self.chain.read().unwrap().transaction(id) } fn tree_route(&self, from: &H256, to: &H256) -> Option { diff --git a/ethcore/src/views.rs b/ethcore/src/views.rs index 4a7ff054d..624d9bb96 100644 --- a/ethcore/src/views.rs +++ b/ethcore/src/views.rs @@ -19,6 +19,22 @@ use util::*; use header::*; use transaction::*; +/// Uniqly identifies block in canon blockchain. +pub enum BlockId { + /// Block's sha3. + Hash(H256), + /// Block number. + Number(BlockNumber) +} + +/// Uniqly identifies transaction in canon blockchain. +pub enum TransactionId { + /// Transaction's sha3. + Hash(H256), + /// Block id and transaction index within this block. + BlockPosition(BlockId, usize) +} + /// View onto transaction rlp. pub struct TransactionView<'a> { rlp: Rlp<'a> diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index afd6a1d1c..c21157599 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -23,7 +23,7 @@ use util::sha3::*; use ethcore::client::*; use ethcore::views::*; use v1::traits::{Eth, EthFilter}; -use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, Transaction, OptionalValue}; +use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, Transaction, OptionalValue, Index}; /// Eth rpc implementation. pub struct EthClient { @@ -152,15 +152,27 @@ impl Eth for EthClient { to_value(&block) }, _ => Ok(Value::Null) - }) + }) } fn transaction_by_hash(&self, params: Params) -> Result { from_params::<(H256,)>(params) - .and_then(|(hash,)| match self.client.transaction(&hash) { + .and_then(|(hash,)| match self.client.transaction(TransactionId::Hash(hash)) { Some(t) => to_value(&Transaction::from(t)), None => Ok(Value::Null) - }) + }) + } + + fn transaction_by_block_hash_and_index(&self, params: Params) -> Result { + from_params::<(H256, Index)>(params) + .and_then(|(hash, index)| match self.client.transaction(TransactionId::BlockPosition(BlockId::Hash(hash), index.value())) { + Some(t) => to_value(&Transaction::from(t)), + None => Ok(Value::Null) + }) + } + + fn transaction_by_block_number_and_index(&self, _params: Params) -> Result { + unimplemented!() } } diff --git a/rpc/src/v1/types/index.rs b/rpc/src/v1/types/index.rs new file mode 100644 index 000000000..a77096fbf --- /dev/null +++ b/rpc/src/v1/types/index.rs @@ -0,0 +1,66 @@ +// 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::{Deserialize, Deserializer, Error}; +use serde::de::Visitor; + +/// Represents usize. +#[derive(Debug, PartialEq)] +pub struct Index(usize); + +impl Index { + pub fn value(&self) -> usize { + self.0 + } +} + +impl Deserialize for Index { + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer { + deserializer.visit(IndexVisitor) + } +} + +struct IndexVisitor; + +impl Visitor for IndexVisitor { + type Value = Index; + + fn visit_str(&mut self, value: &str) -> Result where E: Error { + match value { + _ if value.starts_with("0x") => usize::from_str_radix(&value[2..], 16).map(Index).map_err(|_| Error::syntax("invalid index")), + _ => value.parse::().map(Index).map_err(|_| Error::syntax("invalid index")) + } + } + + fn visit_string(&mut self, value: String) -> Result where E: Error { + self.visit_str(value.as_ref()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + #[test] + fn block_number_deserialization() { + let s = r#"["0xa", "10"]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized, vec![Index(10), Index(10)]); + } +} + diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index b35e7ff15..bdbd157ff 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -18,6 +18,7 @@ mod block; mod block_number; mod bytes; mod filter; +mod index; mod optionals; mod sync; mod transaction; @@ -26,6 +27,7 @@ pub use self::block::{Block, BlockTransactions}; pub use self::block_number::BlockNumber; pub use self::bytes::Bytes; pub use self::filter::Filter; +pub use self::index::Index; pub use self::optionals::OptionalValue; pub use self::sync::SyncStatus; pub use self::transaction::Transaction;