Include the seal when populating the header for a new block#11475
Conversation
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.
* 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)
| 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()); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Added comment + ref to this PR. It still feels a bit dirty to do this here and I'm open to alternatives.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
cool, yeah that's what I meant.
tomusdrw
left a comment
There was a problem hiding this comment.
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.
| 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()); |
There was a problem hiding this comment.
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?
|
Oh, apologies. I just read the description and now I understand how |
andresilva
left a comment
There was a problem hiding this comment.
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.
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>
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 :) |
* 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) ...
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
emptyStepTransitionandmaximumEmptyStepsare 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
EmptyStepsafter 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 callcheck_and_lock_block()inclient.rswhich callsenact_verified()which callsenact().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 preparedLockedBlock.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 aSealedBlockand I wasn't sure if we should change the return type tbh.Fixes #11445