content: {-# LANGUAGE RecordWildCards #-}
  
  module Pos.Chain.Ssc.Commitment
         ( Commitment (..)
         , CommitmentSignature
         , SignedCommitment
         , getCommShares
         ) where
  
  import           Universum
  
  import           Control.Lens (each, traverseOf)
  import qualified Data.HashMap.Strict as HM
  import           Data.SafeCopy (base, deriveSafeCopySimple)
  
  import           Pos.Binary.Class (AsBinary, Bi (..), encodeListLen,
                       enforceSize, fromBinary, serialize')
  import           Pos.Core.Binary ()
  import           Pos.Core.Slotting (EpochIndex)
  import           Pos.Crypto (EncShare, PublicKey, SecretProof, Signature,
                       VssPublicKey)
  import           Pos.Util.Util (cborError)
  
  -- | Commitment is a message generated during the first stage of SSC.
  -- It contains encrypted shares and proof of secret.
  --
  -- There can be more than one share generated for a single participant.
  data Commitment = Commitment
      { commProof  :: !SecretProof
      , commShares :: !(HashMap (AsBinary VssPublicKey)
                                (NonEmpty (AsBinary EncShare)))
      } deriving (Show, Eq, Generic)
  
  instance NFData Commitment
  instance Hashable Commitment
  
  instance Ord Commitment where
      compare = comparing (serialize' . commProof) <>
                comparing (sort . HM.toList . commShares)
  
  instance Bi Commitment where
      encode Commitment{..} = encodeListLen 2 <> encode commShares
                                              <> encode commProof
      decode = do
          enforceSize "Commitment" 2
          commShares <- decode
          when (null commShares) $ cborError "decode@Commitment: no shares"
          commProof <- decode
          return $ Commitment commProof commShares
  
  
  -- | Signature which ensures that commitment was generated by node
  -- with given public key for given epoch.
  type CommitmentSignature = Signature (EpochIndex, Commitment)
  
  type SignedCommitment = (PublicKey, Commitment, CommitmentSignature)
  
  -- | Get commitment shares.
  getCommShares :: Commitment -> Maybe [(VssPublicKey, NonEmpty EncShare)]
  getCommShares =
      traverseOf (each . _1) (rightToMaybe . fromBinary) <=<      -- decode keys
      traverseOf (each . _2 . each) (rightToMaybe . fromBinary) . -- decode shares
      HM.toList . commShares
  
  deriveSafeCopySimple 0 'base ''Commitment
  