diff --git a/ouroboros-consensus/ouroboros-consensus-mock/src/Ouroboros/Consensus/Mock/Protocol/Praos.hs b/ouroboros-consensus/ouroboros-consensus-mock/src/Ouroboros/Consensus/Mock/Protocol/Praos.hs index 82c04938105..28e3d53c68f 100644 --- a/ouroboros-consensus/ouroboros-consensus-mock/src/Ouroboros/Consensus/Mock/Protocol/Praos.hs +++ b/ouroboros-consensus/ouroboros-consensus-mock/src/Ouroboros/Consensus/Mock/Protocol/Praos.hs @@ -46,6 +46,7 @@ import Data.Proxy (Proxy (..)) import Data.Typeable import Data.Word (Word64) import GHC.Generics (Generic) +import GHC.Stack import Numeric.Natural import Cardano.Crypto.DSIGN.Ed448 (Ed448DSIGN) @@ -141,18 +142,21 @@ forgePraosFields :: ( MonadRandom m -> (PraosExtraFields c -> toSign) -> m (PraosFields c toSign) forgePraosFields PraosNodeConfig{..} updateState PraosProof{..} mkToSign = do - keyKES <- runUpdate updateState $ \case + -- For the mock implementation, we consider the KES period to be the slot. + -- In reality, there will be some kind of constants slotsPerPeriod factor. + let kesPeriod :: Natural + kesPeriod = fromIntegral (unSlotNo praosProofSlot) + + oldKey <- runUpdate updateState $ \case PraosKeyEvolving -> -- Another thread is currently evolving the key; wait Nothing PraosKeyAvailable oldKey -> - -- TODO : We should not update the key on each signing, but X slots - -- (for configurable param X) return (PraosKeyEvolving, oldKey) - -- Evolve the key (if needed) + -- Evolve the key newKey <- fromMaybe (error "mkOutoborosPayload: updateKES failed") <$> - updateKES () keyKES + updateKESTo () kesPeriod oldKey runUpdate updateState $ \_ -> return (PraosKeyAvailable newKey, ()) let signedFields = PraosExtraFields { @@ -160,19 +164,37 @@ forgePraosFields PraosNodeConfig{..} updateState PraosProof{..} mkToSign = do , praosRho = praosProofRho , praosY = praosProofY } - m <- signedKES - () - (fromIntegral (unSlotNo praosProofSlot)) - (mkToSign signedFields) - keyKES + m <- signedKES () kesPeriod (mkToSign signedFields) newKey case m of Nothing -> error "mkOutoborosPayload: signedKES failed" Just signature -> do return $ PraosFields { - praosSignature = signature + praosSignature = signature , praosExtraFields = signedFields } +-- | Update key to specified period +-- +-- Throws an error if the key is already /past/ the period +updateKESTo :: forall m v. (MonadRandom m, HasCallStack, KESAlgorithm v) + => ContextKES v + -> Natural -- ^ KES period to evolve to + -> SignKeyKES v + -> m (Maybe (SignKeyKES v)) +updateKESTo ctxt evolveTo = go + where + go :: SignKeyKES v -> m (Maybe (SignKeyKES v)) + go key + | iterationCountKES ctxt key < evolveTo = do + mKey' <- updateKES ctxt key + case mKey' of + Nothing -> return Nothing + Just key' -> go key' + | iterationCountKES ctxt key == evolveTo = + return (Just key) + | otherwise = + error "updateKESTo: key already past period" + {------------------------------------------------------------------------------- Praos specific types -------------------------------------------------------------------------------} diff --git a/ouroboros-consensus/ouroboros-consensus-mock/test/Main.hs b/ouroboros-consensus/ouroboros-consensus-mock/test/Main.hs index a4a0ffef2b0..0e0e2672c80 100644 --- a/ouroboros-consensus/ouroboros-consensus-mock/test/Main.hs +++ b/ouroboros-consensus/ouroboros-consensus-mock/test/Main.hs @@ -6,7 +6,7 @@ import qualified Test.Consensus.Ledger.Mock (tests) import qualified Test.ThreadNet.BFT (tests) import qualified Test.ThreadNet.LeaderSchedule (tests) import qualified Test.ThreadNet.PBFT (tests) --- import qualified Test.ThreadNet.Praos (tests) +import qualified Test.ThreadNet.Praos (tests) main :: IO () main = defaultMain tests @@ -18,5 +18,5 @@ tests = , Test.ThreadNet.BFT.tests , Test.ThreadNet.LeaderSchedule.tests , Test.ThreadNet.PBFT.tests --- , Test.ThreadNet.Praos.tests + , Test.ThreadNet.Praos.tests ]