-
Notifications
You must be signed in to change notification settings - Fork 778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clique: get signers by block num #2610
Conversation
751f5c0
to
1ab62c0
Compare
Codecov Report
Additional details and impacted files
Flags with carried forward coverage won't be shown. Click here to find out more. |
d850394
to
2641d88
Compare
Ok, I think I fixed the CI for blockchain. The usage of |
a684d5a
to
604f264
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This all looks great. Have a few clarifying questions but would love to merge this once final.
@@ -233,17 +244,24 @@ export class CliqueConsensus implements Consensus { | |||
} | |||
} | |||
|
|||
this._cliqueLatestSignerStates.sort((a, b) => (a[0] > b[0] ? 1 : -1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it likely that the clique signer states will get out of order and require us to potentially sort them twice in a single call to this update method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this just to be sure, but looking at the code, it trims the cliqueLatestSignerStates
after the first sort (so they are already ordered by block number) and thus removes latest signer states if they are below a certain threshold number (so the order should still be fine). I think we can remove.
i++ | ||
} | ||
// eslint-disable-next-line no-empty | ||
} catch (e) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the error that could occur here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should have commented because now I don't remember. I think it was something when I tried to sync Goerli, and then for some reason signerState[0]
was undefined, which throws the method. (If you want I can re-investigate)
const signers = this._cliqueLatestSignerStates | ||
if (signers.length === 0) { | ||
return [] | ||
} | ||
return [...signers[signers.length - 1][1]] | ||
for (let i = signers.length - 1; i >= 0; i--) { | ||
if (signers[i][0] < blockNum) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just clarify my understanding here, we're just returning the most recent signer state, (which might be from a block older than the current chain head if no signer state change occurs on the current block, right?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you got the idea here, but just for clarity:
- This solves the reorg problem, because before it always returned whatever active signers there were at current chain head. (On client this could result in execution vs block import being out of sync which thus require different active signers if you verify them)
- Note that
_cliqueLatestSignerStates
is sorted - We return the first candidate which has active signers < requested block num (so the state we want)
- Note that signers always has 1 member
@@ -455,6 +478,7 @@ export class CliqueConsensus implements Consensus { | |||
this._cliqueLatestSignerStates = this._cliqueLatestSignerStates.filter( | |||
(s) => s[0] <= blockNumber | |||
) | |||
this._cliqueLatestSignerStates.sort((a, b) => (a[0] > b[0] ? 1 : -1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How often to signer states get out of order? Seems like we do a lot of sorting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On each reorg they will be out-of-order and will mess up the latest states (this fixes reorgs in case voting changes). However since this is in the delete method and the assumption is that the signer state is always sorted, this should only pop items from the end of the array, so might indeed remove.
for (let i = 1; i < blocks.length; i++) { | ||
await blockchain.putBlock(blocks[i]) | ||
} | ||
st.end() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add a simple check to validate the signer states at some of the historical blocks here or is that sufficiently covered by the reorg tests at the bottom?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow, the diff got completely messed up. Now it looks like I wrote a gigantic amount of new tests which is not the case. I must have accidentally moved some stuff around. I only added reorg tests and this specific test does not reorg. Note that if you putBlock
and the state is wrong (either on reorg or not), then putBlock
will throw. I will try to fix the diff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nvm, I had to move some logic into methods to create the addNextBlockReorg
function. This specific test, I think I added this to test things internally. I think for completeness, lets keep it, but I think if we run this on the old version (without reorg logic) this test still passes anyways.
'address C added to signers' | ||
) | ||
st.ok( | ||
equalsBytes((await blockchain.getCanonicalHeadBlock()).hash(), headBlockUnforked.hash()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Complete nit but these equalsBytes
calls can be replaced with st.deepEquals...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh really? I did not know that :)
I will pick up these points and cleanup. I will also not fix this epoch reorg thing, but I will comment out the test and leave it there for educational purposes, and explicitly comment that we do not support certain reorg situations, like epoch reorg. (And possibly others but I will not research). In case you linearly import a PoA chain now, this should be fine. However, at tip of chain if there are reorgs, failure might occur. (Note that this is still somewhat fixable: you can rewind the chain back to latest epoch change block, read clique state from there, and then just replay the chain, which should now fix the issue on the reorg). So it might be unstable at tip of PoA chains. |
Great, the couple of edits look good. Are we ready to merge? |
Yup. |
(sorry did not update the PR status, just did 😄 ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lgtm
* blockchain/clique: add support for clique signers by block num * blockchain: clique sort clique latest signer states * client: fix build * blockchain/clique: fix tests and add test * blockchain/clique: fix clique bugs * blockchain/tests/clique: fix tests * blockchain: clique: add reorg test * blockchain/clique: add reorg over epoch test (fails) [no ci] * replace equalsBytes with deepEquals * blockchain/clique: address comments * blockchain/clique: ensure tests are ran --------- Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>
I added a test, which currently fails, it messes up the
_cliqueLatestSignerStates
somehow (after adding block 2, it adds a new signer to block 2, but also edits the signers of block 0?)Note: we most likely have this problem with the "latest block signers", and the votes as well.
This PR currently allows to sync Goerli > 5476.
There is another problem with the current implementation: in case of a reorg, the active signers are all still remembered. However, if due to this reorg the signers at block X (where block X is now remembered of the previous change) change, this messes up the signer state internally. It should on reorg actually delete these signers above the block (need to investigate this claim though).
Note to self: local branch
clique-by-blocknum-new