Skip to content

Commit e35decb

Browse files
authored
Merge pull request #5265 from IntersectMBO/td/vrfs-counters
VRFVerKeyHash counters in PState
2 parents ad2b961 + 016a937 commit e35decb

File tree

12 files changed

+114
-35
lines changed

12 files changed

+114
-35
lines changed

eras/conway/impl/src/Cardano/Ledger/Conway/Rules/HardFork.hs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
{-# LANGUAGE EmptyDataDeriving #-}
55
{-# LANGUAGE FlexibleContexts #-}
66
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7+
{-# LANGUAGE LambdaCase #-}
78
{-# LANGUAGE MultiParamTypeClasses #-}
89
{-# LANGUAGE ScopedTypeVariables #-}
910
{-# LANGUAGE TypeApplications #-}
@@ -16,7 +17,7 @@ module Cardano.Ledger.Conway.Rules.HardFork (
1617
ConwayHardForkEvent (..),
1718
) where
1819

19-
import Cardano.Ledger.BaseTypes (ProtVer (..), ShelleyBase, natVersion)
20+
import Cardano.Ledger.BaseTypes
2021
import Cardano.Ledger.Conway.Core
2122
import Cardano.Ledger.Conway.Era (ConwayEra, ConwayHARDFORK)
2223
import Cardano.Ledger.Conway.State
@@ -36,9 +37,12 @@ import Control.State.Transition (
3637
tellEvent,
3738
transitionRules,
3839
)
40+
import Data.Map.Strict (Map)
3941
import qualified Data.Map.Strict as Map
42+
import Data.Maybe (fromMaybe)
4043
import qualified Data.Set as Set
4144
import Data.Void (Void)
45+
import Data.Word (Word64)
4246
import GHC.Generics (Generic)
4347
import Lens.Micro
4448

@@ -102,8 +106,19 @@ updateDRepDelegations certState =
102106

103107
populateVRFKeyHashes :: PState era -> PState era
104108
populateVRFKeyHashes pState =
105-
let allVRFKeyHashes =
106-
spsVrf
107-
<$> Map.elems (pState ^. psStakePoolsL)
108-
<> Map.elems (pState ^. psFutureStakePoolsL)
109-
in pState & psVRFKeyHashesL .~ Set.fromList allVRFKeyHashes
109+
pState
110+
& psVRFKeyHashesL
111+
%~ accumulateVRFKeyHashes (pState ^. psStakePoolsL)
112+
. accumulateVRFKeyHashes (pState ^. psFutureStakePoolsL)
113+
where
114+
accumulateVRFKeyHashes ::
115+
Map (KeyHash 'StakePool) StakePoolState ->
116+
Map (VRFVerKeyHash 'StakePoolVRF) (NonZero Word64) ->
117+
Map (VRFVerKeyHash 'StakePoolVRF) (NonZero Word64)
118+
accumulateVRFKeyHashes spsMap acc =
119+
Map.foldr' (\sps -> addVRFKeyHashOccurrence (sps ^. spsVrfL)) acc spsMap
120+
addVRFKeyHashOccurrence vrfKeyHash =
121+
Map.insertWith combine vrfKeyHash (knownNonZeroBounded @1)
122+
where
123+
-- Saturates at maxBound: if (+1) would overflow to 0, keep existing value
124+
combine _ oldVal = fromMaybe oldVal $ mapNonZero (+ 1) oldVal

eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ conwaySpec ::
112112
, InjectRuleFailure "LEDGER" ConwayLedgerPredFailure era
113113
, InjectRuleFailure "LEDGER" ConwayUtxoPredFailure era
114114
, InjectRuleFailure "LEDGER" ConwayUtxowPredFailure era
115+
, InjectRuleFailure "LEDGER" ShelleyPoolPredFailure era
115116
, InjectRuleFailure "BBODY" ConwayBbodyPredFailure era
116117
, InjectRuleEvent "TICK" ConwayEpochEvent era
117118
, Event (EraRule "EPOCH" era) ~ ConwayEpochEvent era

eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/HardForkSpec.hs

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@ import Cardano.Ledger.Conway.Governance
1414
import Cardano.Ledger.Conway.PParams
1515
import Cardano.Ledger.Conway.State
1616
import Cardano.Ledger.Shelley.LedgerState
17-
import qualified Data.Set as Set
17+
import Cardano.Ledger.Shelley.Rules (ShelleyPoolPredFailure (..))
18+
import qualified Data.Map.Strict as Map
1819
import Lens.Micro
1920
import Test.Cardano.Ledger.Conway.ImpTest
2021
import Test.Cardano.Ledger.Core.Rational
2122
import Test.Cardano.Ledger.Imp.Common
2223

2324
spec ::
2425
forall era.
25-
ConwayEraImp era => SpecWith (ImpInit (LedgerSpec era))
26+
( ConwayEraImp era
27+
, InjectRuleFailure "LEDGER" ShelleyPoolPredFailure era
28+
) =>
29+
SpecWith (ImpInit (LedgerSpec era))
2630
spec = do
2731
it "VRF Keyhashes get populated at v11 HardFork" $ do
2832
-- Since we're testing the HardFork to 11, the test only makes sense for protocol version 10
@@ -48,7 +52,40 @@ spec = do
4852

4953
expectVRFs [] -- VRF keyhashes in PState is not yet populated
5054
enactHardForkV11
51-
expectVRFs [vrf2, vrf3, vrf5]
55+
expectVRFs [(vrf2, 2), (vrf3, 1), (vrf5, 1)]
56+
57+
it "Retiring a stake pool with a duplicate VRF Keyhash after v11 HardFork" $ do
58+
whenMajorVersion @10 $ do
59+
-- register two pools with the same vrf keyhash before the hard fork
60+
(kh1, vrf) <- (,) <$> freshKeyHash <*> freshKeyHashVRF
61+
registerStakePool kh1 vrf
62+
kh2 <- freshKeyHash
63+
registerStakePool kh2 vrf
64+
kh3 <- freshKeyHash
65+
registerStakePool kh3 vrf
66+
67+
enactHardForkV11
68+
expectVRFs [(vrf, 3)]
69+
-- retire one of the pools after the hard fork
70+
retireStakePool kh1 (EpochInterval 1)
71+
retireStakePool kh2 (EpochInterval 1)
72+
passEpoch
73+
-- the vrf keyhash should still be present, since another pool is registered with it
74+
expectVRFs [(vrf, 1)]
75+
76+
-- registration of the same vrf should be disallowed
77+
kh4 <- freshKeyHash
78+
registerStakePoolTx kh4 vrf >>= \tx ->
79+
submitFailingTx
80+
tx
81+
[injectFailure $ VRFKeyHashAlreadyRegistered kh4 vrf]
82+
83+
retireStakePool kh3 (EpochInterval 1)
84+
passEpoch
85+
expectVRFs []
86+
87+
registerStakePool kh4 vrf
88+
expectVRFs [(vrf, 1)]
5289
where
5390
enactHardForkV11 = do
5491
modifyPParams $ \pp ->
@@ -61,17 +98,21 @@ spec = do
6198
submitYesVoteCCs_ committee govActionId
6299
passNEpochs 2
63100
getProtVer `shouldReturn` pv11
64-
registerStakePool kh vrf = do
101+
registerStakePoolTx kh vrf = do
65102
pps <- registerRewardAccount >>= freshPoolParams kh
66-
submitTx_ $
103+
pure $
67104
mkBasicTx mkBasicTxBody
68105
& bodyTxL . certsTxBodyL .~ [RegPoolTxCert $ pps & ppVrfL .~ vrf]
106+
registerStakePool kh vrf =
107+
registerStakePoolTx kh vrf >>= submitTx_
69108
retireStakePool kh retirementInterval = do
70109
curEpochNo <- getsNES nesELL
71110
let retirement = addEpochInterval curEpochNo retirementInterval
72111
submitTx_ $
73112
mkBasicTx mkBasicTxBody
74113
& bodyTxL . certsTxBodyL .~ [RetirePoolTxCert kh retirement]
75114
expectVRFs vrfs =
76-
psVRFKeyHashes <$> getPState `shouldReturn` Set.fromList vrfs
115+
psVRFKeyHashes
116+
<$> getPState
117+
`shouldReturn` Map.fromList [(k, unsafeNonZero v) | (k, v) <- vrfs]
77118
getPState = getsNES @era $ nesEsL . esLStateL . lsCertStateL . certPStateL

eras/shelley/impl/src/Cardano/Ledger/Shelley/Rules/Pool.hs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import Cardano.Ledger.BaseTypes (
3434
ShelleyBase,
3535
addEpochInterval,
3636
invalidKey,
37+
knownNonZeroBounded,
3738
networkId,
3839
)
3940
import Cardano.Ledger.Binary (
@@ -68,7 +69,6 @@ import Control.State.Transition (
6869
import qualified Data.ByteString as BS
6970
import Data.Kind (Type)
7071
import qualified Data.Map as Map
71-
import qualified Data.Set as Set
7272
import Data.Word (Word8)
7373
import GHC.Generics (Generic)
7474
import Lens.Micro
@@ -253,9 +253,9 @@ poolDelegationTransition = do
253253
-- register new, Pool-Reg
254254
Nothing -> do
255255
when (hardforkConwayDisallowDuplicatedVRFKeys pv) $ do
256-
Set.notMember ppVrf psVRFKeyHashes ?! VRFKeyHashAlreadyRegistered ppId ppVrf
256+
Map.notMember ppVrf psVRFKeyHashes ?! VRFKeyHashAlreadyRegistered ppId ppVrf
257257
let updateVRFKeyHash
258-
| hardforkConwayDisallowDuplicatedVRFKeys pv = Set.insert ppVrf
258+
| hardforkConwayDisallowDuplicatedVRFKeys pv = Map.insert ppVrf (knownNonZeroBounded @1)
259259
| otherwise = id
260260
tellEvent $ RegisterPool ppId
261261
pure $
@@ -266,17 +266,18 @@ poolDelegationTransition = do
266266
Just stakePoolState -> do
267267
when (hardforkConwayDisallowDuplicatedVRFKeys pv) $ do
268268
ppVrf == stakePoolState ^. spsVrfL
269-
|| Set.notMember ppVrf psVRFKeyHashes ?! VRFKeyHashAlreadyRegistered ppId ppVrf
269+
|| Map.notMember ppVrf psVRFKeyHashes ?! VRFKeyHashAlreadyRegistered ppId ppVrf
270270
let updateFutureVRFKeyHash
271271
| hardforkConwayDisallowDuplicatedVRFKeys pv =
272-
-- If a pool re-registers with a fresh VRF, we have to add it to the list,
272+
-- If a pool re-registers with a fresh VRF, we have to record it in the map,
273273
-- but also remove the previous VRFHashKey potentially stored in previous re-registration within the same epoch,
274274
-- which we retrieve from futureStakePools.
275275
case Map.lookup ppId psFutureStakePools of
276-
Nothing -> Set.insert ppVrf
276+
Nothing -> Map.insert ppVrf (knownNonZeroBounded @1)
277277
Just futureStakePoolState
278278
| futureStakePoolState ^. spsVrfL /= ppVrf ->
279-
Set.insert ppVrf . Set.delete (futureStakePoolState ^. spsVrfL)
279+
(Map.insert ppVrf (knownNonZeroBounded @1))
280+
. Map.delete (futureStakePoolState ^. spsVrfL)
280281
| otherwise -> id
281282
| otherwise = id
282283
tellEvent $ ReregisterPool ppId

eras/shelley/impl/src/Cardano/Ledger/Shelley/Rules/PoolReap.hs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module Cardano.Ledger.Shelley.Rules.PoolReap (
2323
) where
2424

2525
import Cardano.Ledger.Address
26-
import Cardano.Ledger.BaseTypes (ShelleyBase)
26+
import Cardano.Ledger.BaseTypes
2727
import Cardano.Ledger.Coin (Coin, CompactForm)
2828
import Cardano.Ledger.Compactible (fromCompact)
2929
import Cardano.Ledger.Core
@@ -35,7 +35,6 @@ import Cardano.Ledger.Shelley.LedgerState (
3535
utxosGovStateL,
3636
)
3737
import Cardano.Ledger.Shelley.LedgerState.Types (potEqualsObligation)
38-
import Cardano.Ledger.Slot (EpochNo (..))
3938
import Cardano.Ledger.State
4039
import Cardano.Ledger.Val ((<+>), (<->))
4140
import Control.DeepSeq (NFData)
@@ -50,11 +49,14 @@ import Control.State.Transition (
5049
)
5150
import Data.Default (Default, def)
5251
import Data.Foldable (fold)
52+
import Data.Foldable as F (foldl')
5353
import qualified Data.Map.Merge.Strict as Map
54+
import Data.Map.Strict (Map)
5455
import qualified Data.Map.Strict as Map
5556
import Data.Set (Set)
5657
import qualified Data.Set as Set
5758
import Data.Void (Void)
59+
import Data.Word (Word64)
5860
import GHC.Generics (Generic)
5961
import Lens.Micro
6062

@@ -135,7 +137,7 @@ poolReapTransition = do
135137
ps0 = cs0 ^. certPStateL
136138
-- find the set of VRF key hashes that are no longer relevant, since they have been overwritten
137139
-- via pool re-registration
138-
danglingVrfKeyHashes =
140+
danglingVRFKeyHashes =
139141
Set.fromList $
140142
Map.elems $
141143
Map.merge
@@ -163,7 +165,8 @@ poolReapTransition = do
163165
retiringPools :: Map.Map (KeyHash 'StakePool) StakePoolState
164166
retiringPools = Map.restrictKeys (psStakePools ps) retired
165167
-- collect all accounts for stake pools that will retire
166-
retiredVRFs = foldMap (Set.singleton . spsVrf) retiringPools
168+
retiredVRFKeyHashes = spsVrf <$> Map.elems retiringPools
169+
167170
-- collect all of the potential refunds
168171
accountRefunds :: Map.Map (Credential 'Staking) (CompactForm Coin)
169172
accountRefunds =
@@ -209,8 +212,19 @@ poolReapTransition = do
209212
& certPStateL . psStakePoolsL %~ (`Map.withoutKeys` retired)
210213
& certPStateL . psRetiringL %~ (`Map.withoutKeys` retired)
211214
& certPStateL . psVRFKeyHashesL
212-
%~ ((`Set.difference` retiredVRFs) . (`Set.difference` danglingVrfKeyHashes))
215+
%~ ( removeVRFKeyHashOccurrences (retiredVRFKeyHashes)
216+
. (`Map.withoutKeys` danglingVRFKeyHashes)
217+
)
213218
)
219+
where
220+
removeVRFKeyHashOccurrences ::
221+
[VRFVerKeyHash 'StakePoolVRF] ->
222+
Map (VRFVerKeyHash 'StakePoolVRF) (NonZero Word64) ->
223+
Map (VRFVerKeyHash 'StakePoolVRF) (NonZero Word64)
224+
removeVRFKeyHashOccurrences vrfs vrfsMap = F.foldl' (flip removeVRFKeyHashOccurrence) vrfsMap vrfs
225+
removeVRFKeyHashOccurrence =
226+
-- Removes the key from the map if the value drops to 0
227+
Map.update (mapNonZero (\n -> n - 1))
214228

215229
renderPoolReapViolation ::
216230
( EraGov era

eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/Imp/PoolSpec.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ spec = describe "POOL" $ do
344344
$ Map.member poolKh retiring == isRetiring
345345
expectVRFs vrfs = do
346346
whenMajorVersionAtLeast @11 $
347-
psVRFKeyHashes <$> getPState `shouldReturn` vrfs
347+
Map.keysSet . psVRFKeyHashes <$> getPState `shouldReturn` vrfs
348348
poolParams kh vrf = do
349349
pps <- registerRewardAccount >>= freshPoolParams kh
350350
pure $ pps & ppVrfL .~ vrf

eras/shelley/test-suite/test/Test/Cardano/Ledger/Shelley/Examples/Combinators.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ reapPool pool cs = cs {chainNes = nes'}
354354
ps
355355
{ psRetiring = Map.delete poolId (psRetiring ps)
356356
, psStakePools = Map.delete poolId (psStakePools ps)
357-
, psVRFKeyHashes = Set.delete (ppVrf pool) (psVRFKeyHashes ps)
357+
, psVRFKeyHashes = Map.delete (ppVrf pool) (psVRFKeyHashes ps)
358358
}
359359
pp = es ^. curPParamsEpochStateL
360360
ds = dps ^. certDStateL

eras/shelley/test-suite/test/Test/Cardano/Ledger/Shelley/Serialisation/Golden/Encoding.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ tests =
10851085
expectedHex =
10861086
mconcat
10871087
[ "8700a1581ce0a714319812c3f773ba04ec5d6b3ffcd5aad85006805b047b0825410aa1581ca64647"
1088-
, "4b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520a0384821927101903e882828480a0a0"
1088+
, "4b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520a0384821927101903e8828284a0a0a0"
10891089
, "a08482a0a0a0a084a0a0000086a15822ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef"
10901090
, "36bcea6ebb8a3e250000583d003900cb9358529df4729c3246a2a033cb9821abbfd16de488800590"
10911091
, "4abc410d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb06000a1903e80185a0a0"

libs/cardano-ledger-core/src/Cardano/Ledger/State/CertState.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import Cardano.Ledger.BaseTypes (
4848
Anchor (..),
4949
AnchorData,
5050
KeyValuePairs (..),
51+
NonZero,
5152
StrictMaybe,
5253
ToKeyValuePairs (..),
5354
)
@@ -84,8 +85,8 @@ import qualified Data.Foldable as F
8485
import Data.Kind (Type)
8586
import Data.Map.Strict (Map)
8687
import qualified Data.Map.Strict as Map
87-
import Data.Set (Set)
8888
import qualified Data.Set as Set
89+
import Data.Word (Word64)
8990
import GHC.Generics (Generic)
9091
import Lens.Micro
9192
import NoThunks.Class (NoThunks (..))
@@ -221,7 +222,7 @@ lookupRewardDState DState {dsAccounts} cred = do
221222

222223
-- | The state used by the POOL rule, which tracks stake pool information.
223224
data PState era = PState
224-
{ psVRFKeyHashes :: !(Set (VRFVerKeyHash 'StakePoolVRF))
225+
{ psVRFKeyHashes :: !(Map (VRFVerKeyHash 'StakePoolVRF) (NonZero Word64))
225226
-- ^ VRF key hashes that have been registered via PoolParams
226227
, psStakePools :: !(Map (KeyHash 'StakePool) StakePoolState)
227228
-- ^ The state of current stake pools.
@@ -386,7 +387,7 @@ instance Default (Accounts era) => Default (DState era) where
386387
def = DState def Map.empty (GenDelegs Map.empty) def
387388

388389
instance Default (PState era) where
389-
def = PState Set.empty Map.empty Map.empty Map.empty
390+
def = PState Map.empty Map.empty Map.empty Map.empty
390391

391392
-- | A composite of all the Deposits the system is obligated to eventually pay back.
392393
data Obligations = Obligations
@@ -443,5 +444,5 @@ psFutureStakePoolsL = lens psFutureStakePools (\ps u -> ps {psFutureStakePools =
443444
psRetiringL :: Lens' (PState era) (Map (KeyHash 'StakePool) EpochNo)
444445
psRetiringL = lens psRetiring (\ps u -> ps {psRetiring = u})
445446

446-
psVRFKeyHashesL :: Lens' (PState era) (Set (VRFVerKeyHash 'StakePoolVRF))
447+
psVRFKeyHashesL :: Lens' (PState era) (Map (VRFVerKeyHash 'StakePoolVRF) (NonZero Word64))
447448
psVRFKeyHashesL = lens psVRFKeyHashes (\ps u -> ps {psVRFKeyHashes = u})

libs/cardano-ledger-test/src/Test/Cardano/Ledger/Constrained/Conway/Instances/Basic.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,10 @@ class
418418
ppToSubset :: PParams era -> SimplePParams era
419419
updateToPPU :: SimplePPUpdate -> PParamsUpdate era
420420
ppuToUpdate :: PParamsUpdate era -> SimplePPUpdate
421+
422+
instance HasSimpleRep (NonZero Word64) where
423+
type SimpleRep (NonZero Word64) = Word64
424+
toSimpleRep = unNonZero
425+
fromSimpleRep = unsafeNonZero
426+
427+
instance HasSpec (NonZero Word64)

0 commit comments

Comments
 (0)