storing block receitps in db, client logs method implementation

This commit is contained in:
debris 2016-02-17 12:35:37 +01:00
parent b01652f3e7
commit 5826a34ebb
8 changed files with 236 additions and 29 deletions

View File

@ -50,7 +50,9 @@ pub struct CacheSize {
/// Logs cache size.
pub block_logs: usize,
/// Blooms cache size.
pub blocks_blooms: usize
pub blocks_blooms: usize,
/// Block receipts size.
pub block_receipts: usize
}
struct BloomIndexer {
@ -147,6 +149,9 @@ pub trait BlockProvider {
/// Get the address of transaction with given hash.
fn transaction_address(&self, hash: &H256) -> Option<TransactionAddress>;
/// Get receipts of block with given hash.
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts>;
/// Get the partial-header of a block.
fn block_header(&self, hash: &H256) -> Option<Header> {
self.block(hash).map(|bytes| BlockView::new(&bytes).header())
@ -223,6 +228,7 @@ pub struct BlockChain {
transaction_addresses: RwLock<HashMap<H256, TransactionAddress>>,
block_logs: RwLock<HashMap<H256, BlockLogBlooms>>,
blocks_blooms: RwLock<HashMap<H256, BlocksBlooms>>,
block_receipts: RwLock<HashMap<H256, BlockReceipts>>,
extras_db: DB,
blocks_db: DB,
@ -287,6 +293,11 @@ impl BlockProvider for BlockChain {
self.query_extras(hash, &self.transaction_addresses)
}
/// Get receipts of block with given hash.
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> {
self.query_extras(hash, &self.block_receipts)
}
/// Returns numbers of blocks containing given bloom.
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber> {
let filter = ChainFilter::new(self, self.bloom_indexer.index_size(), self.bloom_indexer.levels());
@ -348,6 +359,7 @@ impl BlockChain {
transaction_addresses: RwLock::new(HashMap::new()),
block_logs: RwLock::new(HashMap::new()),
blocks_blooms: RwLock::new(HashMap::new()),
block_receipts: RwLock::new(HashMap::new()),
extras_db: extras_db,
blocks_db: blocks_db,
cache_man: RwLock::new(cache_man),
@ -504,7 +516,7 @@ impl BlockChain {
/// Inserts the block into backing cache database.
/// Expects the block to be valid and already verified.
/// If the block is already known, does nothing.
pub fn insert_block(&self, bytes: &[u8], receipts: &[Receipt]) {
pub fn insert_block(&self, bytes: &[u8], receipts: Vec<Receipt>) {
// create views onto rlp
let block = BlockView::new(bytes);
let header = block.header_view();
@ -546,8 +558,7 @@ impl BlockChain {
/// Transforms block into WriteBatch that may be written into database
/// Additionally, if it's new best block it returns new best block object.
//fn block_to_extras_insert_batch(&self, bytes: &[u8], _receipts: &[Receipt]) -> (WriteBatch, Option<BestBlock>, BlockDetails) {
fn block_to_extras_update(&self, bytes: &[u8], _receipts: &[Receipt]) -> ExtrasUpdate {
fn block_to_extras_update(&self, bytes: &[u8], receipts: Vec<Receipt>) -> ExtrasUpdate {
// create views onto rlp
let block = BlockView::new(bytes);
let header = block.header_view();
@ -585,11 +596,8 @@ impl BlockChain {
});
}
// save blooms (is it really required?). maybe store receipt whole instead?
//let blooms: Vec<H2048> = receipts.iter().map(|r| r.log_bloom.clone()).collect();
//batch.put_extras(&hash, &BlockLogBlooms {
//blooms: blooms
//});
// update block receipts
batch.put_extras(&hash, &BlockReceipts::new(receipts));
// if it's not new best block, just return
if !is_new_best {
@ -741,7 +749,8 @@ impl BlockChain {
block_details: self.block_details.read().unwrap().heap_size_of_children(),
transaction_addresses: self.transaction_addresses.read().unwrap().heap_size_of_children(),
block_logs: self.block_logs.read().unwrap().heap_size_of_children(),
blocks_blooms: self.blocks_blooms.read().unwrap().heap_size_of_children()
blocks_blooms: self.blocks_blooms.read().unwrap().heap_size_of_children(),
block_receipts: self.block_receipts.read().unwrap().heap_size_of_children()
}
}
@ -773,6 +782,7 @@ impl BlockChain {
let mut transaction_addresses = self.transaction_addresses.write().unwrap();
let mut block_logs = self.block_logs.write().unwrap();
let mut blocks_blooms = self.blocks_blooms.write().unwrap();
let mut block_receipts = self.block_receipts.write().unwrap();
for id in cache_man.cache_usage.pop_back().unwrap().into_iter() {
cache_man.in_use.remove(&id);
@ -782,6 +792,7 @@ impl BlockChain {
CacheID::Extras(ExtrasIndex::TransactionAddress, h) => { transaction_addresses.remove(&h); },
CacheID::Extras(ExtrasIndex::BlockLogBlooms, h) => { block_logs.remove(&h); },
CacheID::Extras(ExtrasIndex::BlocksBlooms, h) => { blocks_blooms.remove(&h); },
CacheID::Extras(ExtrasIndex::BlockReceipts, h) => { block_receipts.remove(&h); },
_ => panic!(),
}
}
@ -823,7 +834,7 @@ mod tests {
let first = "f90285f90219a03caa2203f3d7c136c0295ed128a7d31cea520b1ca5e27afe17d0853331798942a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0bac6177a79e910c98d86ec31a09ae37ac2de15b754fd7bed1ba52362c49416bfa0d45893a296c1490a978e0bd321b5f2635d8280365c1fe9f693d65f233e791344a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefd882560b845627cb99a00102030405060708091011121314151617181920212223242526272829303132a08ccb2837fb2923bd97e8f2d08ea32012d6e34be018c73e49a0f98843e8f47d5d88e53be49fec01012ef866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ba0cb088b8d2ff76a7b2c6616c9d02fb6b7a501afbf8b69d7180b09928a1b80b5e4a06448fe7476c606582039bb72a9f6f4b4fad18507b8dfbd00eebbe151cc573cd2c0".from_hex().unwrap();
bc.insert_block(&first, &[]);
bc.insert_block(&first, vec![]);
let first_hash = H256::from_str("a940e5af7d146b3b917c953a82e1966b906dace3a4e355b5b0a4560190357ea1").unwrap();
@ -856,10 +867,10 @@ mod tests {
let temp = RandomTempPath::new();
let bc = BlockChain::new(&genesis, temp.as_path());
bc.insert_block(&b1, &[]);
bc.insert_block(&b2, &[]);
bc.insert_block(&b3a, &[]);
bc.insert_block(&b3b, &[]);
bc.insert_block(&b1, vec![]);
bc.insert_block(&b2, vec![]);
bc.insert_block(&b3a, vec![]);
bc.insert_block(&b3b, vec![]);
assert_eq!(bc.best_block_hash(), best_block_hash);
assert_eq!(bc.block_number(&genesis_hash).unwrap(), 0);
@ -936,7 +947,7 @@ mod tests {
{
let bc = BlockChain::new(&genesis, temp.as_path());
assert_eq!(bc.best_block_hash(), genesis_hash);
bc.insert_block(&b1, &[]);
bc.insert_block(&b1, vec![]);
assert_eq!(bc.best_block_hash(), b1_hash);
}
@ -995,7 +1006,7 @@ mod tests {
let temp = RandomTempPath::new();
let bc = BlockChain::new(&genesis, temp.as_path());
bc.insert_block(&b1, &[]);
bc.insert_block(&b1, vec![]);
let transactions = bc.transactions(&b1_hash).unwrap();
assert_eq!(transactions.len(), 7);
@ -1026,13 +1037,13 @@ mod tests {
assert_eq!(blocks_b1, vec![]);
assert_eq!(blocks_b2, vec![]);
bc.insert_block(&b1, &[]);
bc.insert_block(&b1, vec![]);
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 3);
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 3);
assert_eq!(blocks_b1, vec![1]);
assert_eq!(blocks_b2, vec![]);
bc.insert_block(&b2, &[]);
bc.insert_block(&b2, vec![]);
let blocks_b1 = bc.blocks_with_bloom(&bloom_b1, 0, 3);
let blocks_b2 = bc.blocks_with_bloom(&bloom_b2, 0, 3);
assert_eq!(blocks_b1, vec![1]);

View File

@ -33,7 +33,9 @@ use env_info::LastHashes;
use verification::*;
use block::*;
use transaction::LocalizedTransaction;
use extras::TransactionAddress;
use extras::{TransactionAddress, BlockReceipts};
use filter::Filter;
use log_entry::LocalizedLogEntry;
pub use blockchain::TreeRoute;
/// Uniquely identifies block.
@ -147,6 +149,9 @@ pub trait BlockChainClient : Sync + Send {
/// Returns numbers of blocks containing given bloom.
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockId, to_block: BlockId) -> Option<Vec<BlockNumber>>;
/// Returns logs matching given filter.
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry>;
}
#[derive(Default, Clone, Debug, Eq, PartialEq)]
@ -307,7 +312,7 @@ impl Client {
good_blocks.push(header.hash().clone());
self.chain.write().unwrap().insert_block(&block.bytes, result.block().receipts()); //TODO: err here?
self.chain.write().unwrap().insert_block(&block.bytes, result.block().receipts().clone()); //TODO: err here?
let ancient = if header.number() >= HISTORY { Some(header.number() - HISTORY) } else { None };
match result.drain().commit(header.number(), &header.hash(), ancient.map(|n|(n, self.chain.read().unwrap().block_hash(n).unwrap()))) {
Ok(_) => (),
@ -474,6 +479,50 @@ impl BlockChainClient for Client {
_ => None
}
}
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry> {
let mut blocks = filter.bloom_possibilities().iter()
.map(|bloom| self.blocks_with_bloom(bloom, filter.from_block.clone(), filter.to_block.clone()))
.filter_map(|m| m)
.flat_map(|m| m)
// remove duplicate elements
.collect::<HashSet<u64>>()
.into_iter()
.collect::<Vec<u64>>();
blocks.sort();
blocks.into_iter()
.map(|number| self.chain.read().unwrap().block_hash(number).map(|hash| (number, hash)))
.filter_map(|m| m)
.map(|(number, hash)| self.chain.read().unwrap().block_receipts(&hash).map(|r| (number, hash, r.receipts)))
.filter_map(|m| m)
.map(|(number, hash, receipts)| {
let mut log_index = 0;
receipts.into_iter()
.enumerate()
.map(|(index, receipt)| {
log_index += receipt.logs.len();
receipt.logs.into_iter()
.enumerate()
.filter(|tuple| filter.matches(&tuple.1))
.map(|(i, log)| LocalizedLogEntry {
entry: log,
block_hash: hash.clone(),
block_number: number as usize,
transaction_hash: H256::new(),
transaction_index: index,
log_index: log_index
})
.collect::<Vec<LocalizedLogEntry>>()
})
.flat_map(|m| m)
.collect::<Vec<LocalizedLogEntry>>()
})
.flat_map(|m| m)
.collect()
}
}
impl MayPanic for Client {

View File

@ -16,9 +16,10 @@
//! Blockchain DB extras.
use rocksdb::{DB, Writable};
use util::*;
use header::BlockNumber;
use rocksdb::{DB, Writable};
use receipt::Receipt;
/// Represents index of extra data in database
#[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)]
@ -32,7 +33,9 @@ pub enum ExtrasIndex {
/// Block log blooms index
BlockLogBlooms = 3,
/// Block blooms index
BlocksBlooms = 4
BlocksBlooms = 4,
/// Block receipts index
BlockReceipts
}
/// trait used to write Extras data to db
@ -307,3 +310,43 @@ impl Encodable for TransactionAddress {
s.append(&self.index);
}
}
/// Contains all block receipts.
#[derive(Clone)]
pub struct BlockReceipts {
pub receipts: Vec<Receipt>
}
impl BlockReceipts {
pub fn new(receipts: Vec<Receipt>) -> Self {
BlockReceipts {
receipts: receipts
}
}
}
impl Decodable for BlockReceipts {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
Ok(BlockReceipts {
receipts: try!(Decodable::decode(decoder))
})
}
}
impl Encodable for BlockReceipts {
fn rlp_append(&self, s: &mut RlpStream) {
s.append(&self.receipts);
}
}
impl HeapSizeOf for BlockReceipts {
fn heap_size_of_children(&self) -> usize {
self.receipts.heap_size_of_children()
}
}
impl ExtrasIndexable for BlockReceipts {
fn extras_index() -> ExtrasIndex {
ExtrasIndex::BlockReceipts
}
}

View File

@ -17,26 +17,27 @@
use util::hash::*;
use util::sha3::*;
use client::BlockId;
use log_entry::LogEntry;
/// Blockchain log filter data.
pub struct Filter {
/// Blockchain will be searched from this block.
from_block: BlockId,
pub from_block: BlockId,
/// Till this block.
to_block: BlockId,
pub to_block: BlockId,
/// Search addresses.
///
/// If None, match all.
/// If specified, log must be produced by one of these addresses.
address: Option<Vec<Address>>,
pub address: Option<Vec<Address>>,
/// Search topics.
///
/// If None, match all.
/// If specified, log must contain one of these topics.
topics: [Option<Vec<H256>>; 4]
pub topics: [Option<Vec<H256>>; 4]
}
impl Filter {
@ -63,6 +64,23 @@ impl Filter {
}).flat_map(|m| m).collect()
})
}
/// Returns true if given log entry matches filter.
pub fn matches(&self, log: &LogEntry) -> bool {
let matches = match self.address {
Some(ref addresses) if !addresses.is_empty() => addresses.iter().fold(false, |res, address| {
res || &log.address == address
}),
_ => true
};
matches && self.topics.iter().enumerate().fold(true, |res, (i, topic)| match *topic {
Some(ref topics) if !topics.is_empty() => res && topics.iter().fold(false, | acc, topic | {
acc || log.topics.get(i) == Some(topic)
}),
_ => res,
})
}
}
#[cfg(test)]
@ -71,6 +89,7 @@ mod tests {
use util::hash::*;
use filter::Filter;
use client::BlockId;
use log_entry::LogEntry;
#[test]
fn test_bloom_possibilities_none() {
@ -149,4 +168,49 @@ mod tests {
assert_eq!(possibilities[0], H2048::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000").unwrap());
}
#[test]
fn test_filter_matches() {
let filter = Filter {
from_block: BlockId::Earliest,
to_block: BlockId::Latest,
address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]),
topics: [
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]),
Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap()]),
None, None
]
};
let entry0 = LogEntry {
address: Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
topics: vec![
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap(),
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
],
data: vec![]
};
let entry1 = LogEntry {
address: Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5e").unwrap(),
topics: vec![
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap(),
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
],
data: vec![]
};
let entry2 = LogEntry {
address: Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(),
topics: vec![
H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(),
],
data: vec![]
};
assert_eq!(filter.matches(&entry0), true);
assert_eq!(filter.matches(&entry1), false);
assert_eq!(filter.matches(&entry2), false);
}
}

View File

@ -37,6 +37,24 @@ impl Encodable for LogEntry {
}
}
impl Decodable for LogEntry {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let entry = LogEntry {
address: try!(d.val_at(0)),
topics: try!(d.val_at(1)),
data: try!(d.val_at(2)),
};
Ok(entry)
}
}
impl HeapSizeOf for LogEntry {
fn heap_size_of_children(&self) -> usize {
self.topics.heap_size_of_children() + self.data.heap_size_of_children()
}
}
impl LogEntry {
/// Calculates the bloom of this log entry.
pub fn bloom(&self) -> LogBloom {

View File

@ -55,6 +55,24 @@ impl Encodable for Receipt {
}
}
impl Decodable for Receipt {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let receipt = Receipt {
state_root: try!(d.val_at(0)),
gas_used: try!(d.val_at(1)),
log_bloom: try!(d.val_at(2)),
logs: try!(d.val_at(3)),
};
Ok(receipt)
}
}
impl HeapSizeOf for Receipt {
fn heap_size_of_children(&self) -> usize {
self.logs.heap_size_of_children()
}
}
#[test]
fn test_basic() {

View File

@ -224,7 +224,7 @@ pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult<BlockCh
let temp = RandomTempPath::new();
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path());
for block_order in 1..block_number {
bc.insert_block(&create_unverifiable_block(block_order, bc.best_block_hash()), &[]);
bc.insert_block(&create_unverifiable_block(block_order, bc.best_block_hash()), vec![]);
}
GuardedTempResult::<BlockChain> {
@ -237,7 +237,7 @@ pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempRes
let temp = RandomTempPath::new();
let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path());
for block_order in 1..block_number {
bc.insert_block(&create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), &[]);
bc.insert_block(&create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None), vec![]);
}
GuardedTempResult::<BlockChain> {

View File

@ -306,6 +306,10 @@ mod tests {
fn blocks_with_bloom(&self, _bloom: &H2048, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec<BlockNumber> {
unimplemented!()
}
fn block_receipts(&self, _hash: &H256) -> Option<BlockReceipts> {
unimplemented!()
}
}
fn basic_test(bytes: &[u8], engine: &Engine) -> Result<(), Error> {