diff --git a/ethcore/src/blockchain.rs b/ethcore/src/blockchain.rs index 4c765360e..ff1e508d3 100644 --- a/ethcore/src/blockchain.rs +++ b/ethcore/src/blockchain.rs @@ -85,6 +85,9 @@ pub trait BlockProvider { /// Get the hash of given block's number. fn block_hash(&self, index: BlockNumber) -> Option; + /// Get the address of transaction with given hash. + fn transaction_address(&self, hash: &H256) -> Option; + /// Get the partial-header of a block. fn block_header(&self, hash: &H256) -> Option
{ self.block(hash).map(|bytes| BlockView::new(&bytes).header()) @@ -107,6 +110,16 @@ pub trait BlockProvider { self.block(hash).map(|bytes| BlockView::new(&bytes).header_view().number()) } + /// 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)) + } + /// Get a list of transactions for a given block. /// Returns None if block deos not exist. fn transactions(&self, hash: &H256) -> Option> { @@ -201,6 +214,11 @@ impl BlockProvider for BlockChain { fn block_hash(&self, index: BlockNumber) -> Option { self.query_extras(&index, &self.block_hashes) } + + /// Get the address of transaction with given hash. + fn transaction_address(&self, hash: &H256) -> Option { + self.query_extras(hash, &self.transaction_addresses) + } } const COLLECTION_QUEUE_SIZE: usize = 8; @@ -474,6 +492,14 @@ impl BlockChain { parent_details.children.push(hash.clone()); batch.put_extras(&parent_hash, &parent_details); + // update transaction addresses + for (i, tx_hash) in block.transaction_hashes().iter().enumerate() { + batch.put_extras(tx_hash, &TransactionAddress { + block_hash: hash.clone(), + index: i + }); + } + // if it's not new best block, just return if !is_new_best { return (batch, None, details); @@ -824,4 +850,21 @@ mod tests { let bc = bc_result.reference(); assert_eq!(bc.best_block_number(), 0); } + + #[test] + fn find_transaction_by_hash() { + let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap(); + let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap(); + let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap(); + + let temp = RandomTempPath::new(); + let bc = BlockChain::new(&genesis, temp.as_path()); + bc.insert_block(&b1); + + let transactions = bc.transactions(&b1_hash).unwrap(); + assert_eq!(transactions.len(), 7); + for t in transactions { + assert_eq!(bc.transaction(&t.hash()).unwrap(), t); + } + } } diff --git a/ethcore/src/client.rs b/ethcore/src/client.rs index 46b53e7b9..11671b3f2 100644 --- a/ethcore/src/client.rs +++ b/ethcore/src/client.rs @@ -85,6 +85,9 @@ pub trait BlockChainClient : Sync + Send { /// Get block total difficulty. fn block_total_difficulty(&self, hash: &H256) -> Option; + /// Get address code. + fn code(&self, address: &Address) -> Option; + /// Get raw block header data by block number. fn block_header_at(&self, n: BlockNumber) -> Option; @@ -161,7 +164,7 @@ pub struct Client { } const HISTORY: u64 = 1000; -const CLIENT_DB_VER_STR: &'static str = "1.0"; +const CLIENT_DB_VER_STR: &'static str = "2.0"; impl Client { /// Create a new client with given spec and DB path. @@ -358,6 +361,10 @@ impl BlockChainClient for Client { self.chain.read().unwrap().block_details(hash).map(|d| d.total_difficulty) } + fn code(&self, address: &Address) -> Option { + self.state().code(address) + } + fn block_header_at(&self, n: BlockNumber) -> Option { self.chain.read().unwrap().block_hash(n).and_then(|h| self.block_header(&h)) } diff --git a/ethcore/src/extras.rs b/ethcore/src/extras.rs index f29925483..b65d4ed7a 100644 --- a/ethcore/src/extras.rs +++ b/ethcore/src/extras.rs @@ -260,7 +260,7 @@ pub struct TransactionAddress { /// Block hash pub block_hash: H256, /// Transaction index within the block - pub index: u64 + pub index: usize } impl ExtrasIndexable for TransactionAddress { diff --git a/ethcore/src/transaction.rs b/ethcore/src/transaction.rs index d90a0dd15..119f565dd 100644 --- a/ethcore/src/transaction.rs +++ b/ethcore/src/transaction.rs @@ -20,7 +20,7 @@ use util::*; use error::*; use evm::Schedule; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] /// Transaction action type. pub enum Action { /// Create creates new contract. @@ -45,7 +45,7 @@ impl Decodable for Action { /// A set of information describing an externally-originating message call /// or contract creation operation. -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct Transaction { /// Nonce. pub nonce: U256, @@ -158,7 +158,7 @@ impl Transaction { -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq)] pub struct SignedTransaction { /// Plain Transaction. unsigned: Transaction, @@ -174,6 +174,12 @@ pub struct SignedTransaction { sender: RefCell> } +impl PartialEq for SignedTransaction { + fn eq(&self, other: &SignedTransaction) -> bool { + self.unsigned == other.unsigned && self.v == other.v && self.r == other.r && self.s == other.s + } +} + impl Deref for SignedTransaction { type Target = Transaction; diff --git a/ethcore/src/verification.rs b/ethcore/src/verification.rs index f827f365e..c7d5e265f 100644 --- a/ethcore/src/verification.rs +++ b/ethcore/src/verification.rs @@ -294,6 +294,10 @@ mod tests { }) } + fn transaction_address(&self, _hash: &H256) -> Option { + unimplemented!() + } + /// Get the hash of given block's number. fn block_hash(&self, index: BlockNumber) -> Option { self.numbers.get(&index).cloned() diff --git a/ethcore/src/views.rs b/ethcore/src/views.rs index 9db917960..5b6b56ea5 100644 --- a/ethcore/src/views.rs +++ b/ethcore/src/views.rs @@ -155,6 +155,11 @@ impl<'a> BlockView<'a> { self.rlp.val_at(1) } + /// Return number of transactions in given block, without deserializing them. + pub fn transactions_count(&self) -> usize { + self.rlp.at(1).iter().count() + } + /// Return List of transactions in given block. pub fn transaction_views(&self) -> Vec { self.rlp.at(1).iter().map(TransactionView::new_from_rlp).collect() @@ -170,6 +175,11 @@ impl<'a> BlockView<'a> { self.rlp.val_at(2) } + /// Return number of uncles in given block, without deserializing them. + pub fn uncles_count(&self) -> usize { + self.rlp.at(2).iter().count() + } + /// Return List of transactions in given block. pub fn uncle_views(&self) -> Vec { self.rlp.at(2).iter().map(HeaderView::new_from_rlp).collect() diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index bea85a74f..66688466c 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -18,4 +18,4 @@ ethcore = { path = "../ethcore" } ethsync = { path = "../sync" } clippy = "0.0.37" target_info = "0.1.0" - +rustc-serialize = "0.3" diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index bf82a64a0..0b148c983 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -20,6 +20,7 @@ #![plugin(serde_macros)] #![plugin(clippy)] +extern crate rustc_serialize; extern crate target_info; extern crate serde; extern crate serde_json; diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 59d6c4d0e..606a1ba6d 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; +use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus}; /// Eth rpc implementation. pub struct EthClient { @@ -40,6 +40,7 @@ impl EthClient { } impl Eth for EthClient { + // TODO: do not hardcode protocol version fn protocol_version(&self, params: Params) -> Result { match params { Params::None => Ok(Value::U64(63)), @@ -47,6 +48,15 @@ impl Eth for EthClient { } } + // TODO: do no hardcode default sync status + fn syncing(&self, params: Params) -> Result { + match params { + Params::None => to_value(&SyncStatus::default()), + _ => Err(Error::invalid_params()) + } + } + + // TODO: do not hardcode author. fn author(&self, params: Params) -> Result { match params { Params::None => to_value(&Address::new()), @@ -54,6 +64,23 @@ impl Eth for EthClient { } } + // TODO: return real value of mining once it's implemented. + fn is_mining(&self, params: Params) -> Result { + match params { + Params::None => Ok(Value::Bool(false)), + _ => Err(Error::invalid_params()) + } + } + + // TODO: return real hashrate once we have mining + fn hashrate(&self, params: Params) -> Result { + match params { + Params::None => Ok(Value::U64(0)), + _ => Err(Error::invalid_params()) + } + } + + // TODO: do not hardode gas_price fn gas_price(&self, params: Params) -> Result { match params { Params::None => Ok(Value::U64(0)), @@ -68,27 +95,37 @@ impl Eth for EthClient { } } - fn is_mining(&self, params: Params) -> Result { - match params { - Params::None => Ok(Value::Bool(false)), - _ => Err(Error::invalid_params()) + fn block_transaction_count(&self, params: Params) -> Result { + match from_params::(params) { + Ok(hash) => match self.client.block(&hash) { + Some(bytes) => to_value(&BlockView::new(&bytes).transactions_count()), + None => Ok(Value::Null) + }, + Err(err) => Err(err) } } - fn hashrate(&self, params: Params) -> Result { - match params { - Params::None => Ok(Value::U64(0)), - _ => Err(Error::invalid_params()) + fn block_uncles_count(&self, params: Params) -> Result { + match from_params::(params) { + Ok(hash) => match self.client.block(&hash) { + Some(bytes) => to_value(&BlockView::new(&bytes).uncles_count()), + None => Ok(Value::Null) + }, + Err(err) => Err(err) } } - fn block_transaction_count(&self, _: Params) -> Result { - Ok(Value::U64(0)) + // 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) + } } 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)) { + Ok((hash, include_txs)) => match (self.client.block_header(&hash), self.client.block_total_difficulty(&hash)) { (Some(bytes), Some(total_difficulty)) => { let view = HeaderView::new(&bytes); let block = Block { @@ -108,7 +145,14 @@ impl Eth for EthClient { difficulty: view.difficulty(), total_difficulty: total_difficulty, uncles: vec![], - transactions: vec![] + transactions: { + if include_txs { + BlockTransactions::Hashes(vec![]) + } else { + BlockTransactions::Full(vec![]) + } + }, + extra_data: Bytes::default() }; to_value(&block) }, diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 11a5291b5..01635e872 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -21,6 +21,8 @@ pub mod traits; mod impls; mod types; +#[cfg(test)] +mod tests; pub use self::traits::{Web3, Eth, EthFilter, Net}; pub use self::impls::*; diff --git a/rpc/src/v1/tests/mod.rs b/rpc/src/v1/tests/mod.rs new file mode 100644 index 000000000..bdf4567b6 --- /dev/null +++ b/rpc/src/v1/tests/mod.rs @@ -0,0 +1 @@ +//TODO: load custom blockchain state and test diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 9ee3926da..d247e2174 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -23,6 +23,9 @@ pub trait Eth: Sized + Send + Sync + 'static { /// Returns protocol version. fn protocol_version(&self, _: Params) -> Result { rpc_unimplemented!() } + /// Returns an object with data about the sync status or false. (wtf?) + fn syncing(&self, _: Params) -> Result { rpc_unimplemented!() } + /// Returns the number of hashes per second that the node is mining with. fn hashrate(&self, _: Params) -> Result { rpc_unimplemented!() } @@ -108,6 +111,7 @@ pub trait Eth: Sized + Send + Sync + 'static { fn to_delegate(self) -> IoDelegate { let mut delegate = IoDelegate::new(Arc::new(self)); delegate.add_method("eth_protocolVersion", Eth::protocol_version); + delegate.add_method("eth_syncing", Eth::syncing); delegate.add_method("eth_hashrate", Eth::hashrate); delegate.add_method("eth_coinbase", Eth::author); delegate.add_method("eth_mining", Eth::is_mining); diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 92ff5c8a6..59cafcf60 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -14,10 +14,28 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use serde::{Serialize, Serializer}; use util::hash::*; use util::uint::*; +use v1::types::{Bytes, Transaction}; -#[derive(Default, Debug, Serialize)] +#[derive(Debug)] +pub enum BlockTransactions { + Hashes(Vec), + Full(Vec) +} + +impl Serialize for BlockTransactions { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: Serializer { + match *self { + BlockTransactions::Hashes(ref hashes) => hashes.serialize(serializer), + BlockTransactions::Full(ref ts) => ts.serialize(serializer) + } + } +} + +#[derive(Debug, Serialize)] pub struct Block { pub hash: H256, #[serde(rename="parentHash")] @@ -38,9 +56,8 @@ pub struct Block { pub gas_used: U256, #[serde(rename="gasLimit")] pub gas_limit: U256, - // TODO: figure out how to properly serialize bytes - //#[serde(rename="extraData")] - //extra_data: Vec, + #[serde(rename="extraData")] + pub extra_data: Bytes, #[serde(rename="logsBloom")] pub logs_bloom: H2048, pub timestamp: U256, @@ -48,5 +65,52 @@ pub struct Block { #[serde(rename="totalDifficulty")] pub total_difficulty: U256, pub uncles: Vec, - pub transactions: Vec + pub transactions: BlockTransactions +} + +#[cfg(test)] +mod tests { + use serde_json; + use util::hash::*; + use util::uint::*; + use v1::types::{Transaction, Bytes}; + 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"}]"#); + + let t = BlockTransactions::Hashes(vec![H256::default()]); + let serialized = serde_json::to_string(&t).unwrap(); + assert_eq!(serialized, r#"["0x0000000000000000000000000000000000000000000000000000000000000000"]"#); + } + + #[test] + fn test_serialize_block() { + let block = Block { + hash: H256::default(), + parent_hash: H256::default(), + uncles_hash: H256::default(), + author: Address::default(), + miner: Address::default(), + state_root: H256::default(), + transactions_root: H256::default(), + receipts_root: H256::default(), + number: U256::default(), + gas_used: U256::default(), + gas_limit: U256::default(), + extra_data: Bytes::default(), + logs_bloom: H2048::default(), + timestamp: U256::default(), + difficulty: U256::default(), + total_difficulty: U256::default(), + uncles: vec![], + transactions: BlockTransactions::Hashes(vec![]) + }; + + let serialized = serde_json::to_string(&block).unwrap(); + assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","author":"0x0000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","number":"0x00","gasUsed":"0x00","gasLimit":"0x00","extraData":"0x00","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x00","difficulty":"0x00","totalDifficulty":"0x00","uncles":[],"transactions":[]}"#); + } } diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs new file mode 100644 index 000000000..bfe20f177 --- /dev/null +++ b/rpc/src/v1/types/block_number.rs @@ -0,0 +1,68 @@ +// 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 rpc api block number param. +#[derive(Debug, PartialEq)] +pub enum BlockNumber { + Num(u64), + Latest, + Earliest, + Pending +} + +impl Deserialize for BlockNumber { + fn deserialize(deserializer: &mut D) -> Result + where D: Deserializer { + deserializer.visit(BlockNumberVisitor) + } +} + +struct BlockNumberVisitor; + +impl Visitor for BlockNumberVisitor { + type Value = BlockNumber; + + fn visit_str(&mut self, value: &str) -> Result where E: Error { + match value { + "latest" => Ok(BlockNumber::Latest), + "earliest" => Ok(BlockNumber::Earliest), + "pending" => Ok(BlockNumber::Pending), + _ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16).map(BlockNumber::Num).map_err(|_| Error::syntax("invalid block number")), + _ => value.parse::().map(BlockNumber::Num).map_err(|_| Error::syntax("invalid block number")) + } + } + + 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", "latest", "earliest", "pending"]"#; + let deserialized: Vec = serde_json::from_str(s).unwrap(); + assert_eq!(deserialized, vec![BlockNumber::Num(10), BlockNumber::Num(10), BlockNumber::Latest, BlockNumber::Earliest, BlockNumber::Pending]) + } +} + diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs new file mode 100644 index 000000000..d6a648d7c --- /dev/null +++ b/rpc/src/v1/types/bytes.rs @@ -0,0 +1,61 @@ +// 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 rustc_serialize::hex::ToHex; +use serde::{Serialize, Serializer}; + +/// Wrapper structure around vector of bytes. +#[derive(Debug)] +pub struct Bytes(Vec); + +impl Bytes { + /// Simple constructor. + pub fn new(bytes: Vec) -> Bytes { + Bytes(bytes) + } +} + +impl Default for Bytes { + fn default() -> Self { + // default serialized value is 0x00 + Bytes(vec![0]) + } +} + +impl Serialize for Bytes { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: Serializer { + let mut serialized = "0x".to_owned(); + serialized.push_str(self.0.to_hex().as_ref()); + serializer.visit_str(serialized.as_ref()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + use rustc_serialize::hex::FromHex; + + #[test] + fn test_bytes_serialize() { + let bytes = Bytes("0123456789abcdef".from_hex().unwrap()); + let serialized = serde_json::to_string(&bytes).unwrap(); + assert_eq!(serialized, r#""0x0123456789abcdef""#); + } +} + + diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index ba8e456ad..2286c69a1 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -15,5 +15,13 @@ // along with Parity. If not, see . mod block; +mod block_number; +mod bytes; +mod sync; +mod transaction; -pub use self::block::Block; +pub use self::block::{Block, BlockTransactions}; +pub use self::block_number::BlockNumber; +pub use self::bytes::Bytes; +pub use self::sync::SyncStatus; +pub use self::transaction::Transaction; diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs new file mode 100644 index 000000000..595da6032 --- /dev/null +++ b/rpc/src/v1/types/sync.rs @@ -0,0 +1,27 @@ +// 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::hash::*; + +#[derive(Default, Debug, Serialize)] +pub struct SyncStatus { + #[serde(rename="startingBlock")] + pub starting_block: H256, + #[serde(rename="currentBlock")] + pub current_block: H256, + #[serde(rename="highestBlock")] + pub highest_block: H256, +} diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs new file mode 100644 index 000000000..968ec2471 --- /dev/null +++ b/rpc/src/v1/types/transaction.rs @@ -0,0 +1,52 @@ +// 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::hash::*; +use util::uint::*; +use v1::types::Bytes; + +#[derive(Debug, Default, Serialize)] +pub struct Transaction { + hash: H256, + nonce: U256, + #[serde(rename="blockHash")] + block_hash: H256, + #[serde(rename="blockNumber")] + block_number: U256, + #[serde(rename="transactionIndex")] + transaction_index: U256, + from: Address, + to: Address, + value: U256, + #[serde(rename="gasPrice")] + gas_price: U256, + gas: U256, + input: Bytes +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + #[test] + 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"}"#); + } +} + diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs index f70c4d1f4..7cb1b3c53 100644 --- a/sync/src/tests/helpers.rs +++ b/sync/src/tests/helpers.rs @@ -82,6 +82,10 @@ impl BlockChainClient for TestBlockChainClient { Some(U256::zero()) } + fn code(&self, _address: &Address) -> 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()) } diff --git a/util/src/uint.rs b/util/src/uint.rs index 4c0b533ef..b3427f6bc 100644 --- a/util/src/uint.rs +++ b/util/src/uint.rs @@ -458,7 +458,8 @@ macro_rules! construct_uint { let mut hex = "0x".to_owned(); let mut bytes = [0u8; 8 * $n_words]; self.to_bytes(&mut bytes); - hex.push_str(bytes.to_hex().as_ref()); + let len = cmp::max((self.bits() + 7) / 8, 1); + hex.push_str(bytes[bytes.len() - len..].to_hex().as_ref()); serializer.visit_str(hex.as_ref()) } }