From 3e8ee9d423e860430fb70f3b45851f2a018c34e2 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 8 Dec 2016 13:44:17 +0100 Subject: [PATCH] light IPC feature and mock state proofs --- Cargo.lock | 2 - ethcore/light/Cargo.toml | 9 +- ethcore/light/build.rs | 5 + ethcore/light/src/lib.rs | 6 +- ethcore/light/src/net/tests/mod.rs | 164 ++++++++++++++++++++++++- ethcore/light/src/types/les_request.rs | 33 +++-- ethcore/light/src/types/mod.rs | 7 +- 7 files changed, 206 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b00701f32..4a5534dbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,8 +462,6 @@ version = "1.5.0" dependencies = [ "ethcore 1.5.0", "ethcore-io 1.5.0", - "ethcore-ipc 1.4.0", - "ethcore-ipc-codegen 1.4.0", "ethcore-network 1.5.0", "ethcore-util 1.5.0", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index 9440bbf88..e80eb3e2f 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Ethcore "] build = "build.rs" [build-dependencies] -"ethcore-ipc-codegen" = { path = "../../ipc/codegen" } +"ethcore-ipc-codegen" = { path = "../../ipc/codegen", optional = true } [dependencies] log = "0.3" @@ -16,6 +16,9 @@ ethcore = { path = "..", features = ["light"] } ethcore-util = { path = "../../util" } ethcore-network = { path = "../../util/network" } ethcore-io = { path = "../../util/io" } -ethcore-ipc = { path = "../../ipc/rpc" } +ethcore-ipc = { path = "../../ipc/rpc", optional = true } rlp = { path = "../../util/rlp" } -time = "0.1" \ No newline at end of file +time = "0.1" + +[features] +ipc = ["ethcore/ipc", "ethcore-ipc", "ethcore-ipc-codegen"] \ No newline at end of file diff --git a/ethcore/light/build.rs b/ethcore/light/build.rs index cff92a011..43915c1cf 100644 --- a/ethcore/light/build.rs +++ b/ethcore/light/build.rs @@ -14,8 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +#[cfg(feature = "ipc")] extern crate ethcore_ipc_codegen; +#[cfg(feature = "ipc")] fn main() { ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap(); } + +#[cfg(not(feature = "ipc"))] +fn main() { } \ No newline at end of file diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index e150f4ee5..c00467c4c 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -47,6 +47,8 @@ extern crate ethcore; extern crate ethcore_util as util; extern crate ethcore_network as network; extern crate ethcore_io as io; -extern crate ethcore_ipc as ipc; extern crate rlp; -extern crate time; \ No newline at end of file +extern crate time; + +#[cfg(feature = "ipc")] +extern crate ethcore_ipc as ipc; \ No newline at end of file diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 7db05542e..555f6d19c 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -123,7 +123,19 @@ impl Provider for TestProvider { } fn proofs(&self, req: request::StateProofs) -> Vec { - req.requests.into_iter().map(|_| ::rlp::EMPTY_LIST_RLP.to_vec()).collect() + req.requests.into_iter() + .map(|req| { + match req.key2 { + Some(key2) => ::util::sha3::SHA3_NULL_RLP.to_owned(), + None => { + // sort of a leaf node + let mut stream = RlpStream::new_list(2); + stream.append(&req.key1).append_empty_data(); + stream.out() + } + } + }) + .collect() } fn contract_code(&self, req: request::ContractCodes) -> Vec { @@ -240,6 +252,9 @@ fn buffer_overflow() { proto.handle_packet(&Expect::Punish(1), &1, packet::GET_BLOCK_HEADERS, &request); } +// test the basic request types -- these just make sure that requests are parsed +// and sent to the provider correctly as well as testing response formatting. + #[test] fn get_block_headers() { let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); @@ -288,4 +303,151 @@ fn get_block_headers() { let expected = Expect::Respond(packet::BLOCK_HEADERS, response); proto.handle_packet(&expected, &1, packet::GET_BLOCK_HEADERS, &request_body); +} + +#[test] +fn get_block_bodies() { + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + + provider.client.add_blocks(100, EachBlockWith::Nothing); + + let cur_status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } + + let request = request::Bodies { + block_hashes: (0..10).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()).collect(), + }; + + let req_id = 111; + + let request_body = encode_request(&Request::Bodies(request.clone()), req_id); + let response = { + let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockID::Number(i + 1)).unwrap()).collect(); + assert_eq!(bodies.len(), 10); + + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10); + + let mut response_stream = RlpStream::new_list(12); + + response_stream.append(&req_id).append(&new_buf); + for body in bodies { + response_stream.append_raw(&body, 1); + } + + response_stream.out() + }; + + let expected = Expect::Respond(packet::BLOCK_BODIES, response); + proto.handle_packet(&expected, &1, packet::GET_BLOCK_BODIES, &request_body); +} + +#[test] +fn get_block_receipts() { + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + + provider.client.add_blocks(1000, EachBlockWith::Nothing); + + let cur_status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } + + // find the first 10 block hashes starting with `f` because receipts are only provided + // by the test client in that case. + let block_hashes: Vec<_> = (0..1000).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()) + .filter(|hash| format!("{}", hash).starts_with("f")).take(10).collect(); + + let request = request::Receipts { + block_hashes: block_hashes.clone(), + }; + + let req_id = 111; + + let request_body = encode_request(&Request::Receipts(request.clone()), req_id); + let response = { + let receipts: Vec<_> = block_hashes.iter() + .map(|hash| provider.client.block_receipts(hash).unwrap()) + .collect(); + + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Receipts, receipts.len()); + + let mut response_stream = RlpStream::new_list(2 + receipts.len()); + + response_stream.append(&req_id).append(&new_buf); + for block_receipts in receipts { + response_stream.append_raw(&block_receipts, 1); + } + + response_stream.out() + }; + + let expected = Expect::Respond(packet::RECEIPTS, response); + proto.handle_packet(&expected, &1, packet::GET_RECEIPTS, &request_body); +} + +#[test] +fn get_block_bodies() { + let flow_params = FlowParams::new(5_000_000.into(), Default::default(), 0.into()); + let capabilities = capabilities(); + + let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + + let cur_status = status(provider.client.chain_info()); + let my_status = write_handshake(&cur_status, &capabilities, &flow_params); + + provider.client.add_blocks(100, EachBlockWith::Nothing); + + let cur_status = status(provider.client.chain_info()); + + { + let packet_body = write_handshake(&cur_status, &capabilities, &flow_params); + proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body)); + proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); + } + + let request = request::Bodies { + block_hashes: (0..10).map(|i| provider.client.block_hash(BlockID::Number(i)).unwrap()).collect(), + }; + + let req_id = 111; + + let request_body = encode_request(&Request::Bodies(request.clone()), req_id); + let response = { + let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockID::Number(i + 1)).unwrap()).collect(); + assert_eq!(bodies.len(), 10); + + let new_buf = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10); + + let mut response_stream = RlpStream::new_list(12); + + response_stream.append(&req_id).append(&new_buf); + for body in bodies { + response_stream.append_raw(&body, 1); + } + + response_stream.out() + }; + + let expected = Expect::Respond(packet::BLOCK_BODIES, response); + proto.handle_packet(&expected, &1, packet::GET_BLOCK_BODIES, &request_body); } \ No newline at end of file diff --git a/ethcore/light/src/types/les_request.rs b/ethcore/light/src/types/les_request.rs index d0de080ee..49bd2e9cc 100644 --- a/ethcore/light/src/types/les_request.rs +++ b/ethcore/light/src/types/les_request.rs @@ -19,7 +19,8 @@ use util::H256; /// A request for block headers. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct Headers { /// Starting block number pub block_num: u64, @@ -35,7 +36,8 @@ pub struct Headers { } /// A request for specific block bodies. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct Bodies { /// Hashes which bodies are being requested for. pub block_hashes: Vec @@ -45,14 +47,16 @@ pub struct Bodies { /// /// This request is answered with a list of transaction receipts for each block /// requested. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct Receipts { /// Block hashes to return receipts for. pub block_hashes: Vec, } /// A request for a state proof -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct StateProof { /// Block hash to query state from. pub block: H256, @@ -66,14 +70,16 @@ pub struct StateProof { } /// A request for state proofs. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct StateProofs { /// All the proof requests. pub requests: Vec, } /// A request for contract code. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct ContractCode { /// Block hash pub block_hash: H256, @@ -82,14 +88,16 @@ pub struct ContractCode { } /// A request for contract code. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct ContractCodes { /// Block hash and account key (== sha3(address)) pairs to fetch code for. pub code_requests: Vec, } /// A request for a header proof from the Canonical Hash Trie. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct HeaderProof { /// Number of the CHT. pub cht_number: u64, @@ -100,14 +108,16 @@ pub struct HeaderProof { } /// A request for header proofs from the CHT. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub struct HeaderProofs { /// All the proof requests. pub requests: Vec, } /// Kinds of requests. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub enum Kind { /// Requesting headers. Headers, @@ -124,7 +134,8 @@ pub enum Kind { } /// Encompasses all possible types of requests in a single structure. -#[derive(Debug, Clone, PartialEq, Eq, Binary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", derive(Binary))] pub enum Request { /// Requesting headers. Headers(Headers), diff --git a/ethcore/light/src/types/mod.rs b/ethcore/light/src/types/mod.rs index 2625358a3..d7f473553 100644 --- a/ethcore/light/src/types/mod.rs +++ b/ethcore/light/src/types/mod.rs @@ -15,6 +15,11 @@ // along with Parity. If not, see . //! Types used in the public (IPC) api which require custom code generation. +#![cfg_attr(feature = "ipc", allow(dead_code, unused_assignments, unused_variables))] // codegen issues -#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues + +#[cfg(feature = "ipc")] include!(concat!(env!("OUT_DIR"), "/mod.rs.in")); + +#[cfg(not(feature = "ipc"))] +include!("mod.rs.in"); \ No newline at end of file