diff --git a/ethcore/light/src/net/load_timer.rs b/ethcore/light/src/net/load_timer.rs index 9c6afc7cc..a169b86d9 100644 --- a/ethcore/light/src/net/load_timer.rs +++ b/ethcore/light/src/net/load_timer.rs @@ -55,6 +55,7 @@ fn hardcoded_serve_time(kind: Kind) -> u64 { match kind { Kind::Headers => 500_000, Kind::HeaderProof => 500_000, + Kind::TransactionIndex => 500_000, Kind::Receipts => 1_000_000, Kind::Body => 1_000_000, Kind::Account => 1_500_000, diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 6ab5903df..77c9bc40c 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -80,7 +80,7 @@ pub const PROTOCOL_VERSIONS: &'static [u8] = &[1]; pub const MAX_PROTOCOL_VERSION: u8 = 1; /// Packet count for PIP. -pub const PACKET_COUNT: u8 = 5; +pub const PACKET_COUNT: u8 = 9; // packet ID definitions. mod packet { @@ -100,6 +100,10 @@ mod packet { // relay transactions to peers. pub const SEND_TRANSACTIONS: u8 = 0x06; + + // request and respond with epoch transition proof + pub const REQUEST_EPOCH_PROOF: u8 = 0x07; + pub const EPOCH_PROOF: u8 = 0x08; } // timeouts for different kinds of requests. all values are in milliseconds. @@ -110,6 +114,7 @@ mod timeout { // timeouts per request within packet. pub const HEADERS: i64 = 250; // per header? + pub const TRANSACTION_INDEX: i64 = 100; pub const BODY: i64 = 50; pub const RECEIPT: i64 = 50; pub const PROOF: i64 = 100; // state proof @@ -523,6 +528,12 @@ impl LightProtocol { packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), + packet::REQUEST_EPOCH_PROOF | packet::EPOCH_PROOF => { + // ignore these for now, but leave them specified. + debug!(target: "pip", "Ignoring request/response for epoch proof"); + Ok(()) + } + other => { Err(Error::UnrecognizedPacket(other)) } @@ -867,6 +878,7 @@ impl LightProtocol { match complete_req { CompleteRequest::Headers(req) => self.provider.block_headers(req).map(Response::Headers), CompleteRequest::HeaderProof(req) => self.provider.header_proof(req).map(Response::HeaderProof), + CompleteRequest::TransactionIndex(_) => None, // don't answer these yet, but leave them in protocol. CompleteRequest::Body(req) => self.provider.block_body(req).map(Response::Body), CompleteRequest::Receipts(req) => self.provider.block_receipts(req).map(Response::Receipts), CompleteRequest::Account(req) => self.provider.account_proof(req).map(Response::Account), diff --git a/ethcore/light/src/net/request_credits.rs b/ethcore/light/src/net/request_credits.rs index 330576f9c..950fa24d8 100644 --- a/ethcore/light/src/net/request_credits.rs +++ b/ethcore/light/src/net/request_credits.rs @@ -83,6 +83,7 @@ impl Credits { pub struct CostTable { base: U256, // cost per packet. headers: U256, // cost per header + transaction_index: U256, body: U256, receipts: U256, account: U256, @@ -98,6 +99,7 @@ impl Default for CostTable { CostTable { base: 100000.into(), headers: 10000.into(), + transaction_index: 10000.into(), body: 15000.into(), receipts: 5000.into(), account: 25000.into(), @@ -119,8 +121,9 @@ impl Encodable for CostTable { s.append(cost); } - s.begin_list(9).append(&self.base); + s.begin_list(10).append(&self.base); append_cost(s, &self.headers, request::Kind::Headers); + append_cost(s, &self.transaction_index, request::Kind::TransactionIndex); append_cost(s, &self.body, request::Kind::Body); append_cost(s, &self.receipts, request::Kind::Receipts); append_cost(s, &self.account, request::Kind::Account); @@ -136,6 +139,7 @@ impl Decodable for CostTable { let base = rlp.val_at(0)?; let mut headers = None; + let mut transaction_index = None; let mut body = None; let mut receipts = None; let mut account = None; @@ -148,6 +152,7 @@ impl Decodable for CostTable { let cost = cost_list.val_at(1)?; match cost_list.val_at(0)? { request::Kind::Headers => headers = Some(cost), + request::Kind::TransactionIndex => transaction_index = Some(cost), request::Kind::Body => body = Some(cost), request::Kind::Receipts => receipts = Some(cost), request::Kind::Account => account = Some(cost), @@ -163,6 +168,7 @@ impl Decodable for CostTable { Ok(CostTable { base: base, headers: unwrap_cost(headers)?, + transaction_index: unwrap_cost(transaction_index)?, body: unwrap_cost(body)?, receipts: unwrap_cost(receipts)?, account: unwrap_cost(account)?, @@ -224,6 +230,7 @@ impl FlowParams { let costs = CostTable { base: 0.into(), headers: cost_for_kind(Kind::Headers), + transaction_index: cost_for_kind(Kind::TransactionIndex), body: cost_for_kind(Kind::Body), receipts: cost_for_kind(Kind::Receipts), account: cost_for_kind(Kind::Account), @@ -249,6 +256,7 @@ impl FlowParams { costs: CostTable { base: free_cost.clone(), headers: free_cost.clone(), + transaction_index: free_cost.clone(), body: free_cost.clone(), receipts: free_cost.clone(), account: free_cost.clone(), @@ -278,6 +286,7 @@ impl FlowParams { match *request { Request::Headers(ref req) => self.costs.headers * req.max.into(), Request::HeaderProof(_) => self.costs.header_proof, + Request::TransactionIndex(_) => self.costs.transaction_index, Request::Body(_) => self.costs.body, Request::Receipts(_) => self.costs.receipts, Request::Account(_) => self.costs.account, diff --git a/ethcore/light/src/net/request_set.rs b/ethcore/light/src/net/request_set.rs index 1277c2615..35182f0bf 100644 --- a/ethcore/light/src/net/request_set.rs +++ b/ethcore/light/src/net/request_set.rs @@ -132,6 +132,7 @@ fn compute_timeout(reqs: &Requests) -> Duration { tm + match *req { Request::Headers(_) => timeout::HEADERS, Request::HeaderProof(_) => timeout::HEADER_PROOF, + Request::TransactionIndex(_) => timeout::TRANSACTION_INDEX, Request::Receipts(_) => timeout::RECEIPT, Request::Body(_) => timeout::BODY, Request::Account(_) => timeout::PROOF, diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs index ceba60dda..ff4a32535 100644 --- a/ethcore/light/src/types/request/mod.rs +++ b/ethcore/light/src/types/request/mod.rs @@ -32,6 +32,11 @@ pub use self::header_proof::{ Incomplete as IncompleteHeaderProofRequest, Response as HeaderProofResponse }; +pub use self::transaction_index::{ + Complete as CompleteTransactionIndexRequest, + Incomplete as IncompleteTransactionIndexRequest, + Response as TransactionIndexResponse +}; pub use self::block_body::{ Complete as CompleteBodyRequest, Incomplete as IncompleteBodyRequest, @@ -242,7 +247,8 @@ pub enum Request { Headers(IncompleteHeadersRequest), /// A request for a header proof (from a CHT) HeaderProof(IncompleteHeaderProofRequest), - // TransactionIndex, + /// A request for a transaction index by hash. + TransactionIndex(IncompleteTransactionIndexRequest), /// A request for a block's receipts. Receipts(IncompleteReceiptsRequest), /// A request for a block body. @@ -264,7 +270,8 @@ pub enum CompleteRequest { Headers(CompleteHeadersRequest), /// A request for a header proof (from a CHT) HeaderProof(CompleteHeaderProofRequest), - // TransactionIndex, + /// A request for a transaction index by hash. + TransactionIndex(CompleteTransactionIndexRequest), /// A request for a block's receipts. Receipts(CompleteReceiptsRequest), /// A request for a block body. @@ -285,6 +292,7 @@ impl CompleteRequest { match *self { CompleteRequest::Headers(_) => Kind::Headers, CompleteRequest::HeaderProof(_) => Kind::HeaderProof, + CompleteRequest::TransactionIndex(_) => Kind::TransactionIndex, CompleteRequest::Receipts(_) => Kind::Receipts, CompleteRequest::Body(_) => Kind::Body, CompleteRequest::Account(_) => Kind::Account, @@ -301,6 +309,7 @@ impl Request { match *self { Request::Headers(_) => Kind::Headers, Request::HeaderProof(_) => Kind::HeaderProof, + Request::TransactionIndex(_) => Kind::TransactionIndex, Request::Receipts(_) => Kind::Receipts, Request::Body(_) => Kind::Body, Request::Account(_) => Kind::Account, @@ -316,6 +325,7 @@ impl Decodable for Request { match rlp.val_at::(0)? { Kind::Headers => Ok(Request::Headers(rlp.val_at(1)?)), Kind::HeaderProof => Ok(Request::HeaderProof(rlp.val_at(1)?)), + Kind::TransactionIndex => Ok(Request::TransactionIndex(rlp.val_at(1)?)), Kind::Receipts => Ok(Request::Receipts(rlp.val_at(1)?)), Kind::Body => Ok(Request::Body(rlp.val_at(1)?)), Kind::Account => Ok(Request::Account(rlp.val_at(1)?)), @@ -336,6 +346,7 @@ impl Encodable for Request { match *self { Request::Headers(ref req) => s.append(req), Request::HeaderProof(ref req) => s.append(req), + Request::TransactionIndex(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), @@ -356,6 +367,7 @@ impl IncompleteRequest for Request { match *self { Request::Headers(ref req) => req.check_outputs(f), Request::HeaderProof(ref req) => req.check_outputs(f), + Request::TransactionIndex(ref req) => req.check_outputs(f), Request::Receipts(ref req) => req.check_outputs(f), Request::Body(ref req) => req.check_outputs(f), Request::Account(ref req) => req.check_outputs(f), @@ -369,6 +381,7 @@ impl IncompleteRequest for Request { match *self { Request::Headers(ref req) => req.note_outputs(f), Request::HeaderProof(ref req) => req.note_outputs(f), + Request::TransactionIndex(ref req) => req.note_outputs(f), Request::Receipts(ref req) => req.note_outputs(f), Request::Body(ref req) => req.note_outputs(f), Request::Account(ref req) => req.note_outputs(f), @@ -382,6 +395,7 @@ impl IncompleteRequest for Request { match *self { Request::Headers(ref mut req) => req.fill(oracle), Request::HeaderProof(ref mut req) => req.fill(oracle), + Request::TransactionIndex(ref mut req) => req.fill(oracle), Request::Receipts(ref mut req) => req.fill(oracle), Request::Body(ref mut req) => req.fill(oracle), Request::Account(ref mut req) => req.fill(oracle), @@ -395,6 +409,7 @@ impl IncompleteRequest for Request { match self { Request::Headers(req) => req.complete().map(CompleteRequest::Headers), Request::HeaderProof(req) => req.complete().map(CompleteRequest::HeaderProof), + Request::TransactionIndex(req) => req.complete().map(CompleteRequest::TransactionIndex), Request::Receipts(req) => req.complete().map(CompleteRequest::Receipts), Request::Body(req) => req.complete().map(CompleteRequest::Body), Request::Account(req) => req.complete().map(CompleteRequest::Account), @@ -408,6 +423,7 @@ impl IncompleteRequest for Request { match *self { Request::Headers(ref mut req) => req.adjust_refs(mapping), Request::HeaderProof(ref mut req) => req.adjust_refs(mapping), + Request::TransactionIndex(ref mut req) => req.adjust_refs(mapping), Request::Receipts(ref mut req) => req.adjust_refs(mapping), Request::Body(ref mut req) => req.adjust_refs(mapping), Request::Account(ref mut req) => req.adjust_refs(mapping), @@ -441,7 +457,8 @@ pub enum Kind { Headers = 0, /// A request for a header proof. HeaderProof = 1, - // TransactionIndex = 2, + /// A request for a transaction index. + TransactionIndex = 2, /// A request for block receipts. Receipts = 3, /// A request for a block body. @@ -461,7 +478,7 @@ impl Decodable for Kind { match rlp.as_val::()? { 0 => Ok(Kind::Headers), 1 => Ok(Kind::HeaderProof), - // 2 => Ok(Kind::TransactionIndex), + 2 => Ok(Kind::TransactionIndex), 3 => Ok(Kind::Receipts), 4 => Ok(Kind::Body), 5 => Ok(Kind::Account), @@ -486,7 +503,8 @@ pub enum Response { Headers(HeadersResponse), /// A response for a header proof (from a CHT) HeaderProof(HeaderProofResponse), - // TransactionIndex, + /// A response for a transaction index. + TransactionIndex(TransactionIndexResponse), /// A response for a block's receipts. Receipts(ReceiptsResponse), /// A response for a block body. @@ -507,6 +525,7 @@ impl ResponseLike for Response { match *self { Response::Headers(ref res) => res.fill_outputs(f), Response::HeaderProof(ref res) => res.fill_outputs(f), + Response::TransactionIndex(ref res) => res.fill_outputs(f), Response::Receipts(ref res) => res.fill_outputs(f), Response::Body(ref res) => res.fill_outputs(f), Response::Account(ref res) => res.fill_outputs(f), @@ -523,6 +542,7 @@ impl Response { match *self { Response::Headers(_) => Kind::Headers, Response::HeaderProof(_) => Kind::HeaderProof, + Response::TransactionIndex(_) => Kind::TransactionIndex, Response::Receipts(_) => Kind::Receipts, Response::Body(_) => Kind::Body, Response::Account(_) => Kind::Account, @@ -538,6 +558,7 @@ impl Decodable for Response { match rlp.val_at::(0)? { Kind::Headers => Ok(Response::Headers(rlp.val_at(1)?)), Kind::HeaderProof => Ok(Response::HeaderProof(rlp.val_at(1)?)), + Kind::TransactionIndex => Ok(Response::TransactionIndex(rlp.val_at(1)?)), Kind::Receipts => Ok(Response::Receipts(rlp.val_at(1)?)), Kind::Body => Ok(Response::Body(rlp.val_at(1)?)), Kind::Account => Ok(Response::Account(rlp.val_at(1)?)), @@ -558,6 +579,7 @@ impl Encodable for Response { match *self { Response::Headers(ref res) => s.append(res), Response::HeaderProof(ref res) => s.append(res), + Response::TransactionIndex(ref res) => s.append(res), Response::Receipts(ref res) => s.append(res), Response::Body(ref res) => s.append(res), Response::Account(ref res) => s.append(res), @@ -864,6 +886,117 @@ pub mod header_proof { } } +/// Request and response for transaction index. +pub mod transaction_index { + use super::{Field, NoSuchOutput, OutputKind, Output}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use util::H256; + + /// Potentially incomplete transaction index request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Incomplete { + /// Transaction hash to get index for. + pub hash: Field, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + 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; + type Response = Response; + + fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + match self.hash { + Field::Scalar(_) => Ok(()), + Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash), + } + } + + fn note_outputs(&self, mut f: F) where F: FnMut(usize, OutputKind) { + f(0, OutputKind::Number); + f(1, OutputKind::Hash); + } + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + if let Field::BackReference(req, idx) = self.hash { + self.hash = match oracle(req, idx) { + Ok(Output::Number(hash)) => Field::Scalar(hash.into()), + _ => Field::BackReference(req, idx), + } + } + } + + fn complete(self) -> Result { + Ok(Complete { + hash: self.hash.into_scalar()?, + }) + } + + fn adjust_refs(&mut self, mapping: F) where F: FnMut(usize) -> usize { + self.hash.adjust_req(mapping) + } + } + + /// A complete transaction index request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Complete { + /// The transaction hash to get index for. + pub hash: H256, + } + + /// The output of a request for transaction index. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Response { + /// Block number. + pub num: u64, + /// Block hash + pub hash: H256, + /// Index in block. + pub index: u64, + } + + impl super::ResponseLike for Response { + /// Fill reusable outputs by providing them to the function. + fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { + f(0, Output::Number(self.num)); + f(1, Output::Hash(self.hash)); + } + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Response { + num: rlp.val_at(0)?, + hash: rlp.val_at(1)?, + index: rlp.val_at(2)?, + }) + } + } + + impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3) + .append(&self.num) + .append(&self.hash) + .append(&self.index); + } + } +} + /// Request and response for block receipts pub mod block_receipts { use super::{Field, NoSuchOutput, OutputKind, Output}; @@ -1704,6 +1837,26 @@ mod tests { check_roundtrip(full_res); } + #[test] + fn transaction_index_roundtrip() { + let req = IncompleteTransactionIndexRequest { + hash: Field::Scalar(Default::default()), + }; + + let full_req = Request::TransactionIndex(req.clone()); + let res = TransactionIndexResponse { + num: 1000, + hash: ::util::H256::random(), + index: 4, + }; + let full_res = Response::TransactionIndex(res.clone()); + + check_roundtrip(req); + check_roundtrip(full_req); + check_roundtrip(res); + check_roundtrip(full_res); + } + #[test] fn receipts_roundtrip() { let req = IncompleteReceiptsRequest {