-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Fetching logs by hash in blockchain database #8463
Changes from 6 commits
c08dcff
6eae998
cd805d2
20c5567
63df877
1f99d77
27d294e
bd27639
a7dd897
a9e4977
a6407ec
2963c3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1853,21 +1853,96 @@ impl BlockChainClient for Client { | |
} | ||
|
||
fn logs(&self, filter: Filter) -> Vec<LocalizedLogEntry> { | ||
let (from, to) = match (self.block_number_ref(&filter.from_block), self.block_number_ref(&filter.to_block)) { | ||
(Some(from), Some(to)) => (from, to), | ||
_ => return Vec::new(), | ||
}; | ||
macro_rules! return_empty_if_none { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could be replaced with |
||
( $v: expr ) => ( | ||
match $v { | ||
Some(value) => value, | ||
None => return Vec::new(), | ||
} | ||
) | ||
} | ||
|
||
let chain = self.chain.read(); | ||
let blocks = filter.bloom_possibilities().iter() | ||
.map(move |bloom| { | ||
chain.blocks_with_bloom(bloom, from, to) | ||
}) | ||
.flat_map(|m| m) | ||
// remove duplicate elements | ||
.collect::<HashSet<u64>>() | ||
.into_iter() | ||
.collect::<Vec<u64>>(); | ||
|
||
// First, check whether `filter.from_block` and `filter.to_block` is on the canon chain. If so, we can use the | ||
// optimized version. | ||
let is_canon = |id| { | ||
match id { | ||
// If it is referred by number, then it is always on the canon chain. | ||
&BlockId::Earliest | &BlockId::Latest | &BlockId::Number(_) => Some(true), | ||
// If it is referred by hash, we see whether a hash -> number -> hash conversion gives us the same | ||
// result. | ||
&BlockId::Hash(hash) => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Some(hash == chain.block_hash(chain.block_number(&hash)?)?), | ||
} | ||
}; | ||
|
||
let blocks = if return_empty_if_none!(is_canon(&filter.from_block)) && return_empty_if_none!(is_canon(&filter.to_block)) { | ||
// If we are on the canon chain, use bloom filter to fetch required hashes. | ||
let from = return_empty_if_none!(self.block_number_ref(&filter.from_block)); | ||
let to = return_empty_if_none!(self.block_number_ref(&filter.to_block)); | ||
|
||
let mut numbers = filter.bloom_possibilities().iter() | ||
.map(|bloom| { | ||
chain.blocks_with_bloom(bloom, from, to) | ||
}) | ||
.flat_map(|m| m) | ||
// remove duplicate elements | ||
.collect::<HashSet<u64>>() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think instead of |
||
.into_iter() | ||
.collect::<Vec<u64>>(); | ||
|
||
numbers.sort_by(|a, b| a.cmp(b)); | ||
numbers.into_iter() | ||
.filter_map(|n| chain.block_hash(n)) | ||
.collect::<Vec<H256>>() | ||
|
||
} else { | ||
// Otherwise, we use a slower version that finds a link between from_block and to_block. | ||
let get_hash = |id| { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That function already exists as |
||
match id { | ||
&BlockId::Earliest | &BlockId::Latest | &BlockId::Number(_) => | ||
chain.block_hash(self.block_number_ref(id)?), | ||
&BlockId::Hash(hash) => Some(hash), | ||
} | ||
}; | ||
|
||
let from_hash = return_empty_if_none!(get_hash(&filter.from_block)); | ||
let from_number = return_empty_if_none!(chain.block_number(&from_hash)); | ||
let to_hash = return_empty_if_none!(get_hash(&filter.to_block)); | ||
let to_number = return_empty_if_none!(chain.block_number(&to_hash)); | ||
|
||
let blooms = filter.bloom_possibilities(); | ||
let bloom_match = |header: &encoded::Header| { | ||
blooms.iter().any(|bloom| header.log_bloom().contains_bloom(bloom)) | ||
}; | ||
|
||
let mut blocks = Vec::new(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd wrap this into a block and use let (blocks, last_hash) = {
let mut blocks = Vec::new();
let mut current_hash = to_hash;
loop {
let header = return_empty_if_none!(chain.block_header_data(¤t_hash));
if bloom_match(&header) {
blocks.push(current_hash);
}
// Stop if `from` block is reached.
if header.number() <= from_number {
break;
}
current_hash = header.parent_hash();
}
(blocks, current_hash)
};
// check if we've actually reached the expected `from` block.
if last_hash != from_hash || blocks.is_empty() {
return Vec::new();
} |
||
let mut current_hash = to_hash; | ||
let mut current_number = to_number; | ||
|
||
if bloom_match(&return_empty_if_none!(chain.block_header_data(¤t_hash))) { | ||
blocks.push(current_hash); | ||
} | ||
|
||
while current_number > from_number { | ||
let header = return_empty_if_none!(chain.block_header_data(¤t_hash)); | ||
|
||
if bloom_match(&header) { | ||
blocks.push(current_hash); | ||
} | ||
|
||
current_hash = header.parent_hash(); | ||
current_number = current_number - 1; | ||
} | ||
|
||
if current_hash != from_hash || blocks.is_empty() { | ||
return Vec::new(); | ||
} | ||
|
||
blocks.reverse(); | ||
blocks | ||
}; | ||
|
||
self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment that
blocks
must be already sorted would be goodThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added!