Skip to content

Commit 2f68910

Browse files
committed
Refactoring to ensure --gen-stake-delegs uses a minimal amount of memory and generates fewer files.
1 parent 3775be5 commit 2f68910

File tree

6 files changed

+166
-121
lines changed

6 files changed

+166
-121
lines changed

cardano-cli/src/Cardano/CLI/Shelley/Run/Address.hs

+33-16
Original file line numberDiff line numberDiff line change
@@ -68,27 +68,44 @@ runAddressKeyGenToFile :: AddressKeyType
6868
-> VerificationKeyFile
6969
-> SigningKeyFile
7070
-> ExceptT ShelleyAddressCmdError IO ()
71-
runAddressKeyGenToFile kt (VerificationKeyFile vkeyPath) (SigningKeyFile skeyPath) =
72-
case kt of
73-
AddressKeyShelley -> generateAndWriteKeyFiles AsPaymentKey
74-
AddressKeyShelleyExtended -> generateAndWriteKeyFiles AsPaymentExtendedKey
75-
AddressKeyByron -> generateAndWriteKeyFiles AsByronKey
71+
runAddressKeyGenToFile kt vkf skf = case kt of
72+
AddressKeyShelley -> generateAndWriteKeyFiles AsPaymentKey vkf skf
73+
AddressKeyShelleyExtended -> generateAndWriteKeyFiles AsPaymentExtendedKey vkf skf
74+
AddressKeyByron -> generateAndWriteKeyFiles AsByronKey vkf skf
75+
76+
generateAndWriteKeyFiles :: ()
77+
=> Key keyrole
78+
=> AsType keyrole
79+
-> VerificationKeyFile
80+
-> SigningKeyFile
81+
-> ExceptT ShelleyAddressCmdError IO ()
82+
generateAndWriteKeyFiles asType vkf skf = do
83+
uncurry (writePaymentKeyFiles vkf skf) =<< generatePaymentKeys asType
84+
85+
generatePaymentKeys :: ()
86+
=> Key keyrole
87+
=> AsType keyrole
88+
-> ExceptT ShelleyAddressCmdError IO (VerificationKey keyrole, SigningKey keyrole)
89+
generatePaymentKeys asType = do
90+
skey <- liftIO $ generateSigningKey asType
91+
return (getVerificationKey skey, skey)
92+
93+
writePaymentKeyFiles :: ()
94+
=> Key keyrole
95+
=> VerificationKeyFile
96+
-> SigningKeyFile
97+
-> VerificationKey keyrole
98+
-> SigningKey keyrole
99+
-> ExceptT ShelleyAddressCmdError IO ()
100+
writePaymentKeyFiles (VerificationKeyFile vkeyPath) (SigningKeyFile skeyPath) vkey skey = do
101+
firstExceptT ShelleyAddressCmdWriteFileError $ do
102+
newExceptT $ writeFileTextEnvelope skeyPath (Just skeyDesc) skey
103+
newExceptT $ writeFileTextEnvelope vkeyPath (Just vkeyDesc) vkey
76104
where
77-
generateAndWriteKeyFiles asType = do
78-
skey <- liftIO $ generateSigningKey asType
79-
let vkey = getVerificationKey skey
80-
firstExceptT ShelleyAddressCmdWriteFileError
81-
. newExceptT
82-
$ writeFileTextEnvelope skeyPath (Just skeyDesc) skey
83-
firstExceptT ShelleyAddressCmdWriteFileError
84-
. newExceptT
85-
$ writeFileTextEnvelope vkeyPath (Just vkeyDesc) vkey
86-
87105
skeyDesc, vkeyDesc :: TextEnvelopeDescr
88106
skeyDesc = "Payment Signing Key"
89107
vkeyDesc = "Payment Verification Key"
90108

91-
92109
runAddressKeyHash :: VerificationKeyTextOrFile
93110
-> Maybe OutputFile
94111
-> ExceptT ShelleyAddressCmdError IO ()

cardano-cli/src/Cardano/CLI/Shelley/Run/Genesis.hs

+76-71
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ import Cardano.CLI.Shelley.Run.Node (ShelleyNodeCmdError (..), renderS
8080
runNodeIssueOpCert, runNodeKeyGenCold, runNodeKeyGenKES, runNodeKeyGenVRF)
8181
import Cardano.CLI.Shelley.Run.Pool (ShelleyPoolCmdError (..), renderShelleyPoolCmdError)
8282
import Cardano.CLI.Shelley.Run.StakeAddress (ShelleyStakeAddressCmdError (..),
83-
renderShelleyStakeAddressCmdError, runStakeAddressKeyGenToFile, keyGenStakeAddress)
83+
renderShelleyStakeAddressCmdError, runStakeAddressKeyGenToFile)
8484
import Cardano.CLI.Types
8585

8686
import Cardano.CLI.Byron.Delegation
@@ -703,23 +703,46 @@ runGenesisCreateStaked (GenesisDir rootdir)
703703
]
704704

705705
-- Distribute M delegates across N pools:
706-
delegations :: [Delegation] <- forM distribution $ \(poolParams, index) -> do
706+
delegations <- liftIO $ Lazy.forM distribution $ \(poolParams, index) -> do
707707
computeDelegation network stdeldir poolParams index
708708

709+
liftIO $ LBS.writeFile (stdeldir </> "delegations.jsonl") $ B.toLazyByteString $
710+
mconcat (List.intersperse "\n" (B.lazyByteString . Aeson.encode <$> delegations))
711+
712+
-- NOTE The following code which reads from the same file 'delegations.jsonl' multiple times
713+
-- looks like duplication, but it is not. The file is read lazily, and it is important that
714+
-- they be read multiple times because the code is streaming and reading the file multiple
715+
-- times ensures that any data structures that are created as a result of the read is not
716+
-- retained in memory.
717+
718+
!numDelegations <- fmap length $ liftIO $ LBS.lines <$> LBS.readFile (stdeldir </> "delegations.jsonl")
719+
720+
delegations2 <- do
721+
delegationLines <- liftIO $ LBS.lines <$> LBS.readFile (stdeldir </> "delegations.jsonl")
722+
return $ catMaybes $ Aeson.decode @Delegation <$> delegationLines
723+
724+
delegations3 <- do
725+
delegationLines <- liftIO $ LBS.lines <$> LBS.readFile (stdeldir </> "delegations.jsonl")
726+
return $ catMaybes $ Aeson.decode @Delegation <$> delegationLines
727+
728+
delegations4 <- do
729+
delegationLines <- liftIO $ LBS.lines <$> LBS.readFile (stdeldir </> "delegations.jsonl")
730+
return $ catMaybes $ Aeson.decode @Delegation <$> delegationLines
731+
709732
genDlgs <- readGenDelegsMap gendir deldir
710733
nonDelegAddrs <- readInitialFundAddresses utxodir network
711734
start <- maybe (SystemStart <$> getCurrentTimePlus30) pure mStart
712735

713736
stuffedUtxoAddrs <- liftIO $ Lazy.replicateM (fromIntegral numStuffedUtxo) genStuffedAddress
714737

715-
let poolMap :: Map (Ledger.KeyHash Ledger.Staking StandardCrypto) (Ledger.PoolParams StandardCrypto)
716-
poolMap = Map.fromList $ mkDelegationMapEntry <$> delegations
717-
delegAddrs = dInitialUtxoAddr <$> delegations
738+
let stake = second Ledger._poolId . mkDelegationMapEntry <$> delegations2
739+
stakePools = [ (Ledger._poolId poolParams, poolParams) | poolParams <- snd . mkDelegationMapEntry <$> delegations3 ]
740+
delegAddrs = dInitialUtxoAddr <$> delegations4
718741
!shelleyGenesis =
719742
updateOutputTemplate
720743
-- Shelley genesis parameters
721-
start genDlgs mNonDlgAmount nonDelegAddrs poolMap
722-
stDlgAmount delegAddrs stuffedUtxoAddrs (toOutputTemplate template)
744+
start genDlgs mNonDlgAmount (length nonDelegAddrs) nonDelegAddrs stakePools stake
745+
stDlgAmount numDelegations delegAddrs stuffedUtxoAddrs (toOutputTemplate template)
723746

724747
-- shelleyGenesis contains lazy loaded data, so using lazyToJson to serialise to avoid
725748
-- retaining large datastructures in memory.
@@ -735,9 +758,7 @@ runGenesisCreateStaked (GenesisDir rootdir)
735758
, textShow genNumUTxOKeys, " non-delegating UTxO keys, "
736759
, textShow genNumPools, " stake pools, "
737760
, textShow genNumStDelegs, " delegating UTxO keys, "
738-
, textShow (length delegations), " delegation relationships, "
739-
, textShow (Map.size poolMap), " delegation map entries, "
740-
, textShow (length delegAddrs), " delegating addresses"
761+
, textShow numDelegations, " delegation map entries, "
741762
] ++
742763
[ mconcat
743764
[ ", "
@@ -855,12 +876,24 @@ createPoolCredentials dir index = do
855876
coldSK = SigningKeyFile $ dir </> "cold" ++ strIndex ++ ".skey"
856877
opCertCtr = OpCertCounterFile $ dir </> "opcert" ++ strIndex ++ ".counter"
857878

858-
data Delegation
859-
= Delegation
860-
{ dInitialUtxoAddr :: AddressInEra ShelleyEra
861-
, dDelegStaking :: Ledger.KeyHash Ledger.Staking StandardCrypto
862-
, dPoolParams :: Ledger.PoolParams StandardCrypto
863-
}
879+
data Delegation = Delegation
880+
{ dInitialUtxoAddr :: AddressInEra ShelleyEra
881+
, dDelegStaking :: Ledger.KeyHash Ledger.Staking StandardCrypto
882+
, dPoolParams :: Ledger.PoolParams StandardCrypto
883+
}
884+
885+
instance ToJSON Delegation where
886+
toJSON delegation = Aeson.object
887+
[ "initialUtxoAddr" .= dInitialUtxoAddr delegation
888+
, "delegStaking" .= dDelegStaking delegation
889+
, "poolParams" .= dPoolParams delegation
890+
]
891+
892+
instance FromJSON Delegation where
893+
parseJSON = Aeson.withObject "Delegation" $ \v -> Delegation
894+
<$> (v .: "initialUtxoAddr")
895+
<*> (v .: "delegStaking")
896+
<*> (v .: "poolParams")
864897

865898
buildPool :: NetworkId -> FilePath -> Word -> ExceptT ShelleyGenesisCmdError IO (Ledger.PoolParams StandardCrypto)
866899
buildPool nw dir index = do
@@ -924,34 +957,13 @@ computeDelegation :: ()
924957
-> FilePath
925958
-> Ledger.PoolParams StandardCrypto
926959
-> Word
927-
-> ExceptT ShelleyGenesisCmdError IO Delegation
928-
computeDelegation nw delegDir pool delegIx = do
929-
let strIndex = show delegIx
930-
931-
let paymentVKF = VerificationKeyFile $ delegDir </> "payment" ++ strIndex ++ ".vkey"
932-
933-
firstExceptT ShelleyGenesisCmdAddressCmdError $ do
934-
let paymentSKF = SigningKeyFile $ delegDir </> "payment" ++ strIndex ++ ".skey"
935-
runAddressKeyGenToFile AddressKeyShelley paymentVKF paymentSKF
936-
937-
let stakingVKF = VerificationKeyFile $ delegDir </> "staking" ++ strIndex ++ ".vkey"
938-
939-
(_, stakeVK) <- firstExceptT ShelleyGenesisCmdStakeAddressCmdError $ do
940-
-- let stakingSK = SigningKeyFile $ delegDir </> "staking" ++ strIndex ++ ".skey"
941-
-- runStakeAddressKeyGenToFile stakingVKF stakingSK
942-
keyGenStakeAddress
960+
-> IO Delegation
961+
computeDelegation nw _delegDir pool _delegIx = do
962+
paymentVK <- fmap getVerificationKey $ generateSigningKey AsPaymentKey
963+
stakeVK <- fmap getVerificationKey $ generateSigningKey AsStakeKey
943964

944-
paySVK <- firstExceptT (ShelleyGenesisCmdAddressCmdError
945-
. ShelleyAddressCmdVerificationKeyTextOrFileError) $
946-
readAddressVerificationKeyTextOrFile
947-
(VktofVerificationKeyFile paymentVKF)
948-
949-
initialUtxoAddr <- case paySVK of
950-
APaymentVerificationKey payVK -> do
951-
firstExceptT ShelleyGenesisCmdAddressCmdError $ do
952-
let stakeVerifier = StakeVerifierKey . VerificationKeyFilePath $ stakingVKF
953-
makeShelleyAddress nw (PaymentCredentialByKey (verificationKeyHash payVK)) <$> makeStakeAddressRef stakeVerifier
954-
_ -> left $ ShelleyGenesisCmdUnexpectedAddressVerificationKey paymentVKF "APaymentVerificationKey" paySVK
965+
let stakeAddressReference = StakeAddressByValue . StakeCredentialByKey . verificationKeyHash $ stakeVK
966+
let initialUtxoAddr = makeShelleyAddress nw (PaymentCredentialByKey (verificationKeyHash paymentVK)) stakeAddressReference
955967

956968
pure Delegation
957969
{ dInitialUtxoAddr = shelleyAddressInEra initialUtxoAddr
@@ -1084,17 +1096,20 @@ updateOutputTemplate
10841096
-> Map (Hash GenesisKey) (Hash GenesisDelegateKey, Hash VrfKey)
10851097
-- Non-delegated initial UTxO spec:
10861098
-> Maybe Lovelace
1099+
-> Int
10871100
-> [AddressInEra ShelleyEra]
10881101
-- Genesis staking: pools/delegation map & delegated initial UTxO spec:
1089-
-> Map (Ledger.KeyHash 'Ledger.Staking StandardCrypto) (Ledger.PoolParams StandardCrypto)
1102+
-> [(Ledger.KeyHash 'Ledger.StakePool StandardCrypto, Ledger.PoolParams StandardCrypto)]
1103+
-> [(Ledger.KeyHash 'Ledger.Staking StandardCrypto, Ledger.KeyHash 'Ledger.StakePool StandardCrypto)]
10901104
-> Lovelace
1091-
-> [AddressInEra ShelleyEra]
1105+
-> Int
1106+
-> [AddressInEra ShelleyEra] --
10921107
-> [AddressInEra ShelleyEra]
10931108
-> OT.OutputShelleyGenesis StandardShelley
10941109
-> OT.OutputShelleyGenesis StandardShelley
10951110
updateOutputTemplate (SystemStart start)
1096-
genDelegMap mAmountNonDeleg utxoAddrsNonDeleg
1097-
poolSpecs (Lovelace amountDeleg) utxoAddrsDeleg stuffedUtxoAddrs
1111+
genDelegMap mAmountNonDeleg nUtxoAddrsNonDeleg utxoAddrsNonDeleg
1112+
pools stake (Lovelace amountDeleg) nUtxoAddrsDeleg utxoAddrsDeleg stuffedUtxoAddrs
10981113
template = do
10991114

11001115
let pparamsFromTemplate = OT.sgProtocolParams template
@@ -1105,15 +1120,16 @@ updateOutputTemplate (SystemStart start)
11051120
, OT.sgInitialFunds = ListMap
11061121
[ (toShelleyAddr addr, toShelleyLovelace v)
11071122
| (addr, v) <-
1108-
distribute (nonDelegCoin - subtractForTreasury) utxoAddrsNonDeleg ++
1109-
distribute (delegCoin - subtractForTreasury) utxoAddrsDeleg ++
1110-
mkStuffedUtxo stuffedUtxoAddrs ]
1123+
distribute (nonDelegCoin - subtractForTreasury) nUtxoAddrsNonDeleg utxoAddrsNonDeleg
1124+
++
1125+
distribute (delegCoin - subtractForTreasury) nUtxoAddrsDeleg utxoAddrsDeleg
1126+
++
1127+
mkStuffedUtxo stuffedUtxoAddrs
1128+
]
11111129
, OT.sgStaking =
1112-
ShelleyGenesisStaking
1113-
{ sgsPools = Map.fromList
1114-
[ (Ledger._poolId poolParams, poolParams)
1115-
| poolParams <- Map.elems poolSpecs ]
1116-
, sgsStake = Ledger._poolId <$> poolSpecs
1130+
OT.OutputShelleyGenesisStaking
1131+
{ OT.osgsPools = ListMap pools
1132+
, OT.osgsStake = ListMap stake
11171133
}
11181134
, OT.sgProtocolParams = pparamsFromTemplate
11191135
}
@@ -1128,22 +1144,11 @@ updateOutputTemplate (SystemStart start)
11281144
nonDelegCoin = fromIntegral (fromMaybe maximumLovelaceSupply (unLovelace <$> mAmountNonDeleg))
11291145
delegCoin = fromIntegral amountDeleg
11301146

1131-
distribute :: Integer -> [AddressInEra ShelleyEra] -> [(AddressInEra ShelleyEra, Lovelace)]
1132-
distribute funds addrs =
1133-
fst $ List.foldl' folder ([], fromIntegral funds) addrs
1134-
where
1135-
nAddrs, coinPerAddr, splitThreshold :: Integer
1136-
nAddrs = fromIntegral $ length addrs
1137-
coinPerAddr = funds `div` nAddrs
1138-
splitThreshold = coinPerAddr + nAddrs
1139-
1140-
folder :: ([(AddressInEra ShelleyEra, Lovelace)], Integer)
1141-
-> AddressInEra ShelleyEra
1142-
-> ([(AddressInEra ShelleyEra, Lovelace)], Integer)
1143-
folder (acc, rest) addr
1144-
| rest > splitThreshold =
1145-
((addr, Lovelace coinPerAddr) : acc, rest - coinPerAddr)
1146-
| otherwise = ((addr, Lovelace rest) : acc, 0)
1147+
distribute :: Integer -> Int -> [AddressInEra ShelleyEra] -> [(AddressInEra ShelleyEra, Lovelace)]
1148+
distribute funds nAddrs addrs = zipWith (,) addrs (fmap Lovelace (coinPerAddr + rest:repeat coinPerAddr))
1149+
where coinPerAddr :: Integer
1150+
coinPerAddr = funds `div` fromIntegral nAddrs
1151+
rest = coinPerAddr * fromIntegral nAddrs
11471152

11481153
mkStuffedUtxo :: [AddressInEra ShelleyEra] -> [(AddressInEra ShelleyEra, Lovelace)]
11491154
mkStuffedUtxo xs = (, Lovelace minUtxoVal) <$> xs

cardano-cli/src/Cardano/CLI/Shelley/Run/Genesis/LazyToJson.hs

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
{-# LANGUAGE LambdaCase #-}
21
{-# LANGUAGE FlexibleInstances #-}
2+
{-# LANGUAGE LambdaCase #-}
33

44
module Cardano.CLI.Shelley.Run.Genesis.LazyToJson
5-
( LazyToJson(..)
5+
( Aeson(..)
6+
, LazyToJson(..)
67
) where
78

89
import Cardano.Ledger.Address (Addr)
910
import Cardano.Ledger.Crypto (StandardCrypto)
10-
import Data.Aeson (Value)
11+
import Data.Aeson (Value, ToJSON)
1112
import Data.Functor ((<$>))
1213
import Data.Monoid (Monoid(..))
1314
import Data.Semigroup (Semigroup(..))
@@ -38,3 +39,8 @@ instance LazyToJson a => LazyToJson [a] where
3839

3940
instance LazyToJson (Addr StandardCrypto) where
4041
lazyToJson = B.lazyByteString . J.encode
42+
43+
newtype Aeson a = Aeson a
44+
45+
instance ToJSON a => LazyToJson (Aeson a) where
46+
lazyToJson (Aeson a) = B.lazyByteString (J.encode a)

cardano-cli/src/Cardano/CLI/Shelley/Run/Genesis/ListMap.hs

+12-17
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,18 @@ newtype ListMap k v = ListMap
2929
} deriving (Eq, Show)
3030

3131
instance ToJSONKey k => ToJSON1 (ListMap k) where
32-
liftToJSON g _ = case toJSONKey of
33-
ToJSONKeyText f _ -> Object . KM.fromList . unListMap . mapKeyValO f g
34-
ToJSONKeyValue f _ -> Array . V.fromList . L.map (toJSONPair f g) . unListMap
35-
36-
liftToEncoding g _ = case toJSONKey of
37-
ToJSONKeyText _ f -> dict f g (foldrWithKey . uncurry)
38-
ToJSONKeyValue _ f -> E.list (pairEncoding f) . unListMap
39-
where
40-
pairEncoding f (a, b) = E.list id [f a, g b]
32+
liftToJSON g _ = case toJSONKey of
33+
ToJSONKeyText f _ -> Object . KM.fromList . unListMap . mapKeyValO f g
34+
ToJSONKeyValue f _ -> Array . V.fromList . L.map (toJSONPair f g) . unListMap
35+
where mapKeyValO :: (k1 -> k2) -> (v1 -> v2) -> ListMap k1 v1 -> ListMap k2 v2
36+
mapKeyValO fk kv = ListMap . foldrWithKey (\(k, v) -> ((fk k, kv v):)) []
37+
toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
38+
toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b)
39+
40+
liftToEncoding g _ = case toJSONKey of
41+
ToJSONKeyText _ f -> dict f g (foldrWithKey . uncurry)
42+
ToJSONKeyValue _ f -> E.list (pairEncoding f) . unListMap
43+
where pairEncoding f (a, b) = E.list id [f a, g b]
4144

4245
instance (ToJSON v, ToJSONKey k) => ToJSON (ListMap k v) where
4346
toJSON = J.toJSON1
@@ -47,14 +50,6 @@ instance (ToJSON v, ToJSONKey k) => ToJSON (ListMap k v) where
4750
foldrWithKey :: ((k, a) -> b -> b) -> b -> ListMap k a -> b
4851
foldrWithKey f z = L.foldr f z . unListMap
4952

50-
-- | Transform the keys and values of a 'M.Map'.
51-
mapKeyValO :: (k1 -> k2) -> (v1 -> v2) -> ListMap k1 v1 -> ListMap k2 v2
52-
mapKeyValO fk kv = ListMap . foldrWithKey (\(k, v) -> ((fk k, kv v):)) []
53-
{-# INLINE mapKeyValO #-}
54-
55-
toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
56-
toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b)
57-
5853
instance forall k v. (ToJSON k, ToJSON v) => LazyToJson (ListMap k v) where
5954
lazyToJson (ListMap kvs) = "{" <> mconcat (L.intersperse "," (elementLazyToJson <$> kvs)) <> "}"
6055
where elementLazyToJson :: (k, v) -> B.Builder

0 commit comments

Comments
 (0)