diff --git a/ethcore/light/src/cache.rs b/ethcore/light/src/cache.rs new file mode 100644 index 000000000..a64c9076e --- /dev/null +++ b/ethcore/light/src/cache.rs @@ -0,0 +1,174 @@ +// Copyright 2015-2017 Parity Technologies (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 . + +//! Cache for data fetched from the network. +//! +//! Stores ancient block headers, bodies, receipts, and total difficulties. +//! Furthermore, stores a "gas price corpus" of relative recency, which is a sorted +//! vector of all gas prices from a recent range of blocks. + +use ethcore::encoded; +use ethcore::header::BlockNumber; +use ethcore::receipt::Receipt; + +use time::{SteadyTime, Duration}; +use util::{U256, H256}; +use util::cache::MemoryLruCache; + +/// Configuration for how much data to cache. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CacheSizes { + /// Maximum size, in bytes, of cached headers. + pub headers: usize, + /// Maximum size, in bytes, of cached canonical hashes. + pub canon_hashes: usize, + /// Maximum size, in bytes, of cached block bodies. + pub bodies: usize, + /// Maximum size, in bytes, of cached block receipts. + pub receipts: usize, + /// Maximum size, in bytes, of cached chain score for the block. + pub chain_score: usize, +} + +impl Default for CacheSizes { + fn default() -> Self { + const MB: usize = 1024 * 1024; + CacheSizes { + headers: 10 * MB, + canon_hashes: 3 * MB, + bodies: 20 * MB, + receipts: 10 * MB, + chain_score: 7 * MB, + } + } +} + +/// The light client data cache. +/// +/// Note that almost all getter methods take `&mut self` due to the necessity to update +/// the underlying LRU-caches on read. +pub struct Cache { + headers: MemoryLruCache, + canon_hashes: MemoryLruCache, + bodies: MemoryLruCache, + receipts: MemoryLruCache>, + chain_score: MemoryLruCache, + corpus: Option<(Vec, SteadyTime)>, + corpus_expiration: Duration, +} + +impl Cache { + /// Create a new data cache with the given sizes and gas price corpus expiration time. + pub fn new(sizes: CacheSizes, corpus_expiration: Duration) -> Self { + Cache { + headers: MemoryLruCache::new(sizes.headers), + canon_hashes: MemoryLruCache::new(sizes.canon_hashes), + bodies: MemoryLruCache::new(sizes.bodies), + receipts: MemoryLruCache::new(sizes.receipts), + chain_score: MemoryLruCache::new(sizes.chain_score), + corpus: None, + corpus_expiration: corpus_expiration, + } + } + + /// Query header by hash. + pub fn block_header(&mut self, hash: &H256) -> Option { + self.headers.get_mut(hash).map(|x| x.clone()) + } + + /// Query hash by number. + pub fn block_hash(&mut self, num: &BlockNumber) -> Option { + self.canon_hashes.get_mut(num).map(|x| x.clone()) + } + + /// Query block body by block hash. + pub fn block_body(&mut self, hash: &H256) -> Option { + self.bodies.get_mut(hash).map(|x| x.clone()) + } + + /// Query block receipts by block hash. + pub fn block_receipts(&mut self, hash: &H256) -> Option> { + self.receipts.get_mut(hash).map(|x| x.clone()) + } + + /// Query chain score by block hash. + pub fn chain_score(&mut self, hash: &H256) -> Option { + self.chain_score.get_mut(hash).map(|x| x.clone()) + } + + /// Cache the given header. + pub fn insert_block_header(&mut self, hash: H256, hdr: encoded::Header) { + self.headers.insert(hash, hdr); + } + + /// Cache the given canonical block hash. + pub fn insert_block_hash(&mut self, num: BlockNumber, hash: H256) { + self.canon_hashes.insert(num, hash); + } + + /// Cache the given block body. + pub fn insert_block_body(&mut self, hash: H256, body: encoded::Body) { + self.bodies.insert(hash, body); + } + + /// Cache the given block receipts. + pub fn insert_block_receipts(&mut self, hash: H256, receipts: Vec) { + self.receipts.insert(hash, receipts); + } + + /// Cache the given chain scoring. + pub fn insert_chain_score(&mut self, hash: H256, score: U256) { + self.chain_score.insert(hash, score); + } + + /// Get gas price corpus, if recent enough. + pub fn gas_price_corpus(&self) -> Option> { + let now = SteadyTime::now(); + + self.corpus.as_ref().and_then(|&(ref corpus, ref tm)| { + if *tm + self.corpus_expiration >= now { + Some(corpus.clone()) + } else { + None + } + }) + } + + /// Set the cached gas price corpus. + pub fn set_gas_price_corpus(&mut self, corpus: Vec) { + self.corpus = Some((corpus, SteadyTime::now())) + } +} + +#[cfg(test)] +mod tests { + use super::Cache; + use time::{Duration, SteadyTime}; + + #[test] + fn corpus_inaccessible() { + let mut cache = Cache::new(Default::default(), Duration::hours(5)); + + cache.set_gas_price_corpus(vec![]); + assert_eq!(cache.gas_price_corpus(), Some(vec![])); + + { + let corpus_time = &mut cache.corpus.as_mut().unwrap().1; + *corpus_time = *corpus_time - Duration::hours(6); + } + assert!(cache.gas_price_corpus().is_none()); + } +} diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index 94d267c7a..53d32f44e 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -37,6 +37,7 @@ pub mod cht; pub mod net; pub mod on_demand; pub mod transaction_queue; +pub mod cache; #[cfg(not(feature = "ipc"))] pub mod provider;