From a6a72241140e4af10facee1b7f727a0b59ce6841 Mon Sep 17 00:00:00 2001 From: Emil B Date: Wed, 11 Dec 2024 20:32:32 +0100 Subject: [PATCH 1/3] Introduce the P7 to P8 update. --- .../src/Concordium/ProtocolUpdate/P7.hs | 12 +- .../ProtocolUpdate/P7/ProtocolP8.hs | 120 ++++++++++++++++++ 2 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 concordium-consensus/src/Concordium/ProtocolUpdate/P7/ProtocolP8.hs diff --git a/concordium-consensus/src/Concordium/ProtocolUpdate/P7.hs b/concordium-consensus/src/Concordium/ProtocolUpdate/P7.hs index 14916211f3..f2bb1febd3 100644 --- a/concordium-consensus/src/Concordium/ProtocolUpdate/P7.hs +++ b/concordium-consensus/src/Concordium/ProtocolUpdate/P7.hs @@ -8,7 +8,7 @@ module Concordium.ProtocolUpdate.P7 ( updateNextProtocolVersion, ) where -import Control.Monad.State +import Control.Monad.State hiding (get) import qualified Data.HashMap.Strict as HM import Data.Serialize @@ -16,21 +16,25 @@ import qualified Concordium.Crypto.SHA256 as SHA256 import Concordium.Types import Concordium.Types.Updates +import qualified Concordium.Genesis.Data.P8 as P8 import Concordium.GlobalState.BlockState import qualified Concordium.GlobalState.Persistent.BlockState as PBS import Concordium.GlobalState.Types import qualified Concordium.GlobalState.Types as GSTypes import Concordium.KonsensusV1.TreeState.Implementation import Concordium.KonsensusV1.TreeState.Types +import qualified Concordium.ProtocolUpdate.P7.ProtocolP8 as ProtocolP8 import qualified Concordium.ProtocolUpdate.P7.Reboot as Reboot -- | Updates that are supported from protocol version P7. -data Update = Reboot +data Update + = Reboot + | ProtocolP8 P8.ProtocolUpdateData deriving (Show) -- | Hash map for resolving updates from their specification hash. updates :: HM.HashMap SHA256.Hash (Get Update) -updates = HM.fromList [(Reboot.updateHash, return Reboot)] +updates = HM.fromList [(Reboot.updateHash, return Reboot), (ProtocolP8.updateHash, ProtocolP8 <$> get)] -- | Determine if a 'ProtocolUpdate' corresponds to a supported update type. checkUpdate :: ProtocolUpdate -> Either String Update @@ -53,9 +57,11 @@ updateRegenesis :: BlockPointer (MPV m) -> m (PVInit m) updateRegenesis Reboot = Reboot.updateRegenesis +updateRegenesis (ProtocolP8 protocolUpdateData) = ProtocolP8.updateRegenesis protocolUpdateData -- | Determine the protocol version the update will update to. updateNextProtocolVersion :: Update -> SomeProtocolVersion updateNextProtocolVersion Reboot{} = SomeProtocolVersion SP7 +updateNextProtocolVersion ProtocolP8{} = SomeProtocolVersion SP8 diff --git a/concordium-consensus/src/Concordium/ProtocolUpdate/P7/ProtocolP8.hs b/concordium-consensus/src/Concordium/ProtocolUpdate/P7/ProtocolP8.hs new file mode 100644 index 0000000000..5ffccc9703 --- /dev/null +++ b/concordium-consensus/src/Concordium/ProtocolUpdate/P7/ProtocolP8.hs @@ -0,0 +1,120 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeFamilies #-} + +-- | This module implements the P7.ProtocolP8 protocol update. +-- This protocol update is valid at protocol version P7, and updates +-- to protocol version P8. +-- +-- This produces a new 'RegenesisDataP8' using the 'GDP8RegenesisFromP7' constructor, +-- as follows: +-- +-- * 'genesisCore': +-- +-- * 'genesisTime' is the timestamp of the last finalized block of the previous chain. +-- * 'genesisEpochDuration' is taken from the previous genesis. +-- * 'genesisSignatureThreshold' is taken from the previous genesis. +-- +-- * 'genesisFirstGenesis' is either: +-- +-- * the hash of the genesis block of the previous chain, if it is a 'GDP7Initial'; or +-- * the 'genesisFirstGenesis' value of the genesis block of the previous chain, if it +-- is a 'GDP7Regenesis'. +-- +-- * 'genesisPreviousGenesis' is the hash of the previous genesis block. +-- +-- * 'genesisTerminalBlock' is the hash of the last finalized block of the previous chain. +-- +-- * 'genesisStateHash' is the state hash of the last finalized block of the previous chain. +-- +-- * 'genesisMigration' is derived from the data provided to the protocol update, which is the +-- validator score parameters +-- +-- The block state is taken from the last finalized block of the previous chain. It is updated +-- as part of the state migration, which makes the following changes: +-- +-- * The seed state is migrated as follows: +-- +-- * The current epoch is reset to zero. +-- * The current and updated leadership election nonce are set to the hash of +-- @"Regenesis" <> encode oldUpdatedNonce@. +-- * The trigger block time is kept the same, meaning that the epoch will transition as soon +-- as possible. +-- * The epoch transition triggered flag is set. +-- * The shutdown triggered flag is cleared. +-- +-- * The old current epoch is subtracted from the next payday epoch. +-- +-- * The protocol update queue is emptied during the migration. +-- +-- Note that, the initial epoch of the new chain is not considered +-- a new epoch for the purposes of block rewards and baker/finalization committee determination. +-- In particular, the timing of the next payday will be the same as if the protocol update +-- had not happened. (For instance, if it would have happened at the start of the next epoch +-- prior to the protocol update, after the update it will happen at the start of epoch 1. +-- The trigger block time in epoch 0 of the new consensus is the same as the trigger block +-- time in the final epoch of the old consensus.) +-- Furthermore, the bakers from the final epoch of the previous chain are also the bakers for the +-- initial epoch of the new chain. +module Concordium.ProtocolUpdate.P7.ProtocolP8 where + +import Control.Monad.State +import Lens.Micro.Platform + +import qualified Concordium.Crypto.SHA256 as SHA256 +import qualified Concordium.Genesis.Data as GenesisData +import qualified Concordium.Genesis.Data.BaseV1 as BaseV1 +import qualified Concordium.Genesis.Data.P8 as P8 +import Concordium.GlobalState.BlockState +import qualified Concordium.GlobalState.Persistent.BlockState as PBS +import Concordium.GlobalState.Types +import qualified Concordium.GlobalState.Types as GSTypes +import qualified Concordium.KonsensusV1.TreeState.Implementation as TreeState +import Concordium.KonsensusV1.TreeState.Types +import Concordium.KonsensusV1.Types +import Concordium.Types.HashableTo (getHash) +import Concordium.Types.ProtocolVersion + +-- | The hash that identifies a update from P7 to P8 protocol. +-- This is the hash of the published specification document. +updateHash :: SHA256.Hash +updateHash = read "dummy" -- FIXME: replace with hash of the published specification document + +-- | Construct the genesis data for a P7.ProtocolP8 update. +-- This takes the terminal block of the old chain which is used as the basis for constructing +-- the new genesis block. +updateRegenesis :: + ( MPV m ~ 'P7, + BlockStateStorage m, + MonadState (TreeState.SkovData (MPV m)) m, + GSTypes.BlockState m ~ PBS.HashedPersistentBlockState (MPV m) + ) => + P8.ProtocolUpdateData -> + -- | The terminal block of the old chain. + BlockPointer 'P7 -> + m (PVInit m) +updateRegenesis protocolUpdateData terminalBlock = do + -- Genesis time is the timestamp of the terminal block + let regenesisTime = blockTimestamp terminalBlock + -- Core parameters are derived from the old genesis, apart from genesis time which is set for + -- the time of the terminal block. + gMetadata <- use TreeState.genesisMetadata + BaseV1.CoreGenesisParametersV1{..} <- gmParameters <$> use TreeState.genesisMetadata + let core = + BaseV1.CoreGenesisParametersV1 + { BaseV1.genesisTime = regenesisTime, + .. + } + -- genesisFirstGenesis is the block hash of the previous genesis, if it is initial, + -- or the genesisFirstGenesis of the previous genesis otherwise. + let genesisFirstGenesis = gmFirstGenesisHash gMetadata + genesisPreviousGenesis = gmCurrentGenesisHash gMetadata + genesisTerminalBlock = getHash terminalBlock + let regenesisBlockState = bpState terminalBlock + genesisStateHash <- getStateHash regenesisBlockState + let genesisMigration = + P8.StateMigrationData + { migrationProtocolUpdateData = protocolUpdateData + } + let newGenesis = GenesisData.RGDP8 $ P8.GDP8RegenesisFromP7{genesisRegenesis = BaseV1.RegenesisDataV1{genesisCore = core, ..}, ..} + return (PVInit newGenesis (GenesisData.StateMigrationParametersP7ToP8 genesisMigration) (bmHeight $ bpInfo terminalBlock)) From f628a35f3b01539cbe34d6af71cee6992fda070f Mon Sep 17 00:00:00 2001 From: Emil B Date: Mon, 6 Jan 2025 14:22:58 +0100 Subject: [PATCH 2/3] Update P8 specification hash. --- .../src/Concordium/ProtocolUpdate/P7/ProtocolP8.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concordium-consensus/src/Concordium/ProtocolUpdate/P7/ProtocolP8.hs b/concordium-consensus/src/Concordium/ProtocolUpdate/P7/ProtocolP8.hs index 5ffccc9703..923a9f5947 100644 --- a/concordium-consensus/src/Concordium/ProtocolUpdate/P7/ProtocolP8.hs +++ b/concordium-consensus/src/Concordium/ProtocolUpdate/P7/ProtocolP8.hs @@ -78,7 +78,7 @@ import Concordium.Types.ProtocolVersion -- | The hash that identifies a update from P7 to P8 protocol. -- This is the hash of the published specification document. updateHash :: SHA256.Hash -updateHash = read "dummy" -- FIXME: replace with hash of the published specification document +updateHash = read "f12e20b6936a6b1b736e95715e1654b92adb4226ef7601b4183895bee563f9da" -- | Construct the genesis data for a P7.ProtocolP8 update. -- This takes the terminal block of the old chain which is used as the basis for constructing From 65795949809192838bf708085da83a12c959ef5b Mon Sep 17 00:00:00 2001 From: Emil B Date: Wed, 8 Jan 2025 09:46:30 +0100 Subject: [PATCH 3/3] Update Changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 708ec75f3f..f51916ad96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased changes +- Add P7 -> P8 update. - Automatically suspend validators from the consensus that missed too many rounds in the previous payday. - Add support for suspend/resume to validator configuration updates.