Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Light client logs should include 'from_block' when querying logs #9331

Merged
merged 3 commits into from
Aug 14, 2018

Conversation

cheme
Copy link
Contributor

@cheme cheme commented Aug 9, 2018

Fixes #8560 .

When using a websocket on logs, there was no entry displayed. The issue was that we called light client get logs method for each block (with a range of one block in the filter), but the method did not include the first block to query (with a range of one block it did include nothing).

This PR fixes this by making LightClient log works correctly (by including the start block of the filter).

For the websocket usecase it also increases OnDemand query a lot, so this other PR may be helpfull : #9318 (during my testing without #9318 the lightclient became a bit irresponsive after a while). There is also probably room for optimization in function 'notify_log', yet it should not directly impact network query (if OnDemand cache works as intended of course).

I did try to change as less as possible code, but from a personal point of view I would rather use :

      let mut receipts_futures: Vec<_> = Vec::new();
      for hdr in self.client.ancestry_iter(filter.to_block) {
        let end_cond =  BlockId::Hash(hdr.hash()) == filter.from_block
          || BlockId::Number(hdr.number()) == filter.from_block;

        let hdr_bloom = hdr.log_bloom();
        if bit_combos.iter().find(|&bloom| hdr_bloom & *bloom == *bloom).is_some() {
          let hdr_number = hdr.number();
          receipts_futures.push(
            self.on_demand.request(ctx, request::BlockReceipts(hdr.into())).expect(NO_INVALID_BACK_REFS)
            .map(move |x|{ (hdr_number, x) })
            );
        }
        if end_cond { break; }
      }
      let receipts_futures = receipts_futures;

than

			let receipts_futures: Vec<_> = self.client.ancestry_iter(filter.to_block)
				.take_while(|ref hdr|{
					BlockId::Hash(hdr.hash()) != filter.from_block
					&& BlockId::Number(hdr.number()) != filter.from_block
				})
				.chain(::std::iter::once(self.client.block_header(filter.from_block).expect("checked before")))
				.filter(|ref hdr| {
					let hdr_bloom = hdr.log_bloom();
					bit_combos.iter().find(|&bloom| hdr_bloom & *bloom == *bloom).is_some()
				})
				.map(|hdr| (hdr.number(), request::BlockReceipts(hdr.into())))
				.map(|(num, req)| self.on_demand.request(ctx, req).expect(NO_INVALID_BACK_REFS).map(move |x| (num, x)))
				.collect();

So if the reviewers prefer the more imperative version please tell me.

prior to this fix the pubsub process did send a
query for each new block header (and for each subs : there is something
to optimize here) by setting from and to of the filter at this block
number; but there was a bug in the code that fetch logs : it was non
inclusive for its start bound, meaning that with start bound = end bound
we never query any block (and attached logs).
@parity-cla-bot
Copy link

It looks like @cheme signed our Contributor License Agreement. 👍

Many thanks,

Parity Technologies CLA Bot

@cheme cheme added the A0-pleasereview 🤓 Pull request needs code review. label Aug 9, 2018
.take_while(|ref hdr| BlockId::Hash(hdr.hash()) != filter.from_block)
.take_while(|ref hdr|{
BlockId::Hash(hdr.hash()) != filter.from_block
&& BlockId::Number(hdr.number()) != filter.from_block
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bug. this take_while will never return false therefore this iterator will never end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

take_while(a).take_while(b) is equivalent to take_while(a && b), right?
Should we check for the BlockId::Latest case as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems correct to me (also when testing). I can still switch back to using two take_while (does not make a lot of difference as it combines correctly).

BlockId::Hash(hdr.hash()) != filter.from_block
&& BlockId::Number(hdr.number()) != filter.from_block
})
.chain(::std::iter::once(self.client.block_header(filter.from_block).expect("checked before")))
.filter(|ref hdr| {
let hdr_bloom = hdr.log_bloom();
bit_combos.iter().find(|&bloom| hdr_bloom & *bloom == *bloom).is_some()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not related to this pr, but it's more optimal to use contains_bloom

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this kind of logic should definitely use dedicated function.

BlockId::Hash(hdr.hash()) != filter.from_block
&& BlockId::Number(hdr.number()) != filter.from_block
})
.chain(::std::iter::once(self.client.block_header(filter.from_block).expect("checked before")))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, you can just write

.chain(self.client.block_header(filter.from_block).ok())

Option implements iterator

@debris debris added A4-gotissues 💥 Pull request is reviewed and has significant issues which must be addressed. and removed A0-pleasereview 🤓 Pull request needs code review. labels Aug 9, 2018
Use of bloom existing function to check if a bloom contains another.
.take_while(|ref hdr| BlockId::Hash(hdr.hash()) != filter.from_block)
.take_while(|ref hdr|{
BlockId::Hash(hdr.hash()) != filter.from_block
&& BlockId::Number(hdr.number()) != filter.from_block
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

take_while(a).take_while(b) is equivalent to take_while(a && b), right?
Should we check for the BlockId::Latest case as well?

BlockId::Hash(hdr.hash()) != filter.from_block
&& BlockId::Number(hdr.number()) != filter.from_block
})
.chain(Some(self.client.block_header(filter.from_block).expect("checked before")))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block_header can return None and I don't see where do we check it's not None, could you point me to that line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was tested in the case of a block hash (in block_number ) and I added a condition before to make sure the number case would be in range to, it is certainly not explicit enough.
I did a little change to test it explicitly in my last commit (plus remove the need to have two condition in takewhile).

@ordian ordian added A8-looksgood 🦄 Pull request is reviewed well. and removed A4-gotissues 💥 Pull request is reviewed and has significant issues which must be addressed. labels Aug 10, 2018
Copy link
Contributor

@andresilva andresilva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


match block_number(filter.to_block) {
Some(to) if to < from_block_number || from_block_number > best_number
=> return Either::A(future::ok(Vec::new())),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit but I would put the => in the previous line, I don't know what's our style on this though.

@debris debris merged commit fcb6cc1 into openethereum:master Aug 14, 2018
@5chdn 5chdn added this to the 2.1 milestone Aug 23, 2018
@5chdn 5chdn added the M4-core ⛓ Core client code / Rust. label Aug 23, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A8-looksgood 🦄 Pull request is reviewed well. M4-core ⛓ Core client code / Rust.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

light client: subscribe over websockets doesnt send new events
7 participants