Skip to content

Commit

Permalink
Switch CostModel parameter type to Int64
Browse files Browse the repository at this point in the history
  • Loading branch information
lehins committed May 3, 2024
1 parent db60eed commit fae926d
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 38 deletions.
2 changes: 1 addition & 1 deletion eras/alonzo/impl/cddl-files/alonzo.cddl
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ costmdls = { * language => cost_model } ; New
; The values in the serialization are assumed to be ordered
; lexicographically by their correpsonding key value.
; See Plutus' `ParamName` for parameter ordering
cost_model = [ 166*166 int ] ; New
cost_model = [ 166*166 int64 ] ; New

transaction_metadatum =
{ * transaction_metadatum => transaction_metadatum }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import qualified Data.ByteString.Base16 as B16
import qualified Data.ByteString.Base16.Lazy as B16L
import qualified Data.ByteString.Lazy as BSL
import Data.Either (fromRight)
import Data.Int
import qualified Data.List.NonEmpty as NE
import qualified Data.Map.Strict as Map
import Data.Maybe (fromJust)
Expand Down Expand Up @@ -406,7 +407,7 @@ expectedCostModelV2 =
(error ("Error creating CostModel from known PlutusV2 parameters" <> show expectedPParams))
(mkCostModel PlutusV2 (expectedPParams ++ (replicate 9 0)))

expectedPParams :: [Integer]
expectedPParams :: [Int64]
expectedPParams =
[ 197209
, 0
Expand Down
4 changes: 2 additions & 2 deletions eras/babbage/impl/cddl-files/babbage.cddl
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,8 @@ language = 0 ; Plutus v1
/ 1 ; Plutus v2

costmdls =
{ ? 0 : [ 166*166 int ] ; Plutus v1
, ? 1 : [ 175*175 int ] ; Plutus v2
{ ? 0 : [ 166*166 int64 ] ; Plutus v1
, ? 1 : [ 175*175 int64 ] ; Plutus v2
}

transaction_metadatum =
Expand Down
8 changes: 4 additions & 4 deletions eras/conway/impl/cddl-files/conway.cddl
Original file line number Diff line number Diff line change
Expand Up @@ -502,10 +502,10 @@ potential_languages = 0 .. 255
; versions in the future.
;
costmdls =
{ ? 0 : [ 166* int ] ; Plutus v1, only 166 integers are used, but more are accepted (and ignored)
, ? 1 : [ 175* int ] ; Plutus v2, only 175 integers are used, but more are accepted (and ignored)
, ? 2 : [ 233* int ] ; Plutus v3, only 233 integers are used, but more are accepted (and ignored)
, ? 3 : [ int ] ; Any 8-bit unsigned number can be used as a key.
{ ? 0 : [ int64 ] ; Plutus v1
, ? 1 : [ int64 ] ; Plutus v2
, ? 2 : [ int64 ] ; Plutus v3
, ? 3 : [ int64 ] ; Any 8-bit unsigned number can be used as a key.
}

transaction_metadatum =
Expand Down
36 changes: 19 additions & 17 deletions libs/cardano-ledger-core/src/Cardano/Ledger/Plutus/CostModels.hs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import Data.Aeson (
)
import Data.Aeson.Key (fromString)
import Data.Aeson.Types (Parser)
import Data.Int
import Data.Map (Map)
import qualified Data.Map.Strict as Map
import Data.Text as T (Text, pack, unpack)
Expand All @@ -115,7 +116,7 @@ import qualified PlutusLedgerApi.V3 as PV3 (ParamName, mkEvaluationContext)
-- to hide the evaluation context.
data CostModel = CostModel
{ cmLanguage :: !Language
, cmValues :: ![Integer]
, cmValues :: ![Int64]
, cmEvalCtx :: !P.EvaluationContext
}
deriving (Generic)
Expand Down Expand Up @@ -149,7 +150,7 @@ instance FromJSON CostModels where

-- | The costmodel parameters in Alonzo Genesis are represented as a map. Plutus API does
-- no longer require the map as a parameter to `mkEvaluationContext`, but the list of
-- Integers representing the values of the map. The expectation on this list of Integers
-- integers representing the values of the map. The expectation on this list of integers
-- is that they are sorted in the order given by the `ParamName` enum, so even though we
-- just have to pass the list to plutus, we still need to use the names of the parameters
-- in order to sort the list. In new versions, we want to represent the costmodel
Expand All @@ -163,7 +164,7 @@ parseCostModel o lang = do
Array _ -> validateCostModel lang =<< parseJSON plutusCostModelValue
_ -> fail $ "Expected either an Array or an Object, but got: " ++ show plutusCostModelValue

costModelFromMap :: MonadFail m => Language -> Map Text Integer -> m CostModel
costModelFromMap :: MonadFail m => Language -> Map Text Int64 -> m CostModel
costModelFromMap lang cmMap =
mapM lookupFail paramNames >>= validateCostModel lang
where
Expand All @@ -173,7 +174,7 @@ costModelFromMap lang cmMap =
Nothing -> fail $ "Unrecognized cost model parameter name: " ++ show paramName
Just v -> pure v

costModelToMap :: CostModel -> Map Text Integer
costModelToMap :: CostModel -> Map Text Int64
costModelToMap cm =
Map.fromList $ zip (costModelParamNames (cmLanguage cm)) (cmValues cm)

Expand Down Expand Up @@ -205,15 +206,15 @@ plutusVXParamNames PlutusV1 = P.showParamName <$> [minBound .. maxBound :: PV1.P
plutusVXParamNames PlutusV2 = P.showParamName <$> [minBound .. maxBound :: PV2.ParamName]
plutusVXParamNames PlutusV3 = P.showParamName <$> [minBound .. maxBound :: PV3.ParamName]

validateCostModel :: MonadFail m => Language -> [Integer] -> m CostModel
validateCostModel :: MonadFail m => Language -> [Int64] -> m CostModel
validateCostModel lang cmps =
case mkCostModel lang cmps of
Left err -> fail $ show err
Right cm -> pure cm

-- | Convert cost model parameters to a cost model, making use of the
-- conversion function mkEvaluationContext from the Plutus API.
mkCostModel :: Language -> [Integer] -> Either P.CostModelApplyError CostModel
mkCostModel :: Language -> [Int64] -> Either P.CostModelApplyError CostModel
mkCostModel lang cm =
case eCostModel of
Right (evalCtx, _) -> Right (CostModel lang cm evalCtx)
Expand All @@ -225,12 +226,12 @@ mkCostModel lang cm =
PlutusV2 -> PV2.mkEvaluationContext
PlutusV3 -> PV3.mkEvaluationContext
eCostModel :: Either P.CostModelApplyError (P.EvaluationContext, [P.CostModelApplyWarn])
eCostModel = runWriterT (mkEvaluationContext cm)
eCostModel = runWriterT (mkEvaluationContext $ map toInteger cm)

getCostModelLanguage :: CostModel -> Language
getCostModelLanguage (CostModel lang _ _) = lang

getCostModelParams :: CostModel -> [Integer]
getCostModelParams :: CostModel -> [Int64]
getCostModelParams (CostModel _ cm _) = cm

getCostModelEvaluationContext :: CostModel -> P.EvaluationContext
Expand All @@ -250,13 +251,13 @@ decodeValidAndUnknownCostModels = do
validAndUnkonwnCms <- decodeMapByKey decCBOR decodeValidOrUnknownCm
pure $ Map.foldrWithKey addValidOrUnknownCm emptyCostModels validAndUnkonwnCms
where
decodeValidOrUnknownCm :: Word8 -> Decoder s (Either [Integer] CostModel)
decodeValidOrUnknownCm :: Word8 -> Decoder s (Either [Int64] CostModel)
decodeValidOrUnknownCm langW8 = do
case mkLanguageEnum (fromIntegral langW8) of
Just lang -> Right <$> decodeCostModelFailHard lang
Nothing -> Left <$> decCBOR

addValidOrUnknownCm :: Word8 -> Either [Integer] CostModel -> CostModels -> CostModels
addValidOrUnknownCm :: Word8 -> Either [Int64] CostModel -> CostModels -> CostModels
addValidOrUnknownCm langW8 unknownOrCm (CostModels validCms errors invalidCms) =
case unknownOrCm of
Left cmIds -> CostModels validCms errors (Map.insert langW8 cmIds invalidCms)
Expand Down Expand Up @@ -292,7 +293,8 @@ costModelParamsCount lang = length $ plutusVXParamNames lang
legacyDecodeCostModel :: Language -> Decoder s CostModel
legacyDecodeCostModel lang = do
when (lang > PlutusV2) $
fail $ "Legacy CostModel decoding is not supported for " ++ show lang ++ " language version"
fail $
"Legacy CostModel decoding is not supported for " ++ show lang ++ " language version"
values <- decCBOR
let numValues = length values
expectedNumValues = costModelParamsCount lang
Expand Down Expand Up @@ -376,7 +378,7 @@ instance NFData CostModelError
-- too few parameters (depending on the version) will result in an error.
-- 'CostModelApplyError' exists to collect these errors in the 'CostModels' type.
-- The 'CostModels' type itself needs to be flexible enough to accept any map
-- of 'Word8' to '[Integer]', so that cost models can be placed in the protocol parameters
-- of 'Word8' to '[Int64]', so that cost models can be placed in the protocol parameters
-- ahead of changes to the Plutus evaluation context. In this way, serializing a cost model,
-- updating software, and deserializing can result in errors going away.
--
Expand All @@ -386,7 +388,7 @@ instance NFData CostModelError
data CostModels = CostModels
{ _costModelsValid :: !(Map Language CostModel)
, _costModelsErrors :: !(Map Language CostModelError)
, _costModelsUnknown :: !(Map Word8 [Integer])
, _costModelsUnknown :: !(Map Word8 [Int64])
}
deriving stock (Eq, Ord, Show, Generic)

Expand All @@ -402,7 +404,7 @@ costModelsValid = _costModelsValid
costModelsErrors :: CostModels -> Map Language CostModelError
costModelsErrors = _costModelsErrors

costModelsUnknown :: CostModels -> Map Word8 [Integer]
costModelsUnknown :: CostModels -> Map Word8 [Int64]
costModelsUnknown = _costModelsUnknown

emptyCostModels :: CostModels
Expand Down Expand Up @@ -437,10 +439,10 @@ mkCostModels cms = CostModels cms mempty mempty
-- error is stored in 'costModelsErrors' and the cost model is stored in
-- 'costModelsUnknown'. Lastly, if the Plutus version is unknown, the cost model is also
-- stored in 'costModelsUnknown'.
mkCostModelsLenient :: Map Word8 [Integer] -> CostModels
mkCostModelsLenient :: Map Word8 [Int64] -> CostModels
mkCostModelsLenient = Map.foldrWithKey addRawCostModel (CostModels mempty mempty mempty)
where
addRawCostModel :: Word8 -> [Integer] -> CostModels -> CostModels
addRawCostModel :: Word8 -> [Int64] -> CostModels -> CostModels
addRawCostModel langW8 cmIds (CostModels validCMs errs invalidCMs) =
case mkLanguageEnum (fromIntegral langW8) of
Just lang ->
Expand All @@ -455,7 +457,7 @@ mkCostModelsLenient = Map.foldrWithKey addRawCostModel (CostModels mempty mempty
-- cost model values, with no distinction between valid and invalid cost models.
-- This is used for serialization, so that judgements about validity can be made
-- upon deserialization.
flattenCostModels :: CostModels -> Map Word8 [Integer]
flattenCostModels :: CostModels -> Map Word8 [Int64]
flattenCostModels (CostModels validCMs _ invalidCMs) =
Map.foldrWithKey (\lang cm -> Map.insert (languageToWord8 lang) (cmValues cm)) invalidCMs validCMs

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ instance ToPlutusData NonNegativeInterval where
fromPlutusData _ = Nothing

instance ToPlutusData CostModels where
toPlutusData cmdls = toPlutusData (flattenCostModels cmdls)
fromPlutusData x = mkCostModelsLenient <$> fromPlutusData x
toPlutusData cmdls = toPlutusData $ fmap (fmap toInteger) (flattenCostModels cmdls)
fromPlutusData x = mkCostModelsLenient . fmap (fmap fromInteger) <$> fromPlutusData x

instance ToPlutusData ExUnits where
toPlutusData (ExUnits a b) = List [toPlutusData a, toPlutusData b]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ instance Arbitrary CostModel where

genValidCostModel :: Language -> Gen CostModel
genValidCostModel lang = do
newParamValues <- vectorOf (costModelParamsCount lang) (arbitrary :: Gen Integer)
newParamValues <- vectorOf (costModelParamsCount lang) arbitrary
either (\err -> error $ "Corrupt cost model: " ++ show err) pure $
mkCostModel lang newParamValues

Expand Down Expand Up @@ -859,24 +859,24 @@ instance Arbitrary FlexibleCostModels where
let cms = known `Map.union` unknown
pure . FlexibleCostModels $ mkCostModelsLenient cms

genUnknownCostModels :: Gen (Map Word8 [Integer])
genUnknownCostModels :: Gen (Map Word8 [Int64])
genUnknownCostModels = Map.fromList <$> listOf genUnknownCostModelValues

genKnownCostModels :: Gen (Map Word8 [Integer])
genKnownCostModels :: Gen (Map Word8 [Int64])
genKnownCostModels = do
langs <- sublistOf nonNativeLanguages
cms <- mapM genCostModelValues langs
return $ Map.fromList cms

genUnknownCostModelValues :: Gen (Word8, [Integer])
genUnknownCostModelValues :: Gen (Word8, [Int64])
genUnknownCostModelValues = do
lang <- chooseInt (firstInvalid, fromIntegral (maxBound :: Word8))
vs <- arbitrary
return (fromIntegral . fromEnum $ lang, vs)
where
firstInvalid = fromEnum (maxBound :: Language) + 1

genCostModelValues :: Language -> Gen (Word8, [Integer])
genCostModelValues :: Language -> Gen (Word8, [Int64])
genCostModelValues lang = do
Positive sub <- arbitrary
(,) lang'
Expand All @@ -887,7 +887,7 @@ genCostModelValues lang = do
where
lang' = fromIntegral (fromEnum lang)
tooFew sub = costModelParamsCount lang - sub
listAtLeast :: Int -> Gen [Integer]
listAtLeast :: Int -> Gen [Int64]
listAtLeast x = do
NonNegative y <- arbitrary
replicateM (x + y) arbitrary
10 changes: 5 additions & 5 deletions libs/cardano-ledger-core/testlib/Test/Cardano/Ledger/Plutus.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,17 @@ mkCostModelConst lang x =
PlutusV2 -> mkCostModel' lang (x <$ PV2.costModelParamsForTesting)
PlutusV3 -> mkCostModel' lang (x <$ PV3.costModelParamsForTesting)

mkCostModel' :: HasCallStack => Language -> [Integer] -> CostModel
mkCostModel' :: (Integral i, Show i, HasCallStack) => Language -> [i] -> CostModel
mkCostModel' lang params =
case mkCostModel lang params of
case mkCostModel lang $ map fromIntegral params of
Left err ->
error $
"Number of CostModel parameters "
++ show (length params)
++ " is not well-formed for "
"CostModel parameters are not well-formed for "
++ show lang
++ ": "
++ show err
++ "\n"
++ show params
Right costModel -> costModel

-- | Test CostModels for all available languages with zero values for all parameters
Expand Down

0 comments on commit fae926d

Please sign in to comment.