diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 575938cd5..9dcd25888 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -24,7 +24,6 @@ //! - It stores only headers (and a pruned subset of them) //! - To allow for flexibility in the database layout once that's incorporated. // TODO: use DB instead of memory. DB Layout: just the contents of `candidates`/`headers` -// use std::collections::{BTreeMap, HashMap}; diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index ebf5f4f08..ada58d8de 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -26,7 +26,7 @@ //! use-cases like sending transactions from a personal account. //! //! The light client performs a header-only sync, doing verification and pruning -//! historical blocks. Upon pruning, batches of 2048 blocks have a number => hash +//! historical blocks. Upon pruning, batches of 2048 blocks have a number => (hash, TD) //! mapping sealed into "canonical hash tries" which can later be used to verify //! historical block queries from peers. diff --git a/ethcore/light/src/types/request.rs b/ethcore/light/src/types/request.rs index 279296cf8..259f3def7 100644 --- a/ethcore/light/src/types/request.rs +++ b/ethcore/light/src/types/request.rs @@ -19,6 +19,7 @@ use std::collections::HashMap; use ethcore::transaction::Action; +use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream}; use util::{Address, H256, U256, Uint}; // re-exports of request types. @@ -77,6 +78,32 @@ impl From for Field { } } +impl Decodable for Field { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + + match rlp.val_at::(0)? { + 0 => Ok(Field::Scalar(rlp.val_at::(1)?)), + 1 => Ok({ + let inner_rlp = rlp.at(1)?; + Field::BackReference(inner_rlp.val_at(0)?, inner_rlp.val_at(1)?) + }) + _ => Err(DecoderError::Custom("Unknown discriminant for PIP field.")), + } + } +} + +impl Encodable for Field { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + match *self { + Field::Scalar(ref data) => s.append(&0u8).append(data), + Field::BackReference(ref req, ref idx) => + s.append(&1u8).begin_list(2).append(req).append(idx), + }; + } +} + /// Request outputs which can be reused as inputs. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Output { @@ -117,6 +144,114 @@ impl From for HashOrNumber { } } +/// All request types, as they're sent over the network. +pub enum Request { + /// A request for block headers. + Headers(IncompleteHeadersRequest), + /// A request for a header proof (from a CHT) + HeaderProof(IncompleteHeaderProofRequest), + // TransactionIndex, + /// A request for a block's receipts. + Receipts(IncompleteReceiptsRequest), + /// A request for a block body. + Body(IncompleteBodyRequest), + /// A request for a merkle proof of an account. + Account(IncompleteAccountRequest), + /// A request for a merkle proof of contract storage. + Storage(IncompleteStorageRequest), + /// A request for contract code. + Code(IncompleteCodeRequest), + // Transaction proof. +} + +impl Request { + fn kind(&self) -> RequestKind { + match *self { + Request::Headers(_) => RequestKind::Headers, + Request::HeaderProof(_) => RequestKind::HeaderProof, + Request::Receipts(_) => RequestKind::Receipts, + Request::Body(_) => RequestKind::Body, + Request::Account(_) => RequestKind::Account, + Request::Storage(_) => RequestKind::Storage, + Request::Code(_) => RequestKind::Code, + } + } +} + +impl Decodable for Request { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + + match rlp.val_at::(0)? { + RequestKind::Headers => Ok(Request::Headers(rlp.val_at(1)?)), + RequestKind::HeaderProof => Ok(Request::HeaderProof(rlp.val_at(1)?)), + RequestKind::Receipts => Ok(Request::Receipts(rlp.val_at(1)?)), + RequestKind::Body => Ok(Request::Body(rlp.val_at(1)?)), + RequestKind::Account => Ok(Request::Account(rlp.val_at(1)?)), + RequestKind::Storage => Ok(Request::Storage(rlp.val_at(1)?)), + RequestKind::Code => Ok(Request::Code(rlp.val_at(1)?)), + } + } +} + +impl Encodable for Request { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2).append(&self.kind()); + + match *self { + Request::Headers(ref req) => s.append(req), + Request::HeaderProof(ref req) => s.append(req), + Request::Receipts(ref req) => s.append(req), + Request::Body(ref req) => s.append(req), + Request::Account(ref req) => s.append(req), + Request::Storage(ref req) => s.append(req), + Request::Code(ref req) => s.append(req), + }; + } +} + + +/// Kinds of requests. +/// Doubles as the "ID" field of the request. +#[repr(u8)] +pub enum RequestKind { + /// A request for headers. + Headers = 0, + HeaderProof = 1, + // TransactionIndex = 2, + Receipts = 3, + Body = 4, + Account = 5, + Storage = 6, + Code = 7, + // TransactionProof = 8, +} + +impl Decodable for RequestKind { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + + match rlp.as_val::()? { + 0 => Ok(RequestKind::Headers), + 1 => Ok(RequestKind::HeaderProof), + // 2 => Ok(RequestKind::TransactionIndex, + 3 => Ok(RequestKind::Receipts), + 4 => Ok(RequestKind::Body), + 5 => Ok(RequestKind::Account), + 6 => Ok(RequestKind::Storage), + 7 => Ok(RequestKind::Code), + // 8 => Ok(RequestKind::TransactionProof), + _ => Err(DecoderError::Custom("Unknown PIP request ID.")), + } + } +} + +impl Encodable for RequestKind { + fn rlp_append(&self, s: &mut RlpStream) { + s.append(self as &u8); + } +} + /// A potentially incomplete request. pub trait IncompleteRequest: Sized { type Complete; @@ -144,6 +279,7 @@ pub trait IncompleteRequest: Sized { pub mod header { use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output}; use ethcore::encoded; + use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream}; use util::U256; /// Potentially incomplete headers request. @@ -159,6 +295,28 @@ pub mod header { pub reverse: bool, } + impl Decodable for Incomplete { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + Ok(Incomplete { + start: rlp.val_at(0)?, + skip: rlp.val_at(1)?, + max: rlp.val_at(2)?, + reverse: rlp.val_at(3)? + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4) + .append(&self.start) + .append(&self.skip) + .append(&self.max) + .append(&self.reverse); + } + } + impl super::IncompleteRequest for Incomplete { type Complete = Complete; @@ -223,6 +381,7 @@ pub mod header { /// Request and response for header proofs. pub mod header_proof { use super::{Field, NoSuchOutput, OutputKind, Output}; + use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream}; use util::{Bytes, U256, H256}; /// Potentially incomplete header proof request. @@ -232,6 +391,21 @@ pub mod header_proof { pub num: Field, } + impl Decodable for Incomplete { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + Ok(Incomplete { + num: rlp.val_at(0)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(1).append(&self.num); + } + } + impl super::IncompleteRequest for Incomplete { type Complete = Complete; @@ -295,6 +469,7 @@ pub mod header_proof { /// Request and response for block receipts pub mod block_receipts { use super::{Field, NoSuchOutput, OutputKind, Output}; + use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream}; use util::{Bytes, U256, H256}; /// Potentially incomplete block receipts request. @@ -304,6 +479,21 @@ pub mod block_receipts { pub hash: Field, } + impl Decodable for Incomplete { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + Ok(Incomplete { + hash: rlp.val_at(0)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(1).append(&self.hash); + } + } + impl super::IncompleteRequest for Incomplete { type Complete = Complete; @@ -360,6 +550,7 @@ pub mod block_receipts { pub mod block_body { use super::{Field, NoSuchOutput, OutputKind, Output}; use ethcore::encoded; + use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream}; use util::{Bytes, U256, H256}; /// Potentially incomplete block body request. @@ -369,6 +560,21 @@ pub mod block_body { pub hash: Field, } + impl Decodable for Incomplete { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + Ok(Incomplete { + hash: rlp.val_at(0)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(1).append(&self.hash); + } + } + impl super::IncompleteRequest for Incomplete { type Complete = Complete; @@ -425,6 +631,7 @@ pub mod block_body { pub mod account { use super::{Field, NoSuchOutput, OutputKind, Output}; use ethcore::encoded; + use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream}; use util::{Bytes, U256, H256}; /// Potentially incomplete request for an account proof. @@ -436,6 +643,24 @@ pub mod account { pub address_hash: Field, } + impl Decodable for Incomplete { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + Ok(Incomplete { + block_hash: rlp.val_at(0)?, + address_hash: rlp.val_at(1)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2) + .append(&self.block_hash) + .append(&self.address_hash); + } + } + impl super::IncompleteRequest for Incomplete { type Complete = Complete; @@ -522,6 +747,7 @@ pub mod account { pub mod storage { use super::{Field, NoSuchOutput, OutputKind, Output}; use ethcore::encoded; + use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream}; use util::{Bytes, U256, H256}; /// Potentially incomplete request for an storage proof. @@ -535,6 +761,26 @@ pub mod storage { pub key_hash: Field, } + impl Decodable for Incomplete { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + Ok(Incomplete { + block_hash: rlp.val_at(0)?, + address_hash: rlp.val_at(1)?, + key_hash: rlp.val_at(2)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3) + .append(&self.block_hash) + .append(&self.address_hash) + .append(&self.key_hash); + } + } + impl super::IncompleteRequest for Incomplete { type Complete = Complete; @@ -628,6 +874,7 @@ pub mod storage { pub mod contract_code { use super::{Field, NoSuchOutput, OutputKind, Output}; use ethcore::encoded; + use rlp::{Encodable, Decodable, Decoder, DecoderError, RlpStream, Stream}; use util::{Bytes, U256, H256}; /// Potentially incomplete _ request. @@ -639,6 +886,24 @@ pub mod contract_code { pub code_hash: Field, } + impl Decodable for Incomplete { + fn decode(decoder: &D) -> Result where D: Decoder { + let rlp = decoder.as_rlp(); + Ok(Incomplete { + block_hash: rlp.val_at(0)?, + code_hash: rlp.val_at(1)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2) + .append(&self.block_hash) + .append(&self.code_hash); + } + } + impl super::IncompleteRequest for Incomplete { type Complete = Complete;