Open gumb0 opened 5 years ago
Oh awesome, I was just thinking of putting together a plan for this. Great timing!
cc @jwasinger
A note on sealing (and block import) implementation:
The block coinbase field contains the vote (or is zeroed). During contract execution, it must be set to the author of the block and reset to the original value (the vote) after contract execution (and before the block is signed). Difficulty must also be set before contract execution.
@jwasinger yeah I've mentioned coinbase change in "Other important things" part.
And DIFFICULTY
opcode I think will work the same as for Ethash (will get it from the header)
My point was mainly to illustrate that you need to set the COINBASE
to a different value when executing contracts, and reset it afterwards to the vote value.
@jwasinger if this is not clarified in https://eips.ethereum.org/EIPS/eip-225 I suggest to create a PR improving the EIP.
Good point. Done https://github.com/ethereum/EIPs/pull/1846
Serializing signers and votes - the lists should be saved into DB periodically (the simplest solution - save them for each block, though this might be wasteful)
I've read through Parity's implementation and they do something simpler, though probably less efficient - just having an in-memory cache of vote states for a number (128) of blocks. In case required state is not present in the cache, it is reconstructed from the latest checkpoint block (by replaying all votes in blocks between checkpoint and required one)
I think it would be easier for us to start with something similar, too.
Updated Keeping track of signers
section now with a better understanding.
Added a bit of more info in other sections, too.
Some highlights of Parity's implementation that I find important/relevant for us:
PR with all changes https://github.com/paritytech/parity-ethereum/pull/9981
Higher-level description of how the code works https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/mod.rs#L17-L59
Data structure to keep the Clique signers/voting state https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/block_state.rs#L60
Processing the votes or checkpoint block (update Clique state from the new header) https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/block_state.rs#L163
Cache storing 128 recently used Clique states https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/mod.rs#L164
Function that reconstructs Clique state from checkpoint block if not found in cache https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/mod.rs#L272
Validating the header https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/mod.rs#L536
Filling the block to seal https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/mod.rs#L381 Another piece of filling the block, including setting the difficulty field https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/mod.rs#L699-L735
Calculating the timestamp when to seal the next block (including random delay for out-of-turn seal) https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/block_state.rs#L268
Optimization for recovery of signer addresses from block signatures: cache that stores recently recovered signer addresses https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/util.rs#L34
Signer address recovery function https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/util.rs#L38
Function to extract the signer list from checkpoint block header https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/util.rs#L85
https://eips.ethereum.org/EIPS/eip-225
Keeping track of signers
Each block has a corresponding list of currently authorized signers and the list of current pending votes. This signers/voting state may be represented as a separate class/struct and
CliqueSealEngine
will store the map of recently used block hash => clique voting state (Parity has LRU cache with 128 items for this)The block processing has an additional step of creating a voting state for this block (a new virtual method should be added to
SealEngineFace
for this). More exactly it does the following:SIGNER_LIMIT
, modify signers list, and discard all votes for this beneficiaryWhen required voting state is not found in the cache, it is reconstructed from the latest epoch transition block (checkpoint block)
extra-data
of the checkpoint block headerHandling of reorgs - nothing special is required to handle reorgs, when we have the cache of <block hash => voting state> for the last MAX_REORG_DEPTH of blocks. If reorg goes further than MAX_REORG_DEPTH, it will also work by replaying the required voting state from the latest checkpoint block.
Tests for signer list logic listed in EIP
(optional, I think this is not required for the first implementation) To get rid of heavy replaying voting from the checkpoint, we can serialize signers and votes into DB periodically.
Block validation
These checks should go into
verify
method of Clique's implementation ofSealEngineFace
.mixhash
is 0timestamp
>=parent.timestamp
+ 15sSIGNER_LIMIT
blocks agodifficulty
is either 1 or 2, depending on signer indexbeneficiary
is 0nonce
is 0nonce
is either 0 or 0xff..fextra-data
size is 32 + 65 bytesOther important things
COINBASE
opcode should return the signer and not the contents ofbeneficiary
. Probably this may be done by askingCliqueSealEngine
for the signer of the block before enacting it and putting it intoEnvInfo
that will be used by EVM.CLI
--goerli
and--rinkeby
(optional) Sealing
Check that we have a private key for the address given through
--author
This should probably go into
CliqueSealEngine::populateFromParent()
method:difficulty
= 1 if BLOCK_NUMBER % SIGNER_COUNT != SIGNER_INDEX, 2 otherwisebeneficiary
andnonce
to 0mixhash
to 0, set uncles to empty list (should happen automatically if we populate from parent)Signing should probably happen in
CliqueSealEngine::generateSeal()
Make sure we sign only once in
SIGNER_LIMIT
blocks and only if we're in the signer's list (Probably we'll need to add new virtual methods toSealEngineFace
which will decide whether we should sign current block. For Ethash it's simpler because any block can be mined)Time of signing strategy (add random delay for out-of-turn sign)
Make sure
--extra-data
CLI parameter sets only first 32 bytes(optional) Voting
(optional) Support Goerli in Warp sync