-
Notifications
You must be signed in to change notification settings - Fork 36
Add Vote and VoteDB for Peras #1768
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
base: peras-staging
Are you sure you want to change the base?
Conversation
|
There's an implementation detail that we should discuss with the researchers: whether or not we need to store votes targeting a different block than the one a node is currently championing.
If an honest node is voting for the wrong block because of adversarial actions, is it realistic for it to expect receiving votes from the honest majority? Or, conversely, to receive a certificate when a quorum is reached elsewhere. |
agustinmista
left a comment
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.
Looking good so far! I have some thoughts around what we discussed the last time. Maybe they could lead to some simplifications.
Also, would you mind marking the new TODOs somehow? Like TODO(peras) so that we can easily grep for them.
| PerasVoteStakeDistr -> | ||
| Either (PerasValidationErr blk) (ValidatedPerasVote blk) | ||
|
|
||
| const_PERAS_QUORUM_THRESHOLD :: PerasVoteStake |
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.
Even if it's not a tunable parameter, I think it would be good to move this to Ouroboros.Consensus.Peras.Params
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.
But I'm not sure that the part of the code dealing with the PerasVoteDB (probably the ChainDB, as PerasCertDB is managed as a part of the ChainDB already) will have access to those params?
If we follow the same structure as for the PerasCertDB, we will have a PerasCertDBArgs struct as a parameter of openPerasVoteDB that could be used to host this param.
| data PerasVoteAggregateStatus blk | ||
| = PerasVoteAggregateQuorumNotReached {pvasVoteAggregate :: !(PerasVoteAggregate blk)} | ||
| | PerasVoteAggregateQuorumReachedAlready | ||
| {pvasVoteAggregate :: !(PerasVoteAggregate blk), pvasCert :: ValidatedPerasCert blk} | ||
| deriving stock (Generic, Eq, Ord, Show) | ||
| deriving anyclass NoThunks |
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 like this way better!
What I am still a bit concerned about, though, is that complete and incomplete voting aggregates are still represented by the same type, and that could make it easier to introduce bugs in the future.
Maybe we can define a GADT indexed by the voting state to make these two cases different at the type level, while still allowing for a single type to be carried around in most places:
type data PerasVotingStatus = NoQuorum | Quorum
data PerasVotingTally blk status where
NoQuorum ::
!(PerasVoteAggregate) ->
PerasVotingTally NoQuorum
Quorum ::
!(PerasVoteAggregate blk) ->
!(ValidatedPerasCert blk) ->
PerasVotingTally QuorumThis way, we could enforce the voting code to only accept the expected state transitions:
- NoQuorum -> NoQuorum
- NoQuorum -> Quorum
- Quorum -> Quorum
(note that Quorum -> NoQuorum shouldn't be possible)
As well as allowing future client code using these aggregates to specify when it expects voting to have reached a quorum, as opposed to handling it dynamically via a Maybe and/or some error "impossible case". E.g., instead of pvasMaybeCert, we could have:
perasVotingCert :: PerasVotingTally blk Quorum -> ValidatedPerasCert blk
perasVotingCert (Quorum _ cert) = certWhich can only be used to extract the cert from the aggregate bundle after voting has reached a quorum.
Going one step further, we could also encode the number of votes that we have seen after reaching a quorum in the type-level Quorum constructor:
type data PerasVotingStatus = NoQuorum | Quorum NatThis would allow us to enrich the state transitions to:
- NoQuorum -> NoQuorum
- NoQuorum -> Quorum Z
- Quorum n -> Quorum (S n)
And we could use Quorum 0 as the witness for the state transition that produces a certificate. This way, I think we could able to get rid of the PerasVoteResult type* or, alternatively, turn it into a thin wrapper over PerasVotingTally to hide the existential:
data PerasVotingTally blk status where
NoQuorum ::
!(PerasVoteAggregate) ->
PerasVotingTally NoQuorum
Quorum ::
!(SNat n) ->
!(PerasVoteAggregate blk) ->
!(ValidatedPerasCert blk) ->
PerasVotingTally (Quorum n)
-- constructor not exported
newtype PerasVoteResult blk = PerasVoteResult (Some (PerasVoteTally blk))
-- instead we could expose only the things the client need, e.g.:
-- to extract the certificate if we have _just_ reached a quorum, and never again
perasVoteResultEmittedCert :: PerasVoteResult blk -> Maybe (ValidatedPerasCert blk)
perasVoteResultEmittedCert (PerasVoteResult (Some tally)) =
case tally of
Quorum Z _ cert -> Just cert
_ -> Nothing[*] depending on whether the future client code might also be interested in reacting to the event of adding a certificate that was already in the VoteDB (I think it wouldn't, as that's sounds as something purely relevant to tracing, which is already being handled by the VoteDB implementation)
| , pvaTotalStake = initialStake | ||
| } | ||
| vote = | ||
| if not (getPerasVoteRound vote == roundNo && getPerasVoteVotedBlock vote == point) |
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 would apply DeMorgan's law to this condition:
if getPerasVoteRound vote /= roundNo || getPerasVoteVotedBlock vote /= point|
|
||
| -- | Add a vote to an existing aggregate if it isn't already present, and update | ||
| -- the stake accordingly. | ||
| -- PRECONDITION: the vote's target must match the aggregate's target. |
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 function is only being used by updatePerasVoteAggregateStatus. Does the latter also inherit this precondition? If not, I would then embed this function as a local binding to updatePerasVoteAggregateStatus and get rid of the error. Otherwise, updatePerasVoteAggregateStatus should also mention the precondition.
b57fa4b to
c93a0f2
Compare
Description
Please include a meaningful description of the PR and link the relevant issues
this PR might resolve.
Also note that: