{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Ouroboros.Consensus.Committee.Class
(
CryptoSupportsVotingCommittee (..)
, UniqueVotesWithSameTarget
, getElectionIdFromVotes
, getVoteCandidateFromVotes
, getRawVotes
, UniqueVotesWithSameTargetError (..)
, ensureUniqueVotesWithSameTarget
, unsafeUniqueVotesWithSameTarget
, checkUniqueVotesWithSameTarget
) where
import Control.Exception (assert)
import Data.Containers.NonEmpty (HasNonEmpty (..))
import Data.Either (isRight)
import Data.Kind (Type)
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NonEmpty
import Data.Proxy (Proxy (..))
import Ouroboros.Consensus.Committee.Crypto
( CryptoSupportsVoteSigning
, ElectionId
, PrivateKey
, VoteCandidate
)
import Ouroboros.Consensus.Committee.Types (PoolId, VoteWeight)
class
CryptoSupportsVoteSigning crypto =>
CryptoSupportsVotingCommittee crypto committee
where
data VotingCommittee crypto committee :: Type
data VotingCommitteeInput crypto committee :: Type
data VotingCommitteeError crypto committee :: Type
data EligibilityWitness crypto committee :: Type
data Vote crypto committee :: Type
data Cert crypto committee :: Type
mkVotingCommittee ::
VotingCommitteeInput crypto committee ->
Either
(VotingCommitteeError crypto committee)
(VotingCommittee crypto committee)
checkShouldVote ::
VotingCommittee crypto committee ->
PoolId ->
PrivateKey crypto ->
ElectionId crypto ->
Either
(VotingCommitteeError crypto committee)
(Maybe (EligibilityWitness crypto committee))
forgeVote ::
EligibilityWitness crypto committee ->
PrivateKey crypto ->
ElectionId crypto ->
VoteCandidate crypto ->
Vote crypto committee
verifyVote ::
VotingCommittee crypto committee ->
Vote crypto committee ->
Either
(VotingCommitteeError crypto committee)
(EligibilityWitness crypto committee)
eligiblePartyVoteWeight ::
VotingCommittee crypto committee ->
EligibilityWitness crypto committee ->
VoteWeight
forgeCert ::
UniqueVotesWithSameTarget crypto committee ->
Either
(VotingCommitteeError crypto committee)
(Cert crypto committee)
verifyCert ::
VotingCommittee crypto committee ->
Cert crypto committee ->
Either
(VotingCommitteeError crypto committee)
(NE [EligibilityWitness crypto committee])
data UniqueVotesWithSameTarget crypto committee
= UniqueVotesWithSameTarget
(ElectionId crypto)
(VoteCandidate crypto)
(NE [Vote crypto committee])
getElectionIdFromVotes ::
UniqueVotesWithSameTarget crypto committee ->
ElectionId crypto
getElectionIdFromVotes :: forall crypto committee.
UniqueVotesWithSameTarget crypto committee -> ElectionId crypto
getElectionIdFromVotes (UniqueVotesWithSameTarget ElectionId crypto
electionId VoteCandidate crypto
_ NE [Vote crypto committee]
_) =
ElectionId crypto
electionId
getVoteCandidateFromVotes ::
UniqueVotesWithSameTarget crypto committee ->
VoteCandidate crypto
getVoteCandidateFromVotes :: forall crypto committee.
UniqueVotesWithSameTarget crypto committee -> VoteCandidate crypto
getVoteCandidateFromVotes (UniqueVotesWithSameTarget ElectionId crypto
_ VoteCandidate crypto
candidate NE [Vote crypto committee]
_) =
VoteCandidate crypto
candidate
getRawVotes ::
UniqueVotesWithSameTarget crypto committee ->
NE [Vote crypto committee]
getRawVotes :: forall crypto committee.
UniqueVotesWithSameTarget crypto committee
-> NE [Vote crypto committee]
getRawVotes (UniqueVotesWithSameTarget ElectionId crypto
_ VoteCandidate crypto
_ NE [Vote crypto committee]
votes) =
NE [Vote crypto committee]
votes
data UniqueVotesWithSameTargetError vote
= DuplicateVotes
(NE [vote])
| TargetMismatch
vote
(NE [vote])
ensureUniqueVotesWithSameTarget ::
forall crypto committee.
( Eq (ElectionId crypto)
, Eq (VoteCandidate crypto)
) =>
(Vote crypto committee -> (ElectionId crypto, VoteCandidate crypto)) ->
(Vote crypto committee -> Vote crypto committee -> Ordering) ->
NE [Vote crypto committee] ->
Either
(UniqueVotesWithSameTargetError (Vote crypto committee))
(UniqueVotesWithSameTarget crypto committee)
ensureUniqueVotesWithSameTarget :: forall crypto committee.
(Eq (ElectionId crypto), Eq (VoteCandidate crypto)) =>
(Vote crypto committee
-> (ElectionId crypto, VoteCandidate crypto))
-> (Vote crypto committee -> Vote crypto committee -> Ordering)
-> NE [Vote crypto committee]
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee))
(UniqueVotesWithSameTarget crypto committee)
ensureUniqueVotesWithSameTarget Vote crypto committee -> (ElectionId crypto, VoteCandidate crypto)
getTarget Vote crypto committee -> Vote crypto committee -> Ordering
cmpVotes NE [Vote crypto committee]
votes =
(() -> UniqueVotesWithSameTarget crypto committee)
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee)) ()
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee))
(UniqueVotesWithSameTarget crypto committee)
forall a b.
(a -> b)
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee)) a
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee)) b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
( UniqueVotesWithSameTarget crypto committee
-> () -> UniqueVotesWithSameTarget crypto committee
forall a b. a -> b -> a
const
( ElectionId crypto
-> VoteCandidate crypto
-> NE [Vote crypto committee]
-> UniqueVotesWithSameTarget crypto committee
forall crypto committee.
ElectionId crypto
-> VoteCandidate crypto
-> NE [Vote crypto committee]
-> UniqueVotesWithSameTarget crypto committee
UniqueVotesWithSameTarget
ElectionId crypto
electionId
VoteCandidate crypto
candidate
(Vote crypto committee
firstVote Vote crypto committee
-> [Vote crypto committee] -> NonEmpty (Vote crypto committee)
forall a. a -> [a] -> NonEmpty a
:| [Vote crypto committee]
nextVotes)
)
)
(Either (UniqueVotesWithSameTargetError (Vote crypto committee)) ()
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee))
(UniqueVotesWithSameTarget crypto committee))
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee)) ()
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee))
(UniqueVotesWithSameTarget crypto committee)
forall a b. (a -> b) -> a -> b
$ Proxy crypto
-> (Vote crypto committee
-> (ElectionId crypto, VoteCandidate crypto))
-> (Vote crypto committee -> Vote crypto committee -> Ordering)
-> NE [Vote crypto committee]
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee)) ()
forall crypto vote.
(Eq (ElectionId crypto), Eq (VoteCandidate crypto)) =>
Proxy crypto
-> (vote -> (ElectionId crypto, VoteCandidate crypto))
-> (vote -> vote -> Ordering)
-> NE [vote]
-> Either (UniqueVotesWithSameTargetError vote) ()
checkUniqueVotesWithSameTarget
(forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @crypto)
Vote crypto committee -> (ElectionId crypto, VoteCandidate crypto)
getTarget
Vote crypto committee -> Vote crypto committee -> Ordering
cmpVotes
NE [Vote crypto committee]
votes
where
Vote crypto committee
firstVote :| [Vote crypto committee]
nextVotes = NE [Vote crypto committee]
votes
(ElectionId crypto
electionId, VoteCandidate crypto
candidate) = Vote crypto committee -> (ElectionId crypto, VoteCandidate crypto)
getTarget Vote crypto committee
firstVote
unsafeUniqueVotesWithSameTarget ::
forall crypto committee.
( Eq (ElectionId crypto)
, Eq (VoteCandidate crypto)
) =>
(Vote crypto committee -> (ElectionId crypto, VoteCandidate crypto)) ->
(Vote crypto committee -> Vote crypto committee -> Ordering) ->
NE [Vote crypto committee] ->
UniqueVotesWithSameTarget crypto committee
unsafeUniqueVotesWithSameTarget :: forall crypto committee.
(Eq (ElectionId crypto), Eq (VoteCandidate crypto)) =>
(Vote crypto committee
-> (ElectionId crypto, VoteCandidate crypto))
-> (Vote crypto committee -> Vote crypto committee -> Ordering)
-> NE [Vote crypto committee]
-> UniqueVotesWithSameTarget crypto committee
unsafeUniqueVotesWithSameTarget Vote crypto committee -> (ElectionId crypto, VoteCandidate crypto)
getTarget Vote crypto committee -> Vote crypto committee -> Ordering
cmpVotes NE [Vote crypto committee]
votes =
Bool
-> UniqueVotesWithSameTarget crypto committee
-> UniqueVotesWithSameTarget crypto committee
forall a. (?callStack::CallStack) => Bool -> a -> a
assert
( Either (UniqueVotesWithSameTargetError (Vote crypto committee)) ()
-> Bool
forall a b. Either a b -> Bool
isRight
( Proxy crypto
-> (Vote crypto committee
-> (ElectionId crypto, VoteCandidate crypto))
-> (Vote crypto committee -> Vote crypto committee -> Ordering)
-> NE [Vote crypto committee]
-> Either
(UniqueVotesWithSameTargetError (Vote crypto committee)) ()
forall crypto vote.
(Eq (ElectionId crypto), Eq (VoteCandidate crypto)) =>
Proxy crypto
-> (vote -> (ElectionId crypto, VoteCandidate crypto))
-> (vote -> vote -> Ordering)
-> NE [vote]
-> Either (UniqueVotesWithSameTargetError vote) ()
checkUniqueVotesWithSameTarget
(forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @crypto)
Vote crypto committee -> (ElectionId crypto, VoteCandidate crypto)
getTarget
Vote crypto committee -> Vote crypto committee -> Ordering
cmpVotes
NE [Vote crypto committee]
votes
)
)
(UniqueVotesWithSameTarget crypto committee
-> UniqueVotesWithSameTarget crypto committee)
-> UniqueVotesWithSameTarget crypto committee
-> UniqueVotesWithSameTarget crypto committee
forall a b. (a -> b) -> a -> b
$ ElectionId crypto
-> VoteCandidate crypto
-> NE [Vote crypto committee]
-> UniqueVotesWithSameTarget crypto committee
forall crypto committee.
ElectionId crypto
-> VoteCandidate crypto
-> NE [Vote crypto committee]
-> UniqueVotesWithSameTarget crypto committee
UniqueVotesWithSameTarget
ElectionId crypto
electionId
VoteCandidate crypto
candidate
(Vote crypto committee
firstVote Vote crypto committee
-> [Vote crypto committee] -> NonEmpty (Vote crypto committee)
forall a. a -> [a] -> NonEmpty a
:| [Vote crypto committee]
nextVotes)
where
Vote crypto committee
firstVote :| [Vote crypto committee]
nextVotes = NE [Vote crypto committee]
votes
(ElectionId crypto
electionId, VoteCandidate crypto
candidate) = Vote crypto committee -> (ElectionId crypto, VoteCandidate crypto)
getTarget Vote crypto committee
firstVote
checkUniqueVotesWithSameTarget ::
( Eq (ElectionId crypto)
, Eq (VoteCandidate crypto)
) =>
Proxy crypto ->
(vote -> (ElectionId crypto, VoteCandidate crypto)) ->
(vote -> vote -> Ordering) ->
NE [vote] ->
Either (UniqueVotesWithSameTargetError vote) ()
checkUniqueVotesWithSameTarget :: forall crypto vote.
(Eq (ElectionId crypto), Eq (VoteCandidate crypto)) =>
Proxy crypto
-> (vote -> (ElectionId crypto, VoteCandidate crypto))
-> (vote -> vote -> Ordering)
-> NE [vote]
-> Either (UniqueVotesWithSameTargetError vote) ()
checkUniqueVotesWithSameTarget Proxy crypto
_ vote -> (ElectionId crypto, VoteCandidate crypto)
getTarget vote -> vote -> Ordering
cmpVotes NE [vote]
votes =
vote -> [vote] -> Either (UniqueVotesWithSameTargetError vote) ()
go vote
firstVote [vote]
nextVotes
where
vote
firstVote :| [vote]
nextVotes =
(vote -> vote -> Ordering) -> NonEmpty vote -> NonEmpty vote
forall a. (a -> a -> Ordering) -> NonEmpty a -> NonEmpty a
NonEmpty.sortBy vote -> vote -> Ordering
cmpVotes NonEmpty vote
NE [vote]
votes
go :: vote -> [vote] -> Either (UniqueVotesWithSameTargetError vote) ()
go vote
_ [] =
() -> Either (UniqueVotesWithSameTargetError vote) ()
forall a b. b -> Either a b
Right ()
go vote
v (vote
v' : [vote]
vs)
| vote -> vote -> Bool
isDuplicateOf vote
v vote
v' = do
let duplicateVotes :: NonEmpty vote
duplicateVotes = vote
v vote -> [vote] -> NonEmpty vote
forall a. a -> [a] -> NonEmpty a
:| (vote
v' vote -> [vote] -> [vote]
forall a. a -> [a] -> [a]
: (vote -> Bool) -> [vote] -> [vote]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (vote -> vote -> Bool
isDuplicateOf vote
v) [vote]
vs)
UniqueVotesWithSameTargetError vote
-> Either (UniqueVotesWithSameTargetError vote) ()
forall a b. a -> Either a b
Left (NE [vote] -> UniqueVotesWithSameTargetError vote
forall vote. NE [vote] -> UniqueVotesWithSameTargetError vote
DuplicateVotes NonEmpty vote
NE [vote]
duplicateVotes)
| vote -> Bool
doesNotMatchFirstVoteTarget vote
v' = do
let mismatchingVotes :: NonEmpty vote
mismatchingVotes = vote
v' vote -> [vote] -> NonEmpty vote
forall a. a -> [a] -> NonEmpty a
:| (vote -> Bool) -> [vote] -> [vote]
forall a. (a -> Bool) -> [a] -> [a]
filter vote -> Bool
doesNotMatchFirstVoteTarget [vote]
vs
UniqueVotesWithSameTargetError vote
-> Either (UniqueVotesWithSameTargetError vote) ()
forall a b. a -> Either a b
Left (vote -> NE [vote] -> UniqueVotesWithSameTargetError vote
forall vote.
vote -> NE [vote] -> UniqueVotesWithSameTargetError vote
TargetMismatch vote
firstVote NonEmpty vote
NE [vote]
mismatchingVotes)
| Bool
otherwise =
vote -> [vote] -> Either (UniqueVotesWithSameTargetError vote) ()
go vote
v' [vote]
vs
doesNotMatchFirstVoteTarget :: vote -> Bool
doesNotMatchFirstVoteTarget vote
v =
vote -> (ElectionId crypto, VoteCandidate crypto)
getTarget vote
v (ElectionId crypto, VoteCandidate crypto)
-> (ElectionId crypto, VoteCandidate crypto) -> Bool
forall a. Eq a => a -> a -> Bool
/= vote -> (ElectionId crypto, VoteCandidate crypto)
getTarget vote
firstVote
isDuplicateOf :: vote -> vote -> Bool
isDuplicateOf vote
v vote
v' =
vote -> vote -> Ordering
cmpVotes vote
v vote
v' Ordering -> Ordering -> Bool
forall a. Eq a => a -> a -> Bool
== Ordering
EQ