This repository was archived by the owner on Aug 18, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 631
/
Copy pathConfiguration.hs
311 lines (260 loc) · 11.9 KB
/
Configuration.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
module Pos.Core.Configuration
( Config (..)
, configK
, configVssMinTTL
, configVssMaxTTL
, configBlkSecurityParam
, configSlotSecurityParam
, configChainQualityThreshold
, configEpochSlots
, configGeneratedSecretsThrow
, configBootStakeholders
, configHeavyDelegation
, configStartTime
, configVssCerts
, configNonAvvmBalances
, configBlockVersionData
, configGenesisProtocolConstants
, configAvvmDistr
, configFtsSeed
, ConfigurationError (..)
, HasConfiguration
, withCoreConfigurations
, withGenesisSpec
, canonicalGenesisJson
, prettyGenesisJson
, module E
) where
import Universum
import Control.Exception (throwIO)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Data.Coerce (coerce)
import System.FilePath ((</>))
import System.IO.Error (userError)
import qualified Text.JSON.Canonical as Canonical
import Pos.Binary.Class (Raw)
import Pos.Core.Common (BlockCount, SharedSeed)
import Pos.Core.Configuration.Core as E
import Pos.Core.Configuration.GenesisHash as E
import Pos.Core.Genesis (GeneratedSecrets, GenesisAvvmBalances,
GenesisData (..), GenesisDelegation,
GenesisInitializer (..), GenesisNonAvvmBalances,
GenesisProtocolConstants (..), GenesisSpec (..),
GenesisVssCertificatesMap (..), GenesisWStakeholders,
genesisProtocolConstantsToProtocolConstants,
mkGenesisDelegation)
import Pos.Core.Genesis.Generate (GeneratedGenesisData (..),
generateGenesisData)
import Pos.Core.ProtocolConstants (ProtocolConstants (..),
pcBlkSecurityParam, pcChainQualityThreshold, pcEpochSlots,
pcSlotSecurityParam, vssMaxTTL, vssMinTTL)
import Pos.Core.Slotting (SlotCount, Timestamp)
import Pos.Core.Ssc (VssCertificatesMap)
import Pos.Core.Update (BlockVersionData)
import Pos.Crypto.Configuration as E
import Pos.Crypto.Hashing (Hash, hashRaw, unsafeHash)
import Pos.Util.Json.Canonical (SchemaError)
import Pos.Util.Util (leftToPanic)
data Config = Config
{ configProtocolMagic :: ProtocolMagic
, configProtocolConstants :: ProtocolConstants
, configGeneratedSecrets :: Maybe GeneratedSecrets
, configGenesisData :: GenesisData
, configGenesisHash :: GenesisHash
}
configK :: Config -> Int
configK = pcK . configProtocolConstants
configVssMinTTL :: Integral i => Config -> i
configVssMinTTL = vssMinTTL . configProtocolConstants
configVssMaxTTL :: Integral i => Config -> i
configVssMaxTTL = vssMaxTTL . configProtocolConstants
configBlkSecurityParam :: Config -> BlockCount
configBlkSecurityParam = pcBlkSecurityParam . configProtocolConstants
configSlotSecurityParam :: Config -> SlotCount
configSlotSecurityParam = pcSlotSecurityParam . configProtocolConstants
configChainQualityThreshold :: Fractional f => Config -> f
configChainQualityThreshold = pcChainQualityThreshold . configProtocolConstants
configEpochSlots :: Config -> SlotCount
configEpochSlots = pcEpochSlots . configProtocolConstants
configGeneratedSecretsThrow
:: (HasCallStack, MonadIO m) => Config -> m GeneratedSecrets
configGeneratedSecretsThrow =
maybe
(liftIO $ throwIO $ userError
"GeneratedSecrets missing from Core.Config"
)
pure
. configGeneratedSecrets
configBootStakeholders :: Config -> GenesisWStakeholders
configBootStakeholders = gdBootStakeholders . configGenesisData
configHeavyDelegation :: Config -> GenesisDelegation
configHeavyDelegation = gdHeavyDelegation . configGenesisData
configStartTime :: Config -> Timestamp
configStartTime = gdStartTime . configGenesisData
configVssCerts :: Config -> VssCertificatesMap
configVssCerts = getGenesisVssCertificatesMap . gdVssCerts . configGenesisData
configNonAvvmBalances :: Config -> GenesisNonAvvmBalances
configNonAvvmBalances = gdNonAvvmBalances . configGenesisData
configBlockVersionData :: Config -> BlockVersionData
configBlockVersionData = gdBlockVersionData . configGenesisData
configGenesisProtocolConstants :: Config -> GenesisProtocolConstants
configGenesisProtocolConstants = gdProtocolConsts . configGenesisData
configAvvmDistr :: Config -> GenesisAvvmBalances
configAvvmDistr = gdAvvmDistr . configGenesisData
configFtsSeed :: Config -> SharedSeed
configFtsSeed = gdFtsSeed . configGenesisData
-- | Coarse catch-all configuration constraint for use by depending modules.
type HasConfiguration = HasCoreConfiguration
canonicalGenesisJson :: GenesisData -> (BSL.ByteString, Hash Raw)
canonicalGenesisJson theGenesisData = (canonicalJsonBytes, jsonHash)
where
jsonHash = hashRaw canonicalJsonBytes
canonicalJsonBytes = Canonical.renderCanonicalJSON $ runIdentity $ Canonical.toJSON theGenesisData
-- | Encode 'GenesisData' in JSON format in a pretty way. JSON object
-- is the same as in canonical JSON, but formatting doesn't adhere to
-- canonical JSON rules.
prettyGenesisJson :: GenesisData -> String
prettyGenesisJson theGenesisData =
Canonical.prettyCanonicalJSON $
runIdentity $ Canonical.toJSON theGenesisData
-- | Come up with a HasConfiguration constraint using a Configuration.
-- The Configuration record can be parsed from JSON or Yaml, and used to
-- get a GenesisSpec, either from the file itself or from another file:
-- the canonical JSON encoding of a mainnet genesis.
--
-- If the canonical JSON source is given, then it will be hashed and checked
-- against expected hash (which is also part of configuration in this case).
--
-- If the configuration gives a testnet genesis spec, then a start time must
-- be provided, probably sourced from command line arguments.
withCoreConfigurations
:: forall m r.
( MonadThrow m
, MonadIO m
)
=> CoreConfiguration
-> (GenesisData -> GenesisData)
-- ^ Update @'GenesisData'@ before passing its parts to @'given'@.
-> FilePath
-- ^ Directory where 'configuration.yaml' is stored.
-> Maybe Timestamp
-- ^ Optional system start time.
-- It must be given when the genesis spec uses a testnet initializer.
-> Maybe Integer
-- ^ Optional seed which overrides one from testnet initializer if
-- provided.
-> (HasConfiguration => Config -> m r)
-> m r
withCoreConfigurations conf@CoreConfiguration{..} fn confDir mSystemStart mSeed act = case ccGenesis of
-- If a 'GenesisData' source file is given, we check its hash against the
-- given expected hash, parse it, and use the GenesisData to fill in all of
-- the obligations.
GCSrc fp expectedHash -> do
!bytes <- liftIO $ BS.readFile (confDir </> fp)
whenJust mSeed $ const $
throwM $ MeaninglessSeed
"Seed doesn't make sense when genesis data itself is provided"
gdataJSON <- case Canonical.parseCanonicalJSON (BSL.fromStrict bytes) of
Left str -> throwM $ GenesisDataParseFailure (fromString str)
Right it -> return it
theGenesisData <- case Canonical.fromJSON gdataJSON of
Left err -> throwM $ GenesisDataSchemaError err
Right it -> return $ fn it
let (_, theGenesisHash) = canonicalGenesisJson theGenesisData
pc = genesisProtocolConstantsToProtocolConstants (gdProtocolConsts theGenesisData)
pm = gpcProtocolMagic (gdProtocolConsts theGenesisData)
when (theGenesisHash /= expectedHash) $
throwM $ GenesisHashMismatch
(show theGenesisHash) (show expectedHash)
withCoreConfiguration conf $
act $
Config
{ configProtocolMagic = pm
, configProtocolConstants = pc
, configGeneratedSecrets = Nothing
, configGenesisData = theGenesisData
, configGenesisHash = GenesisHash $ coerce theGenesisHash
}
-- If a 'GenesisSpec' is given, we ensure we have a start time (needed if
-- it's a testnet initializer) and then make a 'GenesisData' from it.
GCSpec spec -> do
theSystemStart <- case mSystemStart of
Just it -> do
return it
Nothing -> throwM MissingSystemStartTime
-- Override seed if necessary
let overrideSeed :: Integer -> GenesisInitializer -> GenesisInitializer
overrideSeed newSeed gi = gi {giSeed = newSeed}
let theSpec = case mSeed of
Nothing -> spec
Just newSeed -> spec
{ gsInitializer = overrideSeed newSeed (gsInitializer spec)
}
let theConf = conf {ccGenesis = GCSpec theSpec}
withGenesisSpec theSystemStart theConf fn act
withGenesisSpec
:: Timestamp
-> CoreConfiguration
-> (GenesisData -> GenesisData)
-> (HasConfiguration => Config -> r)
-> r
withGenesisSpec theSystemStart conf@CoreConfiguration{..} fn val = case ccGenesis of
GCSrc {} -> error "withGenesisSpec called with GCSrc"
GCSpec spec ->
let
-- Generate
GeneratedGenesisData {..} =
generateGenesisData pm pc (gsInitializer spec) (gsAvvmDistr spec)
-- Unite with generated
finalHeavyDelegation :: GenesisDelegation
finalHeavyDelegation =
leftToPanic "withGenesisSpec" $ mkGenesisDelegation $
(toList $ gsHeavyDelegation spec) <> toList ggdDelegation
-- Construct the final value
theGenesisData = fn $
GenesisData
{ gdBootStakeholders = ggdBootStakeholders
, gdHeavyDelegation = finalHeavyDelegation
, gdStartTime = theSystemStart
, gdVssCerts = ggdVssCerts
, gdNonAvvmBalances = ggdNonAvvm
, gdBlockVersionData = gsBlockVersionData spec
, gdProtocolConsts = gsProtocolConstants spec
, gdAvvmDistr = ggdAvvm
, gdFtsSeed = gsFtsSeed spec
}
-- Anything will do for the genesis hash. A hash of "patak" was used
-- before, and so it remains.
theGenesisHash = GenesisHash $ coerce $ unsafeHash @Text "patak"
in withCoreConfiguration conf $
val $
Config
{ configProtocolMagic = pm
, configProtocolConstants = pc
, configGeneratedSecrets = Just ggdSecrets
, configGenesisData = theGenesisData
, configGenesisHash = theGenesisHash
}
where
pm = gpcProtocolMagic (gsProtocolConstants spec)
pc = genesisProtocolConstantsToProtocolConstants (gsProtocolConstants spec)
data ConfigurationError =
-- | A system start time must be given when a testnet genesis is used.
-- A mainnet genesis has this built-in, so it's not needed.
MissingSystemStartTime
-- | Must not give a custom system start time when using a mainnet
-- genesis.
| UnnecessarySystemStartTime
| GenesisDataParseFailure !Text
| GenesisDataSchemaError !SchemaError
-- | The GenesisData canonical JSON hash is different than expected.
| GenesisHashMismatch !Text !Text
| ConfigurationInternalError !Text
-- | Custom seed was provided, but it doesn't make sense.
| MeaninglessSeed !Text
deriving (Show)
instance Exception ConfigurationError