aleth
aleth copied to clipboard
Clique PoA implementation plan
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
SealEngineFacefor this). More exactly it does the following:- For regular block
- Update vote list (Only the latest proposal per target beneficiary is kept from a single signer)
- if proposal reaches
SIGNER_LIMIT, modify signers list, and discard all votes for this beneficiary
- For the epoch transition block
- Discard all pending votes
- For regular block
-
When required voting state is not found in the cache, it is reconstructed from the latest epoch transition block (checkpoint block)
- Checkpoint state (signer list) is deserialized from
extra-dataof the checkpoint block header - The following blocks' state is replayed (by block processing procedure above) until we reach required block
- Checkpoint state (signer list) is deserialized from
-
Handling 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.
- This requires modifications in database format, adding some kind of new metadata for the block.
- Saving them for each block might be wasteful, so a better strategy should be devised.
- (go-ethereum apparently is saving signers/votes each time it flushes the state trie to disk)
Block validation
These checks should go into verify method of Clique's implementation of SealEngineFace.
mixhashis 0- uncle list is empty
timestamp>=parent.timestamp+ 15s- check that signer is in the list of authorized signers
- check that signer's last signed block is more than
SIGNER_LIMITblocks ago difficultyis either 1 or 2, depending on signer index- For epoch transition block
beneficiaryis 0nonceis 0- check the list of signers in the block header is equivalent to our calculated list
- For regular blocks
nonceis either 0 or 0xff..fextra-datasize is 32 + 65 bytes
Other important things
COINBASEopcode should return the signer and not the contents ofbeneficiary. Probably this may be done by askingCliqueSealEnginefor the signer of the block before enacting it and putting it intoEnvInfothat will be used by EVM.
CLI
- Config for Goerli
- Config for Rinkeby
- Command line arguments
--goerliand--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 otherwise- Epoch transition
- set
beneficiaryandnonceto 0 - include sorted list of signers
- set
- set
mixhashto 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_LIMITblocks and only if we're in the signer's list (Probably we'll need to add new virtual methods toSealEngineFacewhich 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-dataCLI parameter sets only first 32 bytes
(optional) Voting
- CLI argument for the list of votes or RPC method to enable vote (geth has RPC)
- add one of the votes from the list of active votes into the block when signing
(optional) Support Goerli in Warp sync
- Initialize list of signers from the snapshot block
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
Keeping track of signers
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
Block validation
Validating the header https://github.com/paritytech/parity-ethereum/blob/78d0a8696ffec4eb6b0e2214b476fed388e14bed/ethcore/src/engines/clique/mod.rs#L536
Sealing
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
Other
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