From 432c0d59c465e3ef96031a6f98fa354bd66c614d Mon Sep 17 00:00:00 2001 From: debris Date: Fri, 5 Feb 2016 13:21:34 +0100 Subject: [PATCH 01/10] few additional rpc eth methods --- ethcore/src/views.rs | 10 ++++++++ rpc/src/v1/impls/eth.rs | 55 ++++++++++++++++++++++++++++++---------- rpc/src/v1/traits/eth.rs | 4 +++ rpc/src/v1/types/mod.rs | 2 ++ rpc/src/v1/types/sync.rs | 11 ++++++++ 5 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 rpc/src/v1/types/sync.rs diff --git a/ethcore/src/views.rs b/ethcore/src/views.rs index e1c704625..5117309c3 100644 --- a/ethcore/src/views.rs +++ b/ethcore/src/views.rs @@ -139,6 +139,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() @@ -154,6 +159,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/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 46718601b..c1cd327df 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -7,7 +7,7 @@ use util::sha3::*; use ethcore::client::*; use ethcore::views::*; use v1::traits::{Eth, EthFilter}; -use v1::types::Block; +use v1::types::{Block, SyncStatus}; /// Eth rpc implementation. pub struct EthClient { @@ -24,6 +24,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)), @@ -31,6 +32,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()), @@ -38,6 +48,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)), @@ -52,24 +79,26 @@ 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)) - } - 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)) { diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 3dcdfdf05..e9134d84a 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -7,6 +7,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!() } @@ -92,6 +95,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/mod.rs b/rpc/src/v1/types/mod.rs index 7be32e84d..b0ffff2ae 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -1,3 +1,5 @@ mod block; +mod sync; pub use self::block::Block; +pub use self::sync::SyncStatus; diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs new file mode 100644 index 000000000..b13b7167a --- /dev/null +++ b/rpc/src/v1/types/sync.rs @@ -0,0 +1,11 @@ +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, +} From 3adfebdc2039a50f7c5d1a1dca4efaefb8d1e1f3 Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 8 Feb 2016 10:58:08 +0100 Subject: [PATCH 02/10] jsonrpc eth_getCode method --- ethcore/src/client.rs | 7 ++++ rpc/Cargo.toml | 2 +- rpc/src/lib.rs | 1 + rpc/src/v1/impls/eth.rs | 10 ++++- rpc/src/v1/types/block_number.rs | 68 ++++++++++++++++++++++++++++++++ rpc/src/v1/types/bytes.rs | 37 +++++++++++++++++ rpc/src/v1/types/mod.rs | 4 ++ 7 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 rpc/src/v1/types/block_number.rs create mode 100644 rpc/src/v1/types/bytes.rs diff --git a/ethcore/src/client.rs b/ethcore/src/client.rs index 3a0309c1c..28181f5fa 100644 --- a/ethcore/src/client.rs +++ b/ethcore/src/client.rs @@ -86,6 +86,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; @@ -357,6 +360,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/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 72687e03c..d8bcf9540 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, SyncStatus}; +use v1::types::{Block, BlockNumber, Bytes, SyncStatus}; /// Eth rpc implementation. pub struct EthClient { @@ -115,6 +115,14 @@ impl Eth for EthClient { } } + // 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(&Bytes::new(self.client.code(&address).unwrap_or_else(|| vec![]))), + 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)) { 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..51cb7f333 --- /dev/null +++ b/rpc/src/v1/types/bytes.rs @@ -0,0 +1,37 @@ +use rustc_serialize::hex::ToHex; +use serde::{Serialize, Serializer}; + +/// Wrapper structure around vector of bytes. +pub struct Bytes(Vec); + +impl Bytes { + /// Simple constructor. + pub fn new(bytes: Vec) -> Bytes { + Bytes(bytes) + } +} + +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 1b03485ab..0b8582910 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -15,7 +15,11 @@ // along with Parity. If not, see . mod block; +mod block_number; +mod bytes; mod sync; pub use self::block::Block; +pub use self::block_number::BlockNumber; +pub use self::bytes::Bytes; pub use self::sync::SyncStatus; From b2c083ce560f436681b3851d993ef430730a2c85 Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 8 Feb 2016 11:58:47 +0100 Subject: [PATCH 03/10] fixed U256 serialization, tests for transaction serialization --- rpc/src/v1/impls/eth.rs | 2 +- rpc/src/v1/mod.rs | 2 ++ rpc/src/v1/tests/mod.rs | 1 + rpc/src/v1/types/bytes.rs | 8 ++++++++ rpc/src/v1/types/mod.rs | 2 ++ rpc/src/v1/types/transaction.rs | 36 +++++++++++++++++++++++++++++++++ util/src/uint.rs | 3 ++- 7 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 rpc/src/v1/tests/mod.rs create mode 100644 rpc/src/v1/types/transaction.rs diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index d8bcf9540..f90995ffe 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -118,7 +118,7 @@ impl Eth for EthClient { // 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(&Bytes::new(self.client.code(&address).unwrap_or_else(|| vec![]))), + Ok((address, _block_number)) => to_value(&self.client.code(&address).map_or_else(Bytes::default, Bytes::new)), Err(err) => Err(err) } } 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/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 51cb7f333..62aca8464 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -2,6 +2,7 @@ use rustc_serialize::hex::ToHex; use serde::{Serialize, Serializer}; /// Wrapper structure around vector of bytes. +#[derive(Debug)] pub struct Bytes(Vec); impl Bytes { @@ -11,6 +12,13 @@ impl 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 { diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index 0b8582910..226e7e9a6 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -18,8 +18,10 @@ mod block; mod block_number; mod bytes; mod sync; +mod transaction; pub use self::block::Block; 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/transaction.rs b/rpc/src/v1/types/transaction.rs new file mode 100644 index 000000000..de4490cbb --- /dev/null +++ b/rpc/src/v1/types/transaction.rs @@ -0,0 +1,36 @@ +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/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()) } } From a0451a3cb5a360af459ecc8afa498b6181695e31 Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 8 Feb 2016 12:13:05 +0100 Subject: [PATCH 04/10] eth_getBlockXXX takes into account include_tx param --- rpc/src/v1/impls/eth.rs | 13 ++++++++++--- rpc/src/v1/types/block.rs | 27 ++++++++++++++++++++++----- rpc/src/v1/types/mod.rs | 2 +- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index f90995ffe..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, BlockNumber, Bytes, SyncStatus}; +use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus}; /// Eth rpc implementation. pub struct EthClient { @@ -125,7 +125,7 @@ impl Eth for EthClient { 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 { @@ -145,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/types/block.rs b/rpc/src/v1/types/block.rs index 92ff5c8a6..ae5e59fbd 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,5 @@ pub struct Block { #[serde(rename="totalDifficulty")] pub total_difficulty: U256, pub uncles: Vec, - pub transactions: Vec + pub transactions: BlockTransactions } diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index 226e7e9a6..2286c69a1 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -20,7 +20,7 @@ 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; From 41e64bff4e43c290011fdfb608dc06cac189d741 Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 8 Feb 2016 14:02:47 +0100 Subject: [PATCH 05/10] tests for block serialization --- rpc/src/v1/types/block.rs | 49 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index ae5e59fbd..59cafcf60 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -21,7 +21,7 @@ use v1::types::{Bytes, Transaction}; #[derive(Debug)] pub enum BlockTransactions { - Hashes(Vec), + Hashes(Vec), Full(Vec) } @@ -67,3 +67,50 @@ pub struct Block { pub uncles: 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":[]}"#); + } +} From 483ee1fbceb3bc2d5edb8b5945a7d73b4282e017 Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 8 Feb 2016 15:53:22 +0100 Subject: [PATCH 06/10] blockchain transaction api --- ethcore/src/blockchain.rs | 43 +++++++++++++++++++++++++++++++++++++ ethcore/src/extras.rs | 2 +- ethcore/src/transaction.rs | 12 ++++++++--- ethcore/src/verification.rs | 4 ++++ 4 files changed, 57 insertions(+), 4 deletions(-) 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/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() From e5e33826a7fd097fbf08e1de4746fa3bcaed820d Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 8 Feb 2016 16:07:38 +0100 Subject: [PATCH 07/10] fixed failin ethsync test --- sync/src/tests/helpers.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sync/src/tests/helpers.rs b/sync/src/tests/helpers.rs index a5a60d62d..ee052ed5a 100644 --- a/sync/src/tests/helpers.rs +++ b/sync/src/tests/helpers.rs @@ -76,6 +76,10 @@ impl BlockChainClient for TestBlockChainClient { unimplemented!(); } + 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()) From e911e6470424576878a0b9156a4cdd2294c8abda Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 9 Feb 2016 10:19:04 +0100 Subject: [PATCH 08/10] db_version 2 --- util/src/journaldb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/journaldb.rs b/util/src/journaldb.rs index d9d7b29cf..28dcacbfa 100644 --- a/util/src/journaldb.rs +++ b/util/src/journaldb.rs @@ -50,7 +50,7 @@ impl Clone for JournalDB { const LAST_ERA_KEY : [u8; 4] = [ b'l', b'a', b's', b't' ]; const VERSION_KEY : [u8; 4] = [ b'j', b'v', b'e', b'r' ]; -const DB_VERSION: u32 = 1; +const DB_VERSION: u32 = 2; impl JournalDB { /// Create a new instance given a `backing` database. From 40068c1938ee5569402bd231ff88f6bebc133530 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 9 Feb 2016 12:27:05 +0100 Subject: [PATCH 09/10] added missing docs --- rpc/src/v1/types/bytes.rs | 16 ++++++++++++++++ rpc/src/v1/types/sync.rs | 16 ++++++++++++++++ rpc/src/v1/types/transaction.rs | 16 ++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 62aca8464..d6a648d7c 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -1,3 +1,19 @@ +// 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}; diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index b13b7167a..595da6032 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -1,3 +1,19 @@ +// 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)] diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index de4490cbb..968ec2471 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -1,3 +1,19 @@ +// 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; From fd18be4317f05f2fcc38eba7e187b64431bdd611 Mon Sep 17 00:00:00 2001 From: debris Date: Tue, 9 Feb 2016 13:21:32 +0100 Subject: [PATCH 10/10] change CLIENT_DB_VER_STR instead of DB_VERSION --- ethcore/src/client.rs | 2 +- util/src/journaldb.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/src/client.rs b/ethcore/src/client.rs index b31f98f0b..11671b3f2 100644 --- a/ethcore/src/client.rs +++ b/ethcore/src/client.rs @@ -164,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. diff --git a/util/src/journaldb.rs b/util/src/journaldb.rs index 28dcacbfa..d9d7b29cf 100644 --- a/util/src/journaldb.rs +++ b/util/src/journaldb.rs @@ -50,7 +50,7 @@ impl Clone for JournalDB { const LAST_ERA_KEY : [u8; 4] = [ b'l', b'a', b's', b't' ]; const VERSION_KEY : [u8; 4] = [ b'j', b'v', b'e', b'r' ]; -const DB_VERSION: u32 = 2; +const DB_VERSION: u32 = 1; impl JournalDB { /// Create a new instance given a `backing` database.