diff --git a/ethcore/src/blockchain.rs b/ethcore/src/blockchain.rs index ff1e508d3..764b76588 100644 --- a/ethcore/src/blockchain.rs +++ b/ethcore/src/blockchain.rs @@ -23,6 +23,24 @@ use extras::*; use transaction::*; use views::*; +/// Uniquely identifies block. +pub enum BlockId { + /// Block's sha3. + /// Querying by hash is always faster. + Hash(H256), + /// Block number within canon blockchain. + Number(BlockNumber) +} + +/// Uniquely identifies transaction. +pub enum TransactionId { + /// Transaction's sha3. + Hash(H256), + /// Block id and transaction index within this block. + /// Querying by block position is always faster. + Location(BlockId, usize) +} + /// Represents a tree route between `from` block and `to` block: pub struct TreeRoute { /// A vector of hashes of all blocks, ordered from `from` to `to`. @@ -111,19 +129,24 @@ 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).map(|bytes| BlockView::new(&bytes).transactions()).and_then(|t| t.into_iter().nth(address.index)) + fn transaction(&self, id: TransactionId) -> Option { + match id { + TransactionId::Hash(ref hash) => self.transaction_address(hash), + TransactionId::Location(BlockId::Hash(hash), index) => Some(TransactionAddress { + block_hash: hash, + index: index + }), + TransactionId::Location(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. - fn transactions(&self, hash: &H256) -> Option> { - self.block(hash).map(|bytes| BlockView::new(&bytes).transactions()) + /// Returns None if block does not exist. + fn transactions(&self, hash: &H256) -> Option> { + self.block(hash).map(|bytes| BlockView::new(&bytes).localized_transactions()) } /// Returns reference to genesis hash. @@ -864,7 +887,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 11671b3f2..3de5c097e 100644 --- a/ethcore/src/client.rs +++ b/ethcore/src/client.rs @@ -18,7 +18,7 @@ use util::*; use rocksdb::{Options, DB, DBCompactionStyle}; -use blockchain::{BlockChain, BlockProvider, CacheSize}; +use blockchain::{BlockChain, BlockProvider, CacheSize, TransactionId}; use views::BlockView; use error::*; use header::BlockNumber; @@ -31,6 +31,7 @@ use service::{NetSyncMessage, SyncMessage}; use env_info::LastHashes; use verification::*; use block::*; +use transaction::LocalizedTransaction; pub use blockchain::TreeRoute; /// General block status @@ -104,6 +105,9 @@ pub trait BlockChainClient : Sync + Send { /// Get block total difficulty. fn block_total_difficulty_at(&self, n: BlockNumber) -> Option; + /// Get transaction with given hash. + fn transaction(&self, id: TransactionId) -> Option; + /// Get a tree route between `from` and `to`. /// See `BlockChain::tree_route`. fn tree_route(&self, from: &H256, to: &H256) -> Option; @@ -388,6 +392,10 @@ impl BlockChainClient for Client { self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_total_difficulty(&h)) } + fn transaction(&self, id: TransactionId) -> Option { + self.chain.read().unwrap().transaction(id) + } + fn tree_route(&self, from: &H256, to: &H256) -> Option { self.chain.read().unwrap().tree_route(from.clone(), to.clone()) } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 9537c8862..6c4535339 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -98,6 +98,7 @@ pub mod ethereum; pub mod header; pub mod service; pub mod spec; +pub mod transaction; pub mod views; pub mod receipt; @@ -115,7 +116,6 @@ mod state; mod account; mod account_db; mod action_params; -mod transaction; mod null_engine; mod builtin; mod extras; diff --git a/ethcore/src/transaction.rs b/ethcore/src/transaction.rs index 119f565dd..b43c271d3 100644 --- a/ethcore/src/transaction.rs +++ b/ethcore/src/transaction.rs @@ -19,6 +19,7 @@ use util::*; use error::*; use evm::Schedule; +use header::BlockNumber; #[derive(Debug, Clone, PartialEq, Eq)] /// Transaction action type. @@ -156,8 +157,7 @@ impl Transaction { } } - - +/// Signed transaction information. #[derive(Debug, Clone, Eq)] pub struct SignedTransaction { /// Plain Transaction. @@ -290,6 +290,27 @@ impl SignedTransaction { } } +/// Signed Transaction that is a part of canon blockchain. +#[derive(Debug, PartialEq, Eq)] +pub struct LocalizedTransaction { + /// Signed part. + pub signed: SignedTransaction, + /// Block number. + pub block_number: BlockNumber, + /// Block hash. + pub block_hash: H256, + /// Transaction index within block. + pub transaction_index: usize +} + +impl Deref for LocalizedTransaction { + type Target = SignedTransaction; + + fn deref(&self) -> &Self::Target { + &self.signed + } +} + #[test] fn sender_test() { let t: SignedTransaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap()); diff --git a/ethcore/src/views.rs b/ethcore/src/views.rs index 5b6b56ea5..4a7ff054d 100644 --- a/ethcore/src/views.rs +++ b/ethcore/src/views.rs @@ -155,6 +155,22 @@ impl<'a> BlockView<'a> { self.rlp.val_at(1) } + /// Return List of transactions with additional localization info. + pub fn localized_transactions(&self) -> Vec { + let header = self.header_view(); + let block_hash = header.sha3(); + let block_number = header.number(); + self.transactions() + .into_iter() + .enumerate() + .map(|(i, t)| LocalizedTransaction { + signed: t, + block_hash: block_hash.clone(), + block_number: block_number, + transaction_index: i + }).collect() + } + /// Return number of transactions in given block, without deserializing them. pub fn transactions_count(&self) -> usize { self.rlp.at(1).iter().count() @@ -170,6 +186,24 @@ impl<'a> BlockView<'a> { self.rlp.at(1).iter().map(|rlp| rlp.as_raw().sha3()).collect() } + /// Returns transaction at given index without deserializing unnecessary data. + pub fn transaction_at(&self, index: usize) -> Option { + self.rlp.at(1).iter().nth(index).map(|rlp| rlp.as_val()) + } + + /// Returns localized transaction at given index. + pub fn localized_transaction_at(&self, index: usize) -> Option { + let header = self.header_view(); + let block_hash = header.sha3(); + let block_number = header.number(); + self.transaction_at(index).map(|t| LocalizedTransaction { + signed: t, + block_hash: block_hash, + block_number: block_number, + transaction_index: index + }) + } + /// Return list of uncles of given block. pub fn uncles(&self) -> Vec
{ self.rlp.val_at(2) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 606a1ba6d..5d60b40a6 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -22,8 +22,9 @@ use util::uint::*; use util::sha3::*; use ethcore::client::*; use ethcore::views::*; +use ethcore::blockchain::{BlockId, TransactionId}; use v1::traits::{Eth, EthFilter}; -use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus}; +use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, Transaction, OptionalValue, Index}; /// Eth rpc implementation. pub struct EthClient { @@ -96,40 +97,35 @@ impl Eth for EthClient { } fn block_transaction_count(&self, params: Params) -> Result { - match from_params::(params) { - Ok(hash) => match self.client.block(&hash) { + from_params::<(H256,)>(params) + .and_then(|(hash,)| match self.client.block(&hash) { Some(bytes) => to_value(&BlockView::new(&bytes).transactions_count()), None => Ok(Value::Null) - }, - Err(err) => Err(err) - } + }) } fn block_uncles_count(&self, params: Params) -> Result { - match from_params::(params) { - Ok(hash) => match self.client.block(&hash) { + from_params::<(H256,)>(params) + .and_then(|(hash,)| match self.client.block(&hash) { Some(bytes) => to_value(&BlockView::new(&bytes).uncles_count()), None => Ok(Value::Null) - }, - Err(err) => Err(err) - } + }) } // TODO: do not ignore block number param fn code_at(&self, params: Params) -> Result { - match from_params::<(Address, BlockNumber)>(params) { - Ok((address, _block_number)) => to_value(&self.client.code(&address).map_or_else(Bytes::default, Bytes::new)), - Err(err) => Err(err) - } + from_params::<(Address, BlockNumber)>(params) + .and_then(|(address, _block_number)| to_value(&self.client.code(&address).map_or_else(Bytes::default, Bytes::new))) } fn block(&self, params: Params) -> Result { - match from_params::<(H256, bool)>(params) { - Ok((hash, include_txs)) => match (self.client.block_header(&hash), self.client.block_total_difficulty(&hash)) { + from_params::<(H256, bool)>(params) + .and_then(|(hash, include_txs)| match (self.client.block(&hash), self.client.block_total_difficulty(&hash)) { (Some(bytes), Some(total_difficulty)) => { - let view = HeaderView::new(&bytes); + let block_view = BlockView::new(&bytes); + let view = block_view.header_view(); let block = Block { - hash: view.sha3(), + hash: OptionalValue::Value(view.sha3()), parent_hash: view.parent_hash(), uncles_hash: view.uncles_hash(), author: view.author(), @@ -137,7 +133,7 @@ impl Eth for EthClient { state_root: view.state_root(), transactions_root: view.transactions_root(), receipts_root: view.receipts_root(), - number: U256::from(view.number()), + number: OptionalValue::Value(U256::from(view.number())), gas_used: view.gas_used(), gas_limit: view.gas_limit(), logs_bloom: view.log_bloom(), @@ -147,9 +143,9 @@ impl Eth for EthClient { uncles: vec![], transactions: { if include_txs { - BlockTransactions::Hashes(vec![]) + BlockTransactions::Full(block_view.localized_transactions().into_iter().map(From::from).collect()) } else { - BlockTransactions::Full(vec![]) + BlockTransactions::Hashes(block_view.transaction_hashes()) } }, extra_data: Bytes::default() @@ -157,12 +153,31 @@ impl Eth for EthClient { to_value(&block) }, _ => Ok(Value::Null) - }, - Err(err) => Err(err) - } + }) + } + + fn transaction_by_hash(&self, params: Params) -> Result { + from_params::<(H256,)>(params) + .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::Location(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!() } } + /// Eth filter rpc implementation. pub struct EthFilterClient { client: Arc diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index d247e2174..640af1f82 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -74,8 +74,14 @@ pub trait Eth: Sized + Send + Sync + 'static { /// Estimate gas needed for execution of given contract. fn estimate_gas(&self, _: Params) -> Result { rpc_unimplemented!() } - /// Returns transaction at given block and index. - fn transaction_at(&self, _: Params) -> Result { rpc_unimplemented!() } + /// Get transaction by it's hash. + fn transaction_by_hash(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns transaction at given block hash and index. + fn transaction_by_block_hash_and_index(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns transaction by given block number and index. + fn transaction_by_block_number_and_index(&self, _: Params) -> Result { rpc_unimplemented!() } /// Returns transaction receipt. fn transaction_receipt(&self, _: Params) -> Result { rpc_unimplemented!() } @@ -131,8 +137,9 @@ pub trait Eth: Sized + Send + Sync + 'static { delegate.add_method("eth_estimateGas", Eth::estimate_gas); delegate.add_method("eth_getBlockByHash", Eth::block); delegate.add_method("eth_getBlockByNumber", Eth::block); - delegate.add_method("eth_getTransactionByBlockHashAndIndex", Eth::transaction_at); - delegate.add_method("eth_getTransactionByBlockNumberAndIndex", Eth::transaction_at); + delegate.add_method("eth_getTransactionByHash", Eth::transaction_by_hash); + delegate.add_method("eth_getTransactionByBlockHashAndIndex", Eth::transaction_by_block_hash_and_index); + delegate.add_method("eth_getTransactionByBlockNumberAndIndex", Eth::transaction_by_block_number_and_index); delegate.add_method("eth_getTransactionReceipt", Eth::transaction_receipt); delegate.add_method("eth_getUncleByBlockHashAndIndex", Eth::uncle_at); delegate.add_method("eth_getUncleByBlockNumberAndIndex", Eth::uncle_at); diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 59cafcf60..b92111bcb 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -17,7 +17,7 @@ use serde::{Serialize, Serializer}; use util::hash::*; use util::uint::*; -use v1::types::{Bytes, Transaction}; +use v1::types::{Bytes, Transaction, OptionalValue}; #[derive(Debug)] pub enum BlockTransactions { @@ -37,7 +37,7 @@ impl Serialize for BlockTransactions { #[derive(Debug, Serialize)] pub struct Block { - pub hash: H256, + pub hash: OptionalValue, #[serde(rename="parentHash")] pub parent_hash: H256, #[serde(rename="sha3Uncles")] @@ -51,7 +51,7 @@ pub struct Block { pub transactions_root: H256, #[serde(rename="receiptsRoot")] pub receipts_root: H256, - pub number: U256, + pub number: OptionalValue, #[serde(rename="gasUsed")] pub gas_used: U256, #[serde(rename="gasLimit")] @@ -73,14 +73,14 @@ mod tests { use serde_json; use util::hash::*; use util::uint::*; - use v1::types::{Transaction, Bytes}; + use v1::types::{Transaction, Bytes, OptionalValue}; use super::*; #[test] fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x00","transactionIndex":"0x00","from":"0x0000000000000000000000000000000000000000","to":"0x0000000000000000000000000000000000000000","value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x00"}]"#); + 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":"0x00"}]"#); let t = BlockTransactions::Hashes(vec![H256::default()]); let serialized = serde_json::to_string(&t).unwrap(); @@ -90,7 +90,7 @@ mod tests { #[test] fn test_serialize_block() { let block = Block { - hash: H256::default(), + hash: OptionalValue::Value(H256::default()), parent_hash: H256::default(), uncles_hash: H256::default(), author: Address::default(), @@ -98,7 +98,7 @@ mod tests { state_root: H256::default(), transactions_root: H256::default(), receipts_root: H256::default(), - number: U256::default(), + number: OptionalValue::Value(U256::default()), gas_used: U256::default(), gas_limit: U256::default(), extra_data: Bytes::default(), diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs new file mode 100644 index 000000000..9b21cf8e7 --- /dev/null +++ b/rpc/src/v1/types/filter.rs @@ -0,0 +1,88 @@ +// 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_json::value; +use jsonrpc_core::Value; +use util::hash::*; +use v1::types::BlockNumber; + +#[derive(Debug, PartialEq)] +pub enum Topic { + Single(H256), + Multiple(Vec), + Null +} + +impl Deserialize for Topic { + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer { + let v = try!(Value::deserialize(deserializer)); + + if v.is_null() { + return Ok(Topic::Null); + } + + Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Topic::Single) + .or_else(|_| Deserialize::deserialize(&mut value::Deserializer::new(v.clone())).map(Topic::Multiple)) + .map_err(|_| Error::syntax("")) // unreachable, but types must match + } +} + +#[derive(Debug, PartialEq, Deserialize)] +pub struct Filter { + #[serde(rename="fromBlock")] + pub from_block: Option, + #[serde(rename="toBlock")] + pub to_block: Option, + pub address: Option
, + pub topics: Option> +} + +#[cfg(test)] +mod tests { + use serde_json; + use std::str::FromStr; + use util::hash::*; + use super::*; + use v1::types::BlockNumber; + + #[test] + fn topic_deserialization() { + let s = r#"["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", null, ["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"]]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized, vec![ + Topic::Single(H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap()), + Topic::Null, + Topic::Multiple(vec![ + H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(), + H256::from_str("0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc").unwrap() + ]) + ]); + } + + #[test] + fn filter_deserialization() { + let s = r#"{"fromBlock":"earliest","toBlock":"latest"}"#; + let deserialized: Filter = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized, Filter { + from_block: Some(BlockNumber::Earliest), + to_block: Some(BlockNumber::Latest), + address: None, + topics: None + }); + } +} 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 2286c69a1..bdbd157ff 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -17,11 +17,17 @@ mod block; mod block_number; mod bytes; +mod filter; +mod index; +mod optionals; mod sync; mod transaction; 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; diff --git a/rpc/src/v1/types/optionals.rs b/rpc/src/v1/types/optionals.rs new file mode 100644 index 000000000..5db272251 --- /dev/null +++ b/rpc/src/v1/types/optionals.rs @@ -0,0 +1,58 @@ +// 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}; +use serde_json::Value; + +#[derive(Debug)] +pub enum OptionalValue where T: Serialize { + Value(T), + Null +} + +impl Default for OptionalValue where T: Serialize { + fn default() -> Self { + OptionalValue::Null + } +} + +impl Serialize for OptionalValue where T: Serialize { + 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) + } + } +} + +#[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/transaction.rs b/rpc/src/v1/types/transaction.rs index 968ec2471..0e9256ada 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -16,25 +16,47 @@ use util::hash::*; use util::uint::*; -use v1::types::Bytes; +use ethcore::transaction::{LocalizedTransaction, Action}; +use v1::types::{Bytes, OptionalValue}; #[derive(Debug, Default, Serialize)] pub struct Transaction { - hash: H256, - nonce: U256, + pub hash: H256, + pub nonce: U256, #[serde(rename="blockHash")] - block_hash: H256, + pub block_hash: OptionalValue, #[serde(rename="blockNumber")] - block_number: U256, + pub block_number: OptionalValue, #[serde(rename="transactionIndex")] - transaction_index: U256, - from: Address, - to: Address, - value: U256, + pub transaction_index: OptionalValue, + pub from: Address, + pub to: OptionalValue
, + pub value: U256, #[serde(rename="gasPrice")] - gas_price: U256, - gas: U256, - input: Bytes + pub gas_price: U256, + pub gas: U256, + pub input: Bytes +} + +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(), + to: match t.action { + Action::Create => OptionalValue::Null, + Action::Call(ref address) => OptionalValue::Value(address.clone()) + }, + value: t.value, + gas_price: t.gas_price, + gas: t.gas, + input: Bytes::new(t.data.clone()) + } + } } #[cfg(test)] @@ -46,7 +68,7 @@ mod tests { fn test_transaction_serialize() { let t = Transaction::default(); let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x00","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0x00","transactionIndex":"0x00","from":"0x0000000000000000000000000000000000000000","to":"0x0000000000000000000000000000000000000000","value":"0x00","gasPrice":"0x00","gas":"0x00","input":"0x00"}"#); + 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":"0x00"}"#); } } diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs index 7cb1b3c53..f8c08dc93 100644 --- a/sync/src/tests/helpers.rs +++ b/sync/src/tests/helpers.rs @@ -22,6 +22,8 @@ use ethcore::error::*; use io::SyncIo; use chain::{ChainSync}; use ethcore::receipt::Receipt; +use ethcore::transaction::LocalizedTransaction; +use ethcore::blockchain::TransactionId; pub struct TestBlockChainClient { pub blocks: RwLock>, @@ -86,6 +88,10 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } + fn transaction(&self, _id: TransactionId) -> Option { + unimplemented!(); + } + fn block_header(&self, h: &H256) -> Option { self.blocks.read().unwrap().get(h).map(|r| Rlp::new(r).at(0).as_raw().to_vec()) }