-
Notifications
You must be signed in to change notification settings - Fork 86
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
Generate EBBs more realistically in the RealPBFT consensus test #1353
Conversation
9fe3499
to
0d2c951
Compare
I've pushed up three new commits.
Without this, late joining nodes could forge multiple EBBs on top of Origin. They're 10k slots apart, which means the ">= k blocks in 2k slots" was violated.
I noticed that I was only seeing EBBs for Epoch 0. So I added this test to confirm. This test fails without the next commit.
I'm unfamiliar with the consequences here, but this override in
I haven't formulated it crisply yet, but I think there's an invariant along the lines of "we only forge EBBs in the correct slots and every node forges EBBs", so the presence/absence of EBBs will never be a deciding factor in the tests.
|
fbc67ec
to
35dfbd4
Compare
ouroboros-consensus/src/Ouroboros/Consensus/Protocol/ModChainSel.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/src/Ouroboros/Consensus/Protocol/ModChainSel.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/src/Ouroboros/Consensus/Protocol/ModChainSel.hs
Outdated
Show resolved
Hide resolved
35dfbd4
to
11b5b3e
Compare
Edsko and I came up with a better plan: Instead of computing the intersection and then the lengths of the suffices, we can instead simply check whether one of the headers is an EBB or not. We can use -- Look at the tips
case (AF.head ours, AF.head cand) of
-- Both empty
(Left _, Left _) -> EQ
-- Our chain is empty, candidate is not
(Left _, Right _) -> LT
-- Our chain is not empty, the candidate is
(Right _, Left _) -> GT
-- Our chain and candidate not empty
(Right ourHdr, Right candHdr) ->
-- Prefer the highest block number, as it is a proxy for chain length
case blockNo ourHdr `compare` blockNo candHdr of
LT -> LT
GT -> GT
-- If the block numbers are the same, check if one of them is an EBB.
-- An EBB has the same block number as the block before it, so the
-- chain ending with an EBB is actually longer than the one ending with a
-- regular block. Note that `headerPBftFields` returns `Nothing` for
-- an EBB.
EQ ->
case (headerPBftFields cfg ourHdr, headerPBftFields cfg candHdr) of
(Just _, Just _) -> EQ
(Just _, Nothing) -> LT
(Nothing, Nothing) -> EQ
(Nothing, Just _) -> GT This only needs to be done for This also means that we don't need the extra invariants and don't have to change BlockFetch. Furthermore, this is compatible with Edsko's plan to look only at the headers at the tip (#1227) instead of the fragments. |
2e4d700
to
124ab0d
Compare
I've force-pushed some new work. Summary:
I'm not excited about the
|
124ab0d
to
9195a44
Compare
ouroboros-consensus/src/Ouroboros/Consensus/Protocol/Abstract.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/test-consensus/Test/Consensus/Ledger/Byron.hs
Outdated
Show resolved
Hide resolved
ouroboros-consensus/test-consensus/Test/Consensus/Ledger/Byron.hs
Outdated
Show resolved
Hide resolved
ea2bfdb
to
4cb2df4
Compare
The push |
Commit |
9430dbc
to
11a745c
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.
LGTM
Let's get this merged!
ouroboros-consensus/src/Ouroboros/Consensus/Protocol/PBFT/ChainState.hs
Outdated
Show resolved
Hide resolved
TraceForgeAboutToLead s -> do | ||
atomically $ do | ||
lim <- readTVar nextEbbSlotVar | ||
check $ s < lim | ||
o -> traceWith (nodeEventsForges nodeInfoEvents) o |
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 if we want to make EBBs optional? Or even stop producing after some epoch?
It doesn't have to be part of this PR, but it would nice to be able to do that in the future.
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.
If runNodeNetwork
is invoked with NothingForgeEBB
then there will be no EBBs, but this nextEbbSlotVar
will still increment -- there's a comment saying as much at the binding site of nextEbbSlotVar
. It's slightly off-putting, but seemed preferable to having multiple Maybe
s, etc.
No support for stopping EBBs at a certain slot yet.
53b6dee
to
3b73e1c
Compare
3b73e1c
to
41372f2
Compare
Two rebases just now: first |
Moreover, block production and EBB production are synchronized so that a node will forge the EBB in a slot *before* it leads that slot.
41372f2
to
36c8ab2
Compare
Another rebase: I exploded the
|
bors r+ |
1353: Generate EBBs more realistically in the RealPBFT consensus test r=nfrisby a=nfrisby Fixes #966. Fixes #1352. I'm opening this as a Draft PR because I would like your feedback on the basic schemes before I polish it up. Basic schemes: * Issue #966 - Use a new `Tracer` in `Node.Tracers` and a new `TVar` in `Test.Dynamic.Tracer` to ensure that each node has added all EBBs to its `ChainDB` _before_ forging a block when leading. Blocking the traced thread seems like an abuse of `Tracer`s, but I haven't come up with a less invasive option. We also had to update `preferCandidate` to care about length-after-intersection in addition to block number, since it was just ignoring the EBBs we were adding: except the epoch 0 EBB, they'd only be selected once their successor arrived -- but that meant they never got a successor! That in turn required some gymnastics in BlockFetch to ensure we only compare chain fragments that intersect. * Nodes always forge an actual block when leading; they never waste their turn forging an EBB. * `suchThat` to preclude node join plans that cause unpredictable final chains. The ambiguity is currently limited to at most the first `numCoreNodes` blocks, but that is enough to cause trouble because it could affect the PBft signature threshold, which could subvert the "at least `k` blocks in `2k` slots" invariant established by the RealPBFT `NodeJoinPlan` generator. (I tried to integrate this check into the recursive generator itself, but the inherent non-monotonicity spoils the inductive hypothesis that "at the very least the plan is OK if all remaining nodes ASAP".) * Issue #1352 - when the CS client rollsback a candidate fragment and the oldest dropped header has the same slot as the target, then the target is an EBB with an accompanying block in the same slot and so the primary chain state should essentially be rolled back to the preceding slot instead of the EBB's slot. Otherwise we'd retain the block sharing the slot, which the intersection point precedes. We address this by storing the header hash of an EBB in the chain state and taking a `Point` as the target of rewinds. We retain the EBB when rolling back to its slot if its hash matches that of the given `Point`. As an unfortunate compromise, we store the `HeaderHash` as a `ByteString`, since a sufficient argument for `HeaderHash` is not made available by the arguments to the `ChainState` family. Co-authored-by: Thomas Winant <[email protected]> Co-authored-by: Nicolas Frisby <[email protected]>
🎉🎉🎉 |
Fixes #966. Fixes #1352.
I'm opening this as a Draft PR because I would like your feedback on the basic schemes before I polish it up. Basic schemes:
Issue Include mock EBBs #966 - Use a new
Tracer
inNode.Tracers
and a newTVar
inTest.Dynamic.Tracer
to ensure that each node has added all EBBs to itsChainDB
before forging a block when leading. Blocking the traced thread seems like an abuse ofTracer
s, but I haven't come up with a less invasive option. We also had to updatepreferCandidate
to care about length-after-intersection in addition to block number, since it was just ignoring the EBBs we were adding: except the epoch 0 EBB, they'd only be selected once their successor arrived -- but that meant they never got a successor! That in turn required some gymnastics in BlockFetch to ensure we only compare chain fragments that intersect.Nodes always forge an actual block when leading; they never waste their turn forging an EBB.
suchThat
to preclude node join plans that cause unpredictable final chains. The ambiguity is currently limited to at most the firstnumCoreNodes
blocks, but that is enough to cause trouble because it could affect the PBft signature threshold, which could subvert the "at leastk
blocks in2k
slots" invariant established by the RealPBFTNodeJoinPlan
generator. (I tried to integrate this check into the recursive generator itself, but the inherent non-monotonicity spoils the inductive hypothesis that "at the very least the plan is OK if all remaining nodes ASAP".)Issue ChainSync client cannot rollback to an EBB that shares a slot with an actual block #1352 - when the CS client rollsback a candidate fragment and the oldest dropped header has the same slot as the target, then the target is an EBB with an accompanying block in the same slot and so the primary chain state should essentially be rolled back to the preceding slot instead of the EBB's slot. Otherwise we'd retain the block sharing the slot, which the intersection point precedes. We address this by storing the header hash of an EBB in the chain state and taking a
Point
as the target of rewinds. We retain the EBB when rolling back to its slot if its hash matches that of the givenPoint
. As an unfortunate compromise, we store theHeaderHash
as aByteString
, since a sufficient argument forHeaderHash
is not made available by the arguments to theChainState
family.