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

Include the seal when populating the header for a new block #11475

Merged
merged 12 commits into from
Feb 14, 2020

Conversation

dvdplm
Copy link
Collaborator

@dvdplm dvdplm commented Feb 10, 2020

Include the seal when populating the header for a new block so that the state root is calculated properly.

The issue here is that when emptyStepTransition and maximumEmptySteps are both set in the chain spec there are verification errors on block import, at Stage 5.
A block is issued by a node and the other nodes can't verify it: state root mismatch.
Now, the Seal fields contain EmptySteps after the transition and each empty step implies a reward for the validator that emitted the empty step. This means that the accounts involved get touched, so the state root changes. Before that last step of the verification we call check_and_lock_block() in client.rs which calls enact_verified() which calls enact().
enact() essentially copies over data from the incoming block header (the one with the Seal with the EmptySteps) but before this PR it does not copy over the seal from the block being imported to the newly prepared LockedBlock.
The rewards are bestowed in on_close_block() and then the state is committed (which sets the state root).
I think this is what is going on. I didn't use LockedBlock::seal() because it returns a SealedBlock and I wasn't sure if we should change the return type tbh.

Fixes #11445

* master:
  [eth classic chainspec]: remove `balance = 1` (#11459)
  just to make sure we don't screw up this again (#11455)
  backwards compatible call_type creation_method (#11450)
Before this the `header_empty_steps_raw` would panic if the chain has already progressed beyond the block number set in `emptyStepsTransition`. As this is a user accessible configuration option I don't think we should panic.
Nothing really interesting here, renames or removes some methods. Adds some docs and extends a test a bit to clarify the behaviour of the code.
@dvdplm dvdplm changed the title Thank you for your Pull Request! Include the seal when populating the header for a new block Feb 10, 2020
* master:
  Use parity-crypto updated to use upstream rust-secp256k1 (#11406)
  Cleanup some code in Aura (#11466)
  upgrade some of the dependencies (#11467)
  Some more release track changes to README.md (#11465)
  Update simple one-line installer due to switching to a single stable release track (#11463)
  update Dockerfile (#11461)
  Implement EIP-2124 (#11456)
@dvdplm dvdplm self-assigned this Feb 10, 2020
@dvdplm dvdplm added A0-pleasereview 🤓 Pull request needs code review. M4-core ⛓ Core client code / Rust. labels Feb 11, 2020
@@ -222,6 +222,7 @@ impl<'x> OpenBlock<'x> {
self.block.header.set_timestamp(header.timestamp());
self.block.header.set_uncles_hash(*header.uncles_hash());
self.block.header.set_transactions_root(*header.transactions_root());
self.block.header.set_seal(header.seal().to_vec());
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The fix for #11445 is here: without this the LockedBlock built by enact() lacks the seal and this causes the state root to be wrong.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't seem right for me though. The idea with block types is that you go from:
OpenBlock -> ClosedBlock -> LockedBlock -> SealedBlock
and only the last type is guaranteed to have the seal.

How come the seal is included in the state root anyway? It's two separate things and really I don't see a way how seal affects the state?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added comment + ref to this PR. It still feels a bit dirty to do this here and I'm open to alternatives.

Copy link
Collaborator

@niklasad1 niklasad1 Feb 14, 2020

Choose a reason for hiding this comment

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

question: is this not called from Miner::seal_and_import_block_internally code path? It seems like Miner::prepare_block doesn't call populate_from, correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure I understand what you mean, but the reverse call-graph for populate_from() looks like this:

populate_from() is called by…
	enact() is called by…
		enact_verified() is called by…
			check_and_lock_block() is called by…
				import_verified_blocks() is called by… (in impl Importer)
					import_verified_blocks() is called by… (in impl ImportBlock for Client)
						message() is called by… (ClientIoMessage::BlockVerified)
						flush_queue()

In other words, populate_from() is only called on the block verification path, not for block producing.

Copy link
Collaborator

Choose a reason for hiding this comment

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

cool, yeah that's what I meant.

@dvdplm dvdplm requested a review from tomusdrw February 11, 2020 16:13
Copy link
Collaborator

@tomusdrw tomusdrw left a comment

Choose a reason for hiding this comment

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

I need some clarifications on the original issue, but for me it seems unlikely that it actually affect state root at all. However if we can prove that it actually solves the issue, then let's merge it but let's also add docs what the hell happens here.

ethcore/engines/authority-round/src/lib.rs Outdated Show resolved Hide resolved
@@ -222,6 +222,7 @@ impl<'x> OpenBlock<'x> {
self.block.header.set_timestamp(header.timestamp());
self.block.header.set_uncles_hash(*header.uncles_hash());
self.block.header.set_transactions_root(*header.transactions_root());
self.block.header.set_seal(header.seal().to_vec());
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't seem right for me though. The idea with block types is that you go from:
OpenBlock -> ClosedBlock -> LockedBlock -> SealedBlock
and only the last type is guaranteed to have the seal.

How come the seal is included in the state root anyway? It's two separate things and really I don't see a way how seal affects the state?

@tomusdrw
Copy link
Collaborator

Oh, apologies. I just read the description and now I understand how seal can affect state via block rewards. I think it's a good idea to add this as a documentation for seal copying line in populate_from

@niklasad1 niklasad1 added A8-looksgood 🦄 Pull request is reviewed well. and removed A0-pleasereview 🤓 Pull request needs code review. labels Feb 14, 2020
Copy link
Collaborator

@niklasad1 niklasad1 left a comment

Choose a reason for hiding this comment

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

looks good to me, but wouldn't hurt to get another pair of eyes on this one because I'm not super intimate with this.

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.

changes lgtm. like @tomusdrw it also wasn't obvious to me the effects of the seal in state root calculation when we discussed this some days ago.
what I don't know is if this issue was always there or if it surfaced due to some recent change. I believe it must be the later because I tested the empty steps feature when I implemented it, and as it stands (before this PR) it is impossible to use it.

ethcore/engines/authority-round/src/lib.rs Outdated Show resolved Hide resolved
ethcore/engines/authority-round/src/lib.rs Outdated Show resolved Hide resolved
ethcore/src/block.rs Outdated Show resolved Hide resolved
dvdplm and others added 3 commits February 14, 2020 16:30
Co-Authored-By: André Silva <andre.beat@gmail.com>
Co-Authored-By: André Silva <andre.beat@gmail.com>
Co-Authored-By: André Silva <andre.beat@gmail.com>
@dvdplm
Copy link
Collaborator Author

dvdplm commented Feb 14, 2020

what I don't know is if this issue was always there or if it surfaced due to some recent change. I believe it must be the later because I tested the empty steps feature when I implemented it, and as it stands (before this PR) it is impossible to use it.

Yeah, that's my biggest concern too: it must have been broken for quite while. I guess it's possible that users run old versions and haven't run into this yet.

@niklasad1
Copy link
Collaborator

niklasad1 commented Feb 14, 2020

Yeah, that's my biggest concern too: it must have been broken for quite while. I guess it's possible that users run old versions and haven't run into this yet.

I still suspect this Clique Engine PR but I might be wrong :)

@niklasad1 niklasad1 merged commit 856a075 into master Feb 14, 2020
@niklasad1 niklasad1 deleted the dp/fix/maximumEmptySteps-11445 branch February 14, 2020 18:01
@dvdplm dvdplm added the B0-patch-stable 🕷 Pull request should also be back-ported to the stable branch. label Feb 14, 2020
ordian added a commit that referenced this pull request Mar 6, 2020
* master: (27 commits)
  Faster kill_garbage (#11514)
  [EngineSigner]: don't sign message with only zeroes (#11524)
  fix compilation warnings (#11522)
  [ethcore cleanup]: various unrelated fixes from `#11493` (#11507)
  Add benchmark for transaction execution (#11509)
  Add Smart Contract License v1.0
  Misc fixes (#11510)
  [dependencies]: unify `rustc-hex` (#11506)
  Activate on-chain randomness in POA Sokol (#11505)
  Grab bag of cleanup (#11504)
  Implement eth/64 (EIP-2364) and drop support for eth/62 (#11472)
  [dependencies]: remove `util/macros` (#11501)
  OpenEthereum bootnodes are added (#11499)
  [ci benches]: use `all-features` (#11496)
  [verification]: make test-build compile standalone (#11495)
  complete null-signatures removal (#11491)
  Include the seal when populating the header for a new block (#11475)
  fix compilation warnings (#11492)
  cargo update -p cmake (#11490)
  update to published rlp-derive (#11489)
  ...
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. B0-patch-stable 🕷 Pull request should also be back-ported to the stable branch. M4-core ⛓ Core client code / Rust.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[permission] finalizeChange is not called when maximumEmptySteps is set
4 participants