Skip to content

Commit 41abca8

Browse files
authored
Merge pull request #5241 from IntersectMBO/lehins/fix-CostModel-json-parsing
Fix JSON parsing for CostModels
2 parents 3f12258 + 2e9055e commit 41abca8

File tree

10 files changed

+123
-45
lines changed

10 files changed

+123
-45
lines changed

eras/alonzo/impl/src/Cardano/Ledger/Alonzo/Genesis.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import Cardano.Ledger.Binary.Coders (
5050
)
5151
import Cardano.Ledger.Core
5252
import Cardano.Ledger.Genesis (EraGenesis (..))
53+
import Cardano.Ledger.Plutus.CostModels (parseCostModels)
5354
import Control.DeepSeq (NFData)
5455
import Data.Aeson (FromJSON (..), ToJSON (..), (.:), (.=))
5556
import qualified Data.Aeson as Aeson
@@ -171,7 +172,7 @@ instance ToCBOR AlonzoGenesis where
171172
instance FromJSON AlonzoGenesis where
172173
parseJSON = Aeson.withObject "Alonzo Genesis" $ \o -> do
173174
agCoinsPerUTxOWord <- o .: "lovelacePerUTxOWord"
174-
agCostModels <- o .: "costModels"
175+
agCostModels <- parseCostModels False =<< o .: "costModels"
175176
agPrices <- o .: "executionPrices"
176177
agMaxTxExUnits <- o .: "maxTxExUnits"
177178
agMaxBlockExUnits <- o .: "maxBlockExUnits"

eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/CostModelsSpec.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ validCostModelProp = do
5757
ppuRes <- expectRight ppuDecoded
5858
ppuRes `shouldSatisfy` \ppu -> (validCm <$> ppu ^. ppuCostModelsL) == SJust True
5959
where
60-
genValidCostModelEnc lang = genCostModelEncForLanguage lang (costModelParamsCount lang)
60+
genValidCostModelEnc lang = genCostModelEncForLanguage lang (costModelInitParamCount lang)
6161
validCm cms =
6262
not (null (costModelsValid cms)) && null (costModelsUnknown cms)
6363

@@ -84,7 +84,7 @@ underspecifiedCostModelProp = do
8484
cmRes `shouldSatisfy` not . null . costModelsValid
8585
where
8686
genUnderspecifiedCostModelEnc lang = do
87-
let validCount = costModelParamsCount lang
87+
let validCount = costModelInitParamCount lang
8888
count <- choose (0, validCount - 1)
8989
genCostModelEncForLanguage lang count
9090

eras/conway/impl/golden/pparams-update.json

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,27 @@
233233
-962328041173442759,
234234
-4075615882692725743,
235235
-3352890667792537221,
236-
6875093762849820680
236+
6875093762849820680,
237+
-6602342814594595381,
238+
5951863540083319718,
239+
-9200808190774891816,
240+
-3949262931707447679,
241+
-4457084894721383071,
242+
-9165687560670498575,
243+
5292299755808348356,
244+
6269357501932927815,
245+
-6707589547320281809,
246+
-304719936039206445,
247+
3393469372630712782,
248+
-7997979798890445295,
249+
-8267055408668194327,
250+
1496797700699732113,
251+
-4758699786657820125,
252+
8231926240690839101,
253+
8516908119870936999,
254+
-6261418126182035165,
255+
7651136103253405349,
256+
-6472673752314266831
237257
],
238258
"Unknown": {
239259
"10": [

eras/conway/impl/src/Cardano/Ledger/Conway/PParams.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ import Cardano.Ledger.Plutus.CostModels (
135135
CostModel,
136136
decodeCostModel,
137137
encodeCostModel,
138-
mkCostModel,
139138
mkCostModels,
139+
parseCostModelAsArray,
140140
)
141141
import Cardano.Ledger.Plutus.Language (Language (PlutusV3))
142142
import Cardano.Ledger.Plutus.ToPlutusData (ToPlutusData (..))
@@ -1079,7 +1079,7 @@ instance FromJSON (UpgradeConwayPParams Identity) where
10791079
<*> o .: "dRepDeposit"
10801080
<*> o .: "dRepActivity"
10811081
<*> o .: "minFeeRefScriptCostPerByte"
1082-
<*> (either (fail . show) pure . mkCostModel PlutusV3 =<< o .: "plutusV3CostModel")
1082+
<*> (parseCostModelAsArray False PlutusV3 =<< o .: "plutusV3CostModel")
10831083

10841084
upgradeConwayPParams ::
10851085
forall f.

eras/conway/impl/test/data/conway-genesis.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"dRepDeposit": 0,
2626
"dRepActivity": 0,
2727
"minFeeRefScriptCostPerByte": 0,
28-
"plutusV3CostModel": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
28+
"plutusV3CostModel": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
2929
"constitution": {
3030
"anchor": {
3131
"url": "",

libs/cardano-ledger-core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## 1.18.0.0
44

5+
* Deprecate `costModelParamsCount` in favor of `costModelInitParamCount`
6+
* Add `costModelInitParamNames`, `costModelInitParamCount`, `parseCostModelAsArray` and `parseCostModelAsMap`
57
* Export `credToDRep` and `dRepToCred`
68
* Deprecate `PoolParams` in favor of `StakePoolState`. #5196
79
* Move the `PoolParams` module to `Cardano.Ledger.State.StakePool` and export from there.

libs/cardano-ledger-core/src/Cardano/Ledger/Plutus/CostModels.hs

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,13 @@
11
{-# LANGUAGE DataKinds #-}
2-
{-# LANGUAGE DeriveAnyClass #-}
3-
{-# LANGUAGE DeriveFunctor #-}
42
{-# LANGUAGE DeriveGeneric #-}
53
{-# LANGUAGE DerivingVia #-}
64
{-# LANGUAGE FlexibleContexts #-}
75
{-# LANGUAGE FlexibleInstances #-}
8-
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9-
{-# LANGUAGE LambdaCase #-}
106
{-# LANGUAGE MultiParamTypeClasses #-}
11-
{-# LANGUAGE NamedFieldPuns #-}
127
{-# LANGUAGE OverloadedStrings #-}
13-
{-# LANGUAGE PatternSynonyms #-}
148
{-# LANGUAGE ScopedTypeVariables #-}
15-
{-# LANGUAGE StandaloneDeriving #-}
169
{-# LANGUAGE TypeApplications #-}
1710
{-# LANGUAGE TypeFamilies #-}
18-
{-# LANGUAGE TypeOperators #-}
1911
{-# LANGUAGE UndecidableInstances #-}
2012

2113
module Cardano.Ledger.Plutus.CostModels (
@@ -33,12 +25,17 @@ module Cardano.Ledger.Plutus.CostModels (
3325
costModelFromMap,
3426
costModelParamsCount,
3527
decodeCostModel,
28+
costModelInitParamNames,
29+
costModelInitParamCount,
30+
parseCostModelAsMap,
31+
parseCostModelAsArray,
3632

3733
-- * Cost Models
3834
CostModels,
3935
mkCostModels,
4036
emptyCostModels,
4137
updateCostModels,
38+
parseCostModels,
4239
decodeCostModelsLenient,
4340
decodeCostModelsFailing,
4441
costModelsValid,
@@ -63,9 +60,10 @@ import Cardano.Ledger.Plutus.Language (
6360
nonNativeLanguages,
6461
)
6562
import Control.DeepSeq (NFData (..), deepseq)
66-
import Control.Monad (forM, when)
63+
import Control.Monad (forM, unless, when)
6764
import Control.Monad.Trans.Writer (WriterT (runWriterT))
6865
import Data.Aeson (
66+
Array,
6967
FromJSON (..),
7068
Object,
7169
ToJSON (..),
@@ -137,36 +135,72 @@ instance NFData CostModel where
137135
rnf (CostModel lang cm ectx) = lang `deepseq` cm `deepseq` rnf ectx
138136

139137
instance FromJSON CostModels where
140-
parseJSON = withObject "CostModels" $ \o -> do
141-
cms <- mapM (parseCostModel o) nonNativeLanguages
138+
parseJSON = parseCostModels True
139+
140+
parseCostModels ::
141+
-- | Do not restrict number of parameters to the initial count and allow parsing of cost models
142+
-- for unknown plutus versions.
143+
Bool ->
144+
Value ->
145+
Parser CostModels
146+
parseCostModels isLenient =
147+
withObject "CostModels" $ \o -> do
148+
cms <- mapM (parseCostModel isLenient o) nonNativeLanguages
142149
let cmsMap = Map.fromList [(cmLanguage cm, cm) | Just cm <- cms]
143-
unknown <- o .:? "Unknown" .!= mempty
144-
unknownCostModels <- mkCostModelsLenient unknown
150+
unknownCostModels <-
151+
if isLenient
152+
then do
153+
unknown <- o .:? "Unknown" .!= mempty
154+
mkCostModelsLenient unknown
155+
else
156+
pure mempty
145157
pure $ mkCostModels cmsMap <> unknownCostModels
146158

147-
-- | The costmodel parameters in Alonzo Genesis are represented as a map. Plutus API does
148-
-- no longer require the map as a parameter to `mkEvaluationContext`, but the list of
149-
-- integers representing the values of the map. The expectation on this list of integers
150-
-- is that they are sorted in the order given by the `ParamName` enum, so even though we
151-
-- just have to pass the list to plutus, we still need to use the names of the parameters
152-
-- in order to sort the list. In new versions, we want to represent the costmodel
153-
-- parameters directly as a list, so we can avoid this reordering.
154-
parseCostModel :: Object -> Language -> Parser (Maybe CostModel)
155-
parseCostModel o lang = do
159+
-- | The costmodel parameters in Alonzo Genesis are represented as a map. Plutus API does no longer
160+
-- require the map as a parameter to `mkEvaluationContext`, but the list of integers representing
161+
-- the values of the map. The expectation on this list of integers is that they are sorted in the
162+
-- order given by the `ParamName` enum, so even though we just have to pass the list to plutus, we
163+
-- still need to use the names of the parameters in order to sort the list. In new versions, we
164+
-- represent the costmodel parameters directly as a list, so we can avoid this reordering.
165+
parseCostModel :: Bool -> Object -> Language -> Parser (Maybe CostModel)
166+
parseCostModel isLenient o lang = do
156167
plutusCostModelValueMaybe <- o .:? fromString (show lang)
157168
forM plutusCostModelValueMaybe $ \plutusCostModelValue ->
158169
case plutusCostModelValue of
159-
Object _ -> costModelFromMap lang =<< parseJSON plutusCostModelValue
160-
Array _ -> validateCostModel lang =<< parseJSON plutusCostModelValue
170+
Object m -> parseCostModelAsMap isLenient lang m
171+
Array a -> parseCostModelAsArray isLenient lang a
161172
_ -> fail $ "Expected either an Array or an Object, but got: " ++ show plutusCostModelValue
162173

174+
parseCostModelAsMap :: Bool -> Language -> Object -> Parser CostModel
175+
parseCostModelAsMap isLenient lang m = do
176+
costModel <- costModelFromMap lang =<< parseJSON (Object m)
177+
unless isLenient $ guardNumberOfParameters lang m
178+
pure costModel
179+
180+
parseCostModelAsArray :: Bool -> Language -> Array -> Parser CostModel
181+
parseCostModelAsArray isLenient lang a = do
182+
costModel <- validateCostModel lang =<< parseJSON (Array a)
183+
unless isLenient $ guardNumberOfParameters lang a
184+
pure costModel
185+
186+
guardNumberOfParameters :: (Foldable f, MonadFail m) => Language -> f a -> m ()
187+
guardNumberOfParameters lang ps =
188+
let suppliedParameterCount = length ps
189+
expectedParameterCount = costModelInitParamCount lang
190+
in unless (suppliedParameterCount == expectedParameterCount) $
191+
fail $
192+
"Number of parameters supplied "
193+
<> show suppliedParameterCount
194+
<> " does not match the expected number of "
195+
<> show expectedParameterCount
196+
163197
costModelFromMap :: MonadFail m => Language -> Map Text Int64 -> m CostModel
164198
costModelFromMap lang cmMap =
165199
either (fail . unlines . (header :) . NE.toList) (validateCostModel lang) $
166200
validationToEither (traverse lookupFail paramNames)
167201
where
168202
header = "Cost model language: " ++ show lang
169-
paramNames = costModelParamNames lang
203+
paramNames = costModelInitParamNames lang
170204
lookupFail paramName =
171205
case Map.lookup paramName cmMap of
172206
Nothing -> failure $ " Parameter name missing from cost model: " ++ show paramName
@@ -180,6 +214,24 @@ costModelParamNames :: Language -> [Text]
180214
costModelParamNames PlutusV1 = plutusV1ParamNames
181215
costModelParamNames lang = plutusVXParamNames lang
182216

217+
-- | List of parameter names as when they were introduced upon a hard fork to a specific era for a
218+
-- corresponding plutus version.
219+
costModelInitParamNames :: Language -> [Text]
220+
costModelInitParamNames lang = take (costModelInitParamCount lang) $ costModelParamNames lang
221+
222+
-- | Number of `CostModel` parameters for a specified plutus version as when it was initially
223+
-- added. This is useful for genesis files, which shouldn't have the number of parameters vary over
224+
-- time.
225+
costModelInitParamCount :: Language -> Int
226+
costModelInitParamCount lang =
227+
case lang of
228+
PlutusV1 -> 166
229+
PlutusV2 -> 175
230+
PlutusV3 -> 251
231+
PlutusV4 ->
232+
-- This number will continue to change until we are ready to hard fork into Dijkstra era
233+
251
234+
183235
-- | There is a difference in 6 parameter names between the ones appearing alonzo genesis
184236
-- files and the values returned by plutus via `P.showParamName` on the `ParamName` enum.
185237
-- This listed is sorted in the order given by `ParamName` enum, so we can use it to sort
@@ -264,6 +316,10 @@ costModelParamsCount PlutusV1 = 166
264316
costModelParamsCount PlutusV2 = 175
265317
costModelParamsCount PlutusV3 = 231
266318
costModelParamsCount PlutusV4 = 231
319+
{-# DEPRECATED
320+
costModelParamsCount
321+
"Deprecated in favor of `costModelInitParamCount`, since this function provided an incorrect value of 231 for PlutusV3, where it should have been 251"
322+
#-}
267323

268324
decodeCostModelLegacy :: Language -> Decoder s CostModel
269325
decodeCostModelLegacy lang = do
@@ -272,7 +328,7 @@ decodeCostModelLegacy lang = do
272328
"Legacy CostModel decoding is not supported for " ++ show lang ++ " language version"
273329
values <- decCBOR
274330
let numValues = length values
275-
expectedNumValues = costModelParamsCount lang
331+
expectedNumValues = costModelInitParamCount lang
276332
when (numValues /= expectedNumValues) $
277333
fail $
278334
"Expected array with "

libs/cardano-ledger-core/testlib/Test/Cardano/Ledger/Core/Arbitrary.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ import Cardano.Ledger.Keys (BootstrapWitness (..), ChainCode (..), VKey (..), Wi
8686
import Cardano.Ledger.Plutus.CostModels (
8787
CostModel,
8888
CostModels,
89-
costModelParamsCount,
89+
costModelInitParamCount,
9090
mkCostModel,
9191
mkCostModels,
9292
mkCostModelsLenient,
@@ -910,7 +910,7 @@ instance Arbitrary PV1.Data where
910910

911911
genValidCostModel :: Language -> Gen CostModel
912912
genValidCostModel lang = do
913-
newParamValues <- vectorOf (costModelParamsCount lang) arbitrary
913+
newParamValues <- vectorOf (costModelInitParamCount lang) arbitrary
914914
either (\err -> error $ "Corrupt cost model: " ++ show err) pure $
915915
mkCostModel lang newParamValues
916916

@@ -953,14 +953,14 @@ genUnknownCostModelValues = do
953953
genCostModelValues :: Language -> Gen (Word8, [Int64])
954954
genCostModelValues lang = do
955955
Positive sub <- arbitrary
956-
(,) lang'
956+
(,) langWord8
957957
<$> oneof
958-
[ listAtLeast (costModelParamsCount lang) -- Valid Cost Model for known language
958+
[ listAtLeast (costModelInitParamCount lang) -- Valid Cost Model for known language
959959
, take (tooFew sub) <$> arbitrary -- Invalid Cost Model for known language
960960
]
961961
where
962-
lang' = fromIntegral (fromEnum lang)
963-
tooFew sub = costModelParamsCount lang - sub
962+
langWord8 = fromIntegral (fromEnum lang)
963+
tooFew sub = costModelInitParamCount lang - sub
964964
listAtLeast :: Int -> Gen [Int64]
965965
listAtLeast x = do
966966
NonNegative y <- arbitrary

libs/cardano-ledger-core/testlib/Test/Cardano/Ledger/Core/JSON.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,15 @@ roundTripJsonEraSpec =
6666

6767
goldenJsonPParamsSpec ::
6868
forall era.
69-
EraPParams era =>
69+
(HasCallStack, EraPParams era) =>
7070
SpecWith FilePath
7171
goldenJsonPParamsSpec =
72-
it "Golden JSON specs for PParams " $
72+
it "Golden JSON specs for PParams" $
7373
eitherDecodeFileStrict @(PParams era) >=> expectRightDeepExpr_
7474

7575
goldenJsonPParamsUpdateSpec ::
7676
forall era.
77-
EraTest era =>
77+
(HasCallStack, EraTest era) =>
7878
SpecWith FilePath
7979
goldenJsonPParamsUpdateSpec =
8080
it "Golden JSON specs for PParamsUpdate" $ \file -> do

libs/cardano-ledger-core/testlib/Test/Cardano/Ledger/Plutus.hs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{-# LANGUAGE GADTs #-}
22
{-# LANGUAGE LambdaCase #-}
3-
{-# LANGUAGE TypeApplications #-}
43

54
module Test.Cardano.Ledger.Plutus (
65
PlutusArgs (..),
@@ -32,7 +31,7 @@ import Cardano.Ledger.Binary.Plain (decodeFullFromHexText)
3231
import Cardano.Ledger.Plutus.CostModels (
3332
CostModel,
3433
CostModels,
35-
costModelParamsCount,
34+
costModelInitParamCount,
3635
getCostModelEvaluationContext,
3736
mkCostModel,
3837
mkCostModels,
@@ -62,7 +61,7 @@ import Test.Cardano.Ledger.Plutus.ScriptTestContext (
6261

6362
-- | Construct a test cost model where all parameters are set to the same value
6463
mkCostModelConst :: HasCallStack => Language -> Int64 -> CostModel
65-
mkCostModelConst lang = mkCostModel' lang . replicate (costModelParamsCount lang)
64+
mkCostModelConst lang = mkCostModel' lang . replicate (costModelInitParamCount lang)
6665

6766
mkCostModel' :: (Integral i, Show i, HasCallStack) => Language -> [i] -> CostModel
6867
mkCostModel' lang params =

0 commit comments

Comments
 (0)