Skip to content

Commit af4b27e

Browse files
authored
Praos: use larger nonce stabilization window starting in Conway (#927)
Closes #297, closes IntersectMBO/cardano-ledger#2478, closes IntersectMBO/cardano-ledger#1914 See erratum 17.3 (amended in IntersectMBO/cardano-ledger#4015) in the Shelley ledger specs for context.
2 parents a2cb6e5 + d20248c commit af4b27e

File tree

8 files changed

+63
-44
lines changed

8 files changed

+63
-44
lines changed

docs/website/contents/for-developers/Glossary.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,22 +137,27 @@ In the latest Cardano era, each epoch lasts for 432000 slots (= 5 days).
137137

138138
The ledger rules take snapshots of the nonce and stake distribution at different points of time in the course of an epoch. A snapshot may only be _used_ when it has stabilized, which means that their block has become immutable (being older than `k` blocks). Currently, Cardano `mainnet` uses an epoch length of `10k/f` slots, divided into three parts:
139139

140-
- Part 1, length `4k/f` - At the beginning of this part, which forms the boundary with the previous epoch, the stake distribution snapshot is taken.
140+
Note that nothing in the implementation happens on the transition from Part 1 to Part 2 (in contrast to "from Part 2 to Part 3"), so there exist no concrete values for the individual length of these two phases. The length of Part 3 is however explicitly recorded in the implementation, so the length of Part 1 and Part 2 combined is `10k/f` minus the length of Part 3.
141+
142+
- Part 1, at least length `3k/f` - At the beginning of this part, which forms the boundary with the previous epoch, the stake distribution snapshot is taken.
141143
At the end of this part, the stake distribution has stabilized.
142144

143-
- Part 2, length `3k/f` - At the end of this part, the nonce snapshot is taken.
145+
- Part 2, at least length `k/f` - At the end of this part, the nonce snapshot is taken.
144146

145147
The nonce is snapshotted after the stake distribution to prevent _identity grinding_.
146148
However, this does not prevent _nonce grinding_.
147149

148150
It must contain at least one honest block (created by an honest party as defined above), so that the nonce cannot be solely influenced by the adversary – otherwise they could identity grind before Part 1, knowing what the nonce would be due to owning all the blocks up to its snapshot.
149151
This criterion is a minimum requirement and wildly unrealistic not to be satisfied with `mainnet` parameters.
150152

151-
The nonce snapshot could likely be taken earlier without sacrificing security, like after Part 1.
152-
Waiting another `3k/f` is playing it extra safe.
153+
The nonce snapshot could likely be taken earlier without sacrificing security, like already after `3k/f` after the start of the epoch.
154+
Waiting all the way until the start of Part 3 is playing it extra safe.
155+
156+
- Part 3, length `4k/f` since the Conway era, and `3k/f` for all prior Shelley-based eras - At the end of this part, the nonce snapshot has stabilized and can be used for the leader schedule of the next epoch.
153157

154-
- Part 3, length `3k/f` - At the end of this part, the nonce snapshot has stabilized and can be used for the leader schedule of the next epoch.
158+
Most importantly, Part 3 has to be at least `3k/f` slots (one stability window) long for the nonce to stabilize before the start of the next epoch (such that all pools agree on the leader schedule).
155159

160+
As advised by the IOG researchers, the nonce should be snapshotted even a bit earlier for intricate reasons related to Ouroboros Genesis. See erratum 17.3 in the [Shelley ledger specs](https://github.com/IntersectMBO/cardano-ledger/blob/master/README.md) for context.
156161

157162
## :Eventual consensus
158163

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
### Non-Breaking
2+
3+
- Change the randomness stabilization window for Conway (and future eras) to
4+
`4k/f` instead of `3k/f` (one stability window) that was used for Babbage and
5+
TPraos-based eras. See erratum 17.3 in the Shelley ledger specs for context.
6+
7+
Note that this is a backwards-incompatible change for all existing chains
8+
containing (at least one full epoch worth of) Conway blocks.

ouroboros-consensus-cardano/src/ouroboros-consensus-cardano/Ouroboros/Consensus/Cardano/Node.hs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,13 @@ protocolInfoCardano paramsCardano
730730
praosMaxMajorPV = maxMajorProtVer,
731731
praosMaxLovelaceSupply = SL.sgMaxLovelaceSupply genesisShelley,
732732
praosNetworkId = SL.sgNetworkId genesisShelley,
733-
praosSystemStart = SystemStart $ SL.sgSystemStart genesisShelley
733+
praosSystemStart = SystemStart $ SL.sgSystemStart genesisShelley,
734+
praosRandomnessStabilisationWindow =
735+
-- This value is used for all Praos eras /except/ Babbage, see
736+
-- 'partialConsensusConfigBabbage'.
737+
SL.computeRandomnessStabilisationWindow
738+
(SL.sgSecurityParam genesisShelley)
739+
(SL.mkActiveSlotCoeff $ SL.sgActiveSlotsCoeff genesisShelley)
734740
}
735741

736742
PraosParams { praosSlotsPerKESPeriod, praosMaxKESEvo } = praosParams
@@ -827,7 +833,17 @@ protocolInfoCardano paramsCardano
827833

828834
partialConsensusConfigBabbage ::
829835
PartialConsensusConfig (BlockProtocol (ShelleyBlock (Praos c) (BabbageEra c)))
830-
partialConsensusConfigBabbage = praosParams
836+
partialConsensusConfigBabbage = praosParams {
837+
-- For Praos in Babbage (just as in all TPraos eras) we use the
838+
-- smaller (3k/f vs 4k/f slots) stability window here for
839+
-- backwards-compatibility. See erratum 17.3 in the Shelley ledger
840+
-- specs for context.
841+
praosRandomnessStabilisationWindow =
842+
SL.computeStabilityWindow
843+
(SL.sgSecurityParam genesisShelley)
844+
(SL.mkActiveSlotCoeff $ SL.sgActiveSlotsCoeff genesisShelley)
845+
}
846+
831847

832848
partialLedgerConfigBabbage :: PartialLedgerConfig (ShelleyBlock (Praos c) (BabbageEra c))
833849
partialLedgerConfigBabbage =
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Breaking
2+
3+
- Remove unused `translateConsensusConfig` from `TranslateProto`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
### Breaking
2+
3+
- Add `praosRandomnessStabilisationWindow` to `PraosParams`, allowing to
4+
configure in which slot the epoch nonce is snapshotted (which can now vary
5+
between different eras).

ouroboros-consensus-protocol/src/ouroboros-consensus-protocol/Ouroboros/Consensus/Protocol/Praos.hs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ import Cardano.Ledger.Keys (KeyHash, KeyRole (BlockIssuer),
4545
import qualified Cardano.Ledger.Keys as SL
4646
import Cardano.Ledger.PoolDistr
4747
(IndividualPoolStake (IndividualPoolStake))
48-
import Cardano.Ledger.Shelley.API (computeStabilityWindow)
4948
import qualified Cardano.Ledger.Shelley.API as SL
5049
import Cardano.Ledger.Slot (Duration (Duration), (+*))
5150
import Cardano.Protocol.TPraos.BHeader (BoundedNatural (bvValue),
@@ -183,29 +182,35 @@ forgePraosFields
183182
-- | Praos parameters that are node independent
184183
data PraosParams = PraosParams
185184
{ -- | See 'Globals.slotsPerKESPeriod'.
186-
praosSlotsPerKESPeriod :: !Word64,
185+
praosSlotsPerKESPeriod :: !Word64,
187186
-- | Active slots coefficient. This parameter represents the proportion
188187
-- of slots in which blocks should be issued. This can be interpreted as
189188
-- the probability that a party holding all the stake will be elected as
190189
-- leader for a given slot.
191-
praosLeaderF :: !SL.ActiveSlotCoeff,
190+
praosLeaderF :: !SL.ActiveSlotCoeff,
192191
-- | See 'Globals.securityParameter'.
193-
praosSecurityParam :: !SecurityParam,
192+
praosSecurityParam :: !SecurityParam,
194193
-- | Maximum number of KES iterations, see 'Globals.maxKESEvo'.
195-
praosMaxKESEvo :: !Word64,
194+
praosMaxKESEvo :: !Word64,
196195
-- | Quorum for update system votes and MIR certificates, see
197196
-- 'Globals.quorum'.
198-
praosQuorum :: !Word64,
197+
praosQuorum :: !Word64,
199198
-- | All blocks invalid after this protocol version, see
200199
-- 'Globals.maxMajorPV'.
201-
praosMaxMajorPV :: !MaxMajorProtVer,
200+
praosMaxMajorPV :: !MaxMajorProtVer,
202201
-- | Maximum number of lovelace in the system, see
203202
-- 'Globals.maxLovelaceSupply'.
204-
praosMaxLovelaceSupply :: !Word64,
203+
praosMaxLovelaceSupply :: !Word64,
205204
-- | Testnet or mainnet?
206-
praosNetworkId :: !SL.Network,
205+
praosNetworkId :: !SL.Network,
207206
-- | The system start, as projected from the chain's genesis block.
208-
praosSystemStart :: !SystemStart
207+
praosSystemStart :: !SystemStart,
208+
-- | The number of slots before the start of an epoch where the
209+
-- corresponding epoch nonce is snapshotted. This has to be at least one
210+
-- stability window such that the nonce is stable at the beginning of the
211+
-- epoch. Ouroboros Genesis requires this to be even larger, see
212+
-- 'SL.computeRandomnessStabilisationWindow'.
213+
praosRandomnessStabilisationWindow :: !Word64
209214
}
210215
deriving (Generic, NoThunks)
211216

@@ -462,7 +467,7 @@ instance PraosCrypto c => ConsensusProtocol (Praos c) where
462467
-- - Update the operational certificate counter.
463468
reupdateChainDepState
464469
_cfg@( PraosConfig
465-
PraosParams {praosSecurityParam, praosLeaderF}
470+
PraosParams {praosRandomnessStabilisationWindow}
466471
ei
467472
)
468473
b
@@ -473,7 +478,7 @@ instance PraosCrypto c => ConsensusProtocol (Praos c) where
473478
praosStateLabNonce = prevHashToNonce (Views.hvPrevHash b),
474479
praosStateEvolvingNonce = newEvolvingNonce,
475480
praosStateCandidateNonce =
476-
if slot +* Duration stabilityWindow < firstSlotNextEpoch
481+
if slot +* Duration praosRandomnessStabilisationWindow < firstSlotNextEpoch
477482
then newEvolvingNonce
478483
else praosStateCandidateNonce cs,
479484
praosStateOCertCounters =
@@ -489,8 +494,6 @@ instance PraosCrypto c => ConsensusProtocol (Praos c) where
489494
let nextEpoch = EpochNo $ currentEpochNo + 1
490495
epochInfoFirst epochInfoWithErr nextEpoch
491496
cs = tickedPraosStateChainDepState tcs
492-
stabilityWindow =
493-
computeStabilityWindow (maxRollbacks praosSecurityParam) praosLeaderF
494497
eta = vrfNonceValue (Proxy @c) $ Views.hvVrfRes b
495498
newEvolvingNonce = praosStateEvolvingNonce cs eta
496499
OCert _ n _ _ = Views.hvOCert b

ouroboros-consensus-protocol/src/ouroboros-consensus-protocol/Ouroboros/Consensus/Protocol/Praos/Translate.hs

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ import qualified Cardano.Protocol.TPraos.Rules.Prtcl as SL
1818
import qualified Cardano.Protocol.TPraos.Rules.Tickn as SL
1919
import Data.Coerce (coerce)
2020
import qualified Data.Map.Strict as Map
21-
import Ouroboros.Consensus.Protocol.Praos (ConsensusConfig (..),
22-
Praos, PraosParams (..), PraosState (..))
21+
import Ouroboros.Consensus.Protocol.Praos (Praos, PraosState (..))
2322
import Ouroboros.Consensus.Protocol.Praos.Views
2423
(LedgerView (lvMaxBodySize, lvMaxHeaderSize, lvProtocolVersion))
2524
import qualified Ouroboros.Consensus.Protocol.Praos.Views as Views
26-
import Ouroboros.Consensus.Protocol.TPraos (TPraos, TPraosParams (..),
25+
import Ouroboros.Consensus.Protocol.TPraos (TPraos,
2726
TPraosState (tpraosStateChainDepState, tpraosStateLastSlot))
2827
import Ouroboros.Consensus.Protocol.Translate (TranslateProto (..))
2928

@@ -45,23 +44,6 @@ instance
4544
) =>
4645
TranslateProto (TPraos c1) (Praos c2)
4746
where
48-
translateConsensusConfig TPraosConfig {tpraosParams, tpraosEpochInfo} =
49-
PraosConfig
50-
{ praosParams =
51-
PraosParams
52-
{ praosSlotsPerKESPeriod = tpraosSlotsPerKESPeriod tpraosParams,
53-
praosLeaderF = tpraosLeaderF tpraosParams,
54-
praosSecurityParam = tpraosSecurityParam tpraosParams,
55-
praosMaxKESEvo = tpraosMaxKESEvo tpraosParams,
56-
praosQuorum = tpraosQuorum tpraosParams,
57-
praosMaxMajorPV = tpraosMaxMajorPV tpraosParams,
58-
praosMaxLovelaceSupply = tpraosMaxLovelaceSupply tpraosParams,
59-
praosNetworkId = tpraosNetworkId tpraosParams,
60-
praosSystemStart = tpraosSystemStart tpraosParams
61-
},
62-
praosEpochInfo = tpraosEpochInfo
63-
}
64-
6547
translateLedgerView SL.LedgerView {SL.lvPoolDistr, SL.lvChainChecks} =
6648
Views.LedgerView
6749
{ Views.lvPoolDistr = coercePoolDistr lvPoolDistr,

ouroboros-consensus-protocol/src/ouroboros-consensus-protocol/Ouroboros/Consensus/Protocol/Translate.hs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import Ouroboros.Consensus.Protocol.Abstract
99
-- | Translate across protocols
1010
class TranslateProto protoFrom protoTo
1111
where
12-
translateConsensusConfig ::
13-
ConsensusConfig protoFrom -> ConsensusConfig protoTo
1412
-- | Translate the ledger view.
1513
translateLedgerView ::
1614
LedgerView protoFrom -> LedgerView protoTo
@@ -20,6 +18,5 @@ class TranslateProto protoFrom protoTo
2018
-- | Degenerate instance - we may always translate from a protocol to itself.
2119
instance TranslateProto singleProto singleProto
2220
where
23-
translateConsensusConfig = id
2421
translateLedgerView = id
2522
translateChainDepState = id

0 commit comments

Comments
 (0)