diff --git a/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs b/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs index 7601a1b2097..5b122a2c9e3 100644 --- a/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs +++ b/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs @@ -110,6 +110,9 @@ builtinMemoryModels = BuiltinCostModelBase , paramHeadList = Id $ ModelOneArgumentConstantCost 32 , paramTailList = Id $ ModelOneArgumentConstantCost 32 , paramNullList = Id $ ModelOneArgumentConstantCost 32 + , paramLengthArray = Id $ ModelOneArgumentConstantCost 99 + , paramListToArray = Id $ ModelOneArgumentConstantCost 99 + , paramIndexArray = Id $ ModelTwoArgumentsConstantCost 99 , paramChooseData = Id $ ModelSixArgumentsConstantCost 32 , paramConstrData = Id $ ModelTwoArgumentsConstantCost 32 , paramMapData = Id $ ModelOneArgumentConstantCost 32 diff --git a/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs b/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs index fedbadcaf5a..0ca44503293 100644 --- a/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs +++ b/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs @@ -75,6 +75,9 @@ builtinCostModelNames = BuiltinCostModelBase , paramHeadList = "headListModel" , paramTailList = "tailListModel" , paramNullList = "nullListModel" + , paramLengthArray = "lengthArrayModel" + , paramListToArray = "listToArrayModel" + , paramIndexArray = "indexArrayModel" , paramChooseData = "chooseDataModel" , paramConstrData = "constrDataModel" , paramMapData = "mapDataModel" @@ -209,6 +212,10 @@ createBuiltinCostModel bmfile rfile = do paramHeadList <- getParams readCF1 paramHeadList paramTailList <- getParams readCF1 paramTailList paramNullList <- getParams readCF1 paramNullList + -- Arrays + paramLengthArray <- getParams readCF1 paramLengthArray + paramListToArray <- getParams readCF1 paramListToArray + paramIndexArray <- getParams readCF2 paramIndexArray -- Data paramChooseData <- getParams readCF6 paramChooseData paramConstrData <- getParams readCF2 paramConstrData diff --git a/plutus-core/cost-model/data/builtinCostModelA.json b/plutus-core/cost-model/data/builtinCostModelA.json index 7053057ff9f..18c8bf5ff65 100644 --- a/plutus-core/cost-model/data/builtinCostModelA.json +++ b/plutus-core/cost-model/data/builtinCostModelA.json @@ -698,6 +698,36 @@ "type": "constant_cost" } }, + "lengthArray" : { + "cpu": { + "arguments": 99999999999999, + "type": "constant_cost" + }, + "memory": { + "arguments": 99999999999999, + "type": "constant_cost" + } + }, + "listToArray": { + "cpu": { + "arguments": 99999999999999, + "type": "constant_cost" + }, + "memory": { + "arguments": 99999999999999, + "type": "constant_cost" + } + }, + "indexArray": { + "cpu": { + "arguments": 99999999999999, + "type": "constant_cost" + }, + "memory": { + "arguments": 99999999999999, + "type": "constant_cost" + } + }, "quotientInteger": { "cpu": { "arguments": { diff --git a/plutus-core/cost-model/data/builtinCostModelB.json b/plutus-core/cost-model/data/builtinCostModelB.json index d52c258c175..2ae662a9a2c 100644 --- a/plutus-core/cost-model/data/builtinCostModelB.json +++ b/plutus-core/cost-model/data/builtinCostModelB.json @@ -698,6 +698,36 @@ "type": "constant_cost" } }, + "lengthArray" : { + "cpu": { + "arguments": 99999999999999, + "type": "constant_cost" + }, + "memory": { + "arguments": 99999999999999, + "type": "constant_cost" + } + }, + "listToArray": { + "cpu": { + "arguments": 99999999999999, + "type": "constant_cost" + }, + "memory": { + "arguments": 99999999999999, + "type": "constant_cost" + } + }, + "indexArray": { + "cpu": { + "arguments": 99999999999999, + "type": "constant_cost" + }, + "memory": { + "arguments": 99999999999999, + "type": "constant_cost" + } + }, "quotientInteger": { "cpu": { "arguments": { diff --git a/plutus-core/cost-model/data/builtinCostModelC.json b/plutus-core/cost-model/data/builtinCostModelC.json index 4664f40d9b2..29f10fb6876 100644 --- a/plutus-core/cost-model/data/builtinCostModelC.json +++ b/plutus-core/cost-model/data/builtinCostModelC.json @@ -707,6 +707,36 @@ "type": "constant_cost" } }, + "lengthArray" : { + "cpu": { + "arguments": 99999999999999, + "type": "constant_cost" + }, + "memory": { + "arguments": 99999999999999, + "type": "constant_cost" + } + }, + "listToArray": { + "cpu": { + "arguments": 99999999999999, + "type": "constant_cost" + }, + "memory": { + "arguments": 99999999999999, + "type": "constant_cost" + } + }, + "indexArray": { + "cpu": { + "arguments": 99999999999999, + "type": "constant_cost" + }, + "memory": { + "arguments": 99999999999999, + "type": "constant_cost" + } + }, "quotientInteger": { "cpu": { "arguments": { diff --git a/plutus-core/plutus-core.cabal b/plutus-core/plutus-core.cabal index 258e1829016..c8adc42f61f 100644 --- a/plutus-core/plutus-core.cabal +++ b/plutus-core/plutus-core.cabal @@ -217,6 +217,7 @@ library other-modules: Data.Aeson.Flatten Data.Functor.Foldable.Monadic + Data.Vector.Orphans PlutusCore.Builtin.HasConstant PlutusCore.Builtin.KnownKind PlutusCore.Builtin.KnownType @@ -341,7 +342,7 @@ library , time , transformers , unordered-containers - , vector + , vector ^>=0.13.2 , witherable if impl(ghc <9.0) @@ -376,7 +377,7 @@ test-suite plutus-core-test default-language: Haskell2010 build-depends: , aeson - , base >=4.9 && <5 + , base >=4.9 && <5 , bytestring , containers , data-default-class @@ -400,6 +401,7 @@ test-suite plutus-core-test , text , th-lift-instances , th-utilities + , vector ^>=0.13.2 test-suite untyped-plutus-core-test import: lang @@ -815,6 +817,7 @@ library plutus-core-testlib , tasty-hedgehog , tasty-hunit , text + , vector -- This wraps up the use of the certifier library -- so we can present a consistent inteface whether we diff --git a/plutus-core/plutus-core/src/Data/Vector/Orphans.hs b/plutus-core/plutus-core/src/Data/Vector/Orphans.hs new file mode 100644 index 00000000000..acf187d5afb --- /dev/null +++ b/plutus-core/plutus-core/src/Data/Vector/Orphans.hs @@ -0,0 +1,16 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Data.Vector.Orphans () where + +import Data.Hashable (Hashable (hashWithSalt)) +import Data.Vector.Strict qualified as Strict +import Flat (Flat (..)) +import Flat.Instances.Vector () + +instance (Hashable a) => Hashable (Strict.Vector a) where + hashWithSalt = Strict.foldl' hashWithSalt + +instance (Flat a) => Flat (Strict.Vector a) where + size = size . Strict.toLazy -- Strict to Lazy is O(1) + encode = encode . Strict.toLazy + decode = Strict.fromLazy <$> decode -- Strict from Lazy is O(1) diff --git a/plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs b/plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs index f780ec98ebb..5f25ef8e998 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs @@ -43,6 +43,8 @@ import Data.ByteString.Lazy qualified as BSL import Data.Ix (Ix) import Data.Text (Text) import Data.Text.Encoding (decodeUtf8', encodeUtf8) +import Data.Vector.Strict (Vector) +import Data.Vector.Strict qualified as Vector import Flat hiding (from, to) import Flat.Decoder (Get, dBEBits8) import Flat.Encoder as Flat (Encoding, NumBits, eBits) @@ -104,6 +106,10 @@ data DefaultFun | HeadList | TailList | NullList + -- Arrays + | LengthArray + | ListToArray + | IndexArray -- Data -- See Note [Pattern matching on built-in types]. -- It is convenient to have a "choosing" function for a data type that has more than two @@ -1547,13 +1553,47 @@ instance uni ~ DefaultUni => ToBuiltinMeaning uni DefaultFun where let nullListDenotation :: SomeConstant uni [a] -> BuiltinResult Bool nullListDenotation (SomeConstant (Some (ValueOf uniListA xs))) = case uniListA of - DefaultUniList _argUni -> pure $ null xs + DefaultUniList _uniA -> pure $ null xs _ -> throwing _StructuralUnliftingError "Expected a list but got something else" {-# INLINE nullListDenotation #-} in makeBuiltinMeaning nullListDenotation (runCostingFunOneArgument . paramNullList) + toBuiltinMeaning _semvar LengthArray = + let lengthArrayDenotation :: SomeConstant uni (Vector a) -> BuiltinResult Int + lengthArrayDenotation (SomeConstant (Some (ValueOf uni vec))) = + case uni of + DefaultUniArray _uniA -> pure $ Vector.length vec + _ -> throwing _StructuralUnliftingError "Expected an array but got something else" + {-# INLINE lengthArrayDenotation #-} + in makeBuiltinMeaning lengthArrayDenotation (runCostingFunOneArgument . paramLengthArray) + + toBuiltinMeaning _semvar ListToArray = + let listToArrayDenotation :: SomeConstant uni [a] -> BuiltinResult (Opaque val (Vector a)) + listToArrayDenotation (SomeConstant (Some (ValueOf uniListA xs))) = + case uniListA of + DefaultUniList uniA -> pure $ fromValueOf (DefaultUniArray uniA) $ Vector.fromList xs + _ -> throwing _StructuralUnliftingError "Expected an array but got something else" + {-# INLINE listToArrayDenotation #-} + in makeBuiltinMeaning listToArrayDenotation (runCostingFunOneArgument . paramListToArray) + + toBuiltinMeaning _semvar IndexArray = + let indexArrayDenotation :: SomeConstant uni (Vector a) -> Int -> BuiltinResult (Opaque val a) + indexArrayDenotation (SomeConstant (Some (ValueOf uni vec))) n = + case uni of + DefaultUniArray arg -> do + case vec Vector.!? n of + Nothing -> fail "Array index out of bounds" + Just el -> pure $ fromValueOf arg el + _ -> + -- See Note [Structural vs operational errors within builtins]. + -- The arguments are going to be printed in the "cause" part of the error + -- message, so we don't need to repeat them here. + throwing _StructuralUnliftingError "Expected an array but got something else" + {-# INLINE indexArrayDenotation #-} + in makeBuiltinMeaning indexArrayDenotation (runCostingFunTwoArguments . paramIndexArray) + -- Data toBuiltinMeaning _semvar ChooseData = let chooseDataDenotation :: Data -> a -> a -> a -> a -> a -> a @@ -2183,6 +2223,10 @@ instance Flat DefaultFun where CaseList -> 88 CaseData -> 89 + LengthArray -> 90 + ListToArray -> 91 + IndexArray -> 92 + decode = go =<< decodeBuiltin where go 0 = pure AddInteger go 1 = pure SubtractInteger @@ -2274,6 +2318,9 @@ instance Flat DefaultFun where go 87 = pure ExpModInteger go 88 = pure CaseList go 89 = pure CaseData + go 90 = pure LengthArray + go 91 = pure ListToArray + go 92 = pure IndexArray go t = fail $ "Failed to decode builtin tag, got: " ++ show t size _ n = n + builtinTagWidth diff --git a/plutus-core/plutus-core/src/PlutusCore/Default/Universe.hs b/plutus-core/plutus-core/src/PlutusCore/Default/Universe.hs index 56e6837da6e..8623ed6617f 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Default/Universe.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Default/Universe.hs @@ -34,6 +34,7 @@ module PlutusCore.Default.Universe ( DefaultUni (..) , pattern DefaultUniList + , pattern DefaultUniArray , pattern DefaultUniPair , noMoreTypeFunctions , module Export -- Re-exporting universes infrastructure for convenience. @@ -46,7 +47,8 @@ import PlutusCore.Crypto.BLS12_381.G1 qualified as BLS12_381.G1 import PlutusCore.Crypto.BLS12_381.G2 qualified as BLS12_381.G2 import PlutusCore.Crypto.BLS12_381.Pairing qualified as BLS12_381.Pairing import PlutusCore.Data (Data) -import PlutusCore.Evaluation.Machine.ExMemoryUsage (IntegerCostedLiterally (..), +import PlutusCore.Evaluation.Machine.ExMemoryUsage (ArrayCostedByLength (..), + IntegerCostedLiterally (..), ListCostedByLength (..), NumBytesCostedAsNumWords (..)) import PlutusCore.Pretty.Extra (juxtRenderContext) @@ -57,6 +59,7 @@ import Data.Proxy (Proxy (Proxy)) import Data.Text (Text) import Data.Text qualified as Text import Data.Typeable (typeRep) +import Data.Vector.Strict (Vector) import Data.Word (Word16, Word32, Word64) import GHC.Exts (inline, oneShot) import Text.PrettyBy.Fixity (RenderContext, inContextM, juxtPrettyM) @@ -104,6 +107,7 @@ data DefaultUni a where DefaultUniString :: DefaultUni (Esc Text) DefaultUniUnit :: DefaultUni (Esc ()) DefaultUniBool :: DefaultUni (Esc Bool) + DefaultUniProtoArray :: DefaultUni (Esc Vector) DefaultUniProtoList :: DefaultUni (Esc []) DefaultUniProtoPair :: DefaultUni (Esc (,)) DefaultUniApply :: !(DefaultUni (Esc f)) -> !(DefaultUni (Esc a)) -> DefaultUni (Esc (f a)) @@ -116,6 +120,8 @@ data DefaultUni a where -- so we just leave GHC with its craziness. pattern DefaultUniList uniA = DefaultUniProtoList `DefaultUniApply` uniA +pattern DefaultUniArray uniA = + DefaultUniProtoArray `DefaultUniApply` uniA pattern DefaultUniPair uniA uniB = DefaultUniProtoPair `DefaultUniApply` uniA `DefaultUniApply` uniB @@ -150,6 +156,9 @@ instance GEq DefaultUni where geqStep DefaultUniProtoList a2 = do DefaultUniProtoList <- Just a2 Just Refl + geqStep DefaultUniProtoArray a2 = do + DefaultUniProtoArray <- Just a2 + Just Refl geqStep DefaultUniProtoPair a2 = do DefaultUniProtoPair <- Just a2 Just Refl @@ -187,6 +196,7 @@ instance ToKind DefaultUni where toSingKind DefaultUniUnit = knownKind toSingKind DefaultUniBool = knownKind toSingKind DefaultUniProtoList = knownKind + toSingKind DefaultUniProtoArray = knownKind toSingKind DefaultUniProtoPair = knownKind toSingKind (DefaultUniApply uniF _) = case toSingKind uniF of _ `SingKindArrow` cod -> cod toSingKind DefaultUniData = knownKind @@ -211,6 +221,7 @@ instance PrettyBy RenderContext (DefaultUni a) where DefaultUniUnit -> "unit" DefaultUniBool -> "bool" DefaultUniProtoList -> "list" + DefaultUniProtoArray -> "array" DefaultUniProtoPair -> "pair" DefaultUniApply uniF uniA -> uniF `juxtPrettyM` uniA DefaultUniData -> "data" @@ -251,6 +262,8 @@ instance DefaultUni `Contains` Bool where knownUni = DefaultUniBool instance DefaultUni `Contains` [] where knownUni = DefaultUniProtoList +instance DefaultUni `Contains` Vector where + knownUni = DefaultUniProtoArray instance DefaultUni `Contains` (,) where knownUni = DefaultUniProtoPair instance DefaultUni `Contains` Data where @@ -274,6 +287,8 @@ instance KnownBuiltinTypeAst tyname DefaultUni Bool => KnownTypeAst tyname DefaultUni Bool instance KnownBuiltinTypeAst tyname DefaultUni [a] => KnownTypeAst tyname DefaultUni [a] +instance KnownBuiltinTypeAst tyname DefaultUni (Vector a) => + KnownTypeAst tyname DefaultUni (Vector a) instance KnownBuiltinTypeAst tyname DefaultUni (a, b) => KnownTypeAst tyname DefaultUni (a, b) instance KnownBuiltinTypeAst tyname DefaultUni Data => @@ -299,6 +314,8 @@ instance KnownBuiltinTypeIn DefaultUni term Data => ReadKnownIn DefaultUni term Data instance KnownBuiltinTypeIn DefaultUni term [a] => ReadKnownIn DefaultUni term [a] +instance KnownBuiltinTypeIn DefaultUni term (Vector a) => + ReadKnownIn DefaultUni term (Vector a) instance KnownBuiltinTypeIn DefaultUni term (a, b) => ReadKnownIn DefaultUni term (a, b) instance KnownBuiltinTypeIn DefaultUni term BLS12_381.G1.Element => @@ -322,6 +339,8 @@ instance KnownBuiltinTypeIn DefaultUni term Data => MakeKnownIn DefaultUni term Data instance KnownBuiltinTypeIn DefaultUni term [a] => MakeKnownIn DefaultUni term [a] +instance KnownBuiltinTypeIn DefaultUni term (Vector a) => + MakeKnownIn DefaultUni term (Vector a) instance KnownBuiltinTypeIn DefaultUni term (a, b) => MakeKnownIn DefaultUni term (a, b) instance KnownBuiltinTypeIn DefaultUni term BLS12_381.G1.Element => @@ -487,6 +506,13 @@ deriving newtype instance KnownBuiltinTypeIn DefaultUni term [a] => deriving newtype instance KnownBuiltinTypeIn DefaultUni term [a] => ReadKnownIn DefaultUni term (ListCostedByLength a) +deriving newtype instance KnownTypeAst tyname DefaultUni a => + KnownTypeAst tyname DefaultUni (ArrayCostedByLength a) +deriving newtype instance KnownBuiltinTypeIn DefaultUni term (Vector a) => + MakeKnownIn DefaultUni term (ArrayCostedByLength a) +deriving newtype instance KnownBuiltinTypeIn DefaultUni term (Vector a) => + ReadKnownIn DefaultUni term (ArrayCostedByLength a) + deriving via AsInteger Natural instance KnownTypeAst tyname DefaultUni Natural deriving via AsInteger Natural instance KnownBuiltinTypeIn DefaultUni term Integer => @@ -523,6 +549,7 @@ instance Closed DefaultUni where , constr `Permits` () , constr `Permits` Bool , constr `Permits` [] + , constr `Permits` Vector , constr `Permits` (,) , constr `Permits` Data , constr `Permits` BLS12_381.G1.Element @@ -544,6 +571,7 @@ instance Closed DefaultUni where encodeUni DefaultUniBLS12_381_G1_Element = [9] encodeUni DefaultUniBLS12_381_G2_Element = [10] encodeUni DefaultUniBLS12_381_MlResult = [11] + encodeUni DefaultUniProtoArray = [12] -- See Note [Decoding universes]. -- See Note [Stable encoding of tags]. @@ -564,6 +592,7 @@ instance Closed DefaultUni where 9 -> k DefaultUniBLS12_381_G1_Element 10 -> k DefaultUniBLS12_381_G2_Element 11 -> k DefaultUniBLS12_381_MlResult + 12 -> k DefaultUniProtoArray _ -> empty bring @@ -576,6 +605,8 @@ instance Closed DefaultUni where bring _ DefaultUniBool r = r bring p (DefaultUniProtoList `DefaultUniApply` uniA) r = bring p uniA r + bring p (DefaultUniProtoArray `DefaultUniApply` uniA) r = + bring p uniA r bring p (DefaultUniProtoPair `DefaultUniApply` uniA `DefaultUniApply` uniB) r = bring p uniA $ bring p uniB r bring _ (f `DefaultUniApply` _ `DefaultUniApply` _ `DefaultUniApply` _) _ = diff --git a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/BuiltinCostModel.hs b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/BuiltinCostModel.hs index b19656b6971..50da94cdd3a 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/BuiltinCostModel.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/BuiltinCostModel.hs @@ -127,6 +127,10 @@ data BuiltinCostModelBase f = , paramHeadList :: f ModelOneArgument , paramTailList :: f ModelOneArgument , paramNullList :: f ModelOneArgument + -- Arrays + , paramLengthArray :: f ModelOneArgument + , paramListToArray :: f ModelOneArgument + , paramIndexArray :: f ModelTwoArguments -- Data , paramChooseData :: f ModelSixArguments , paramConstrData :: f ModelTwoArguments diff --git a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExBudgetingDefaults.hs b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExBudgetingDefaults.hs index 47129dc99da..32b3d705f86 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExBudgetingDefaults.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExBudgetingDefaults.hs @@ -288,6 +288,10 @@ unitCostBuiltinCostModel = BuiltinCostModelBase , paramHeadList = unitCostOneArgument , paramTailList = unitCostOneArgument , paramNullList = unitCostOneArgument + -- Arrays + , paramLengthArray = unitCostOneArgument + , paramListToArray = unitCostOneArgument + , paramIndexArray = unitCostTwoArguments -- Data , paramChooseData = unitCostSixArguments , paramConstrData = unitCostTwoArguments diff --git a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExMemoryUsage.hs b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExMemoryUsage.hs index 87bd1f79843..3ace134b858 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExMemoryUsage.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExMemoryUsage.hs @@ -13,6 +13,7 @@ module PlutusCore.Evaluation.Machine.ExMemoryUsage , NumBytesCostedAsNumWords(..) , IntegerCostedLiterally(..) , ListCostedByLength(..) + , ArrayCostedByLength(..) ) where import PlutusCore.Crypto.BLS12_381.G1 as BLS12_381.G1 @@ -27,6 +28,8 @@ import Data.Functor import Data.Proxy import Data.SatInt import Data.Text qualified as T +import Data.Vector.Strict (Vector) +import Data.Vector.Strict qualified as Vector import Data.Word import GHC.Exts (Int (I#)) import GHC.Integer @@ -218,6 +221,15 @@ instance ExMemoryUsage (ListCostedByLength a) where -- realistic input should be that large; however if you're going to use this then be -- sure to convince yourself that it's safe. +newtype ArrayCostedByLength a = ArrayCostedByLength { unArrayCostedByLength :: Vector a } +instance ExMemoryUsage (ArrayCostedByLength a) where + memoryUsage (ArrayCostedByLength l) = singletonRose . fromIntegral $ Vector.length l + {-# INLINE memoryUsage #-} + -- Note that this uses `fromIntegral`, which will narrow large values to + -- maxBound::SatInt = 2^63-1. This shouldn't be a problem for costing because no + -- realistic input should be that large; however if you're going to use this then be + -- sure to convince yourself that it's safe. + -- | Calculate a 'CostingInteger' for the given 'Integer'. memoryUsageInteger :: Integer -> CostingInteger -- integerLog2# is unspecified for 0 (but in practice returns -1) @@ -293,6 +305,7 @@ addConstantRose (CostRose cost1 forest1) (CostRose cost2 forest2) = {-# INLINE addConstantRose #-} instance ExMemoryUsage a => ExMemoryUsage [a] where + -- sizeof([a]) = (1 + 3N) words + N * sizeof(v) memoryUsage = CostRose nilCost . map (addConstantRose consRose . memoryUsage) where -- As per https://wiki.haskell.org/GHC/Memory_Footprint nilCost = 1 @@ -301,6 +314,15 @@ instance ExMemoryUsage a => ExMemoryUsage [a] where {-# INLINE consRose #-} {-# INLINE memoryUsage #-} +instance ExMemoryUsage a => ExMemoryUsage (Vector a) where + -- sizeof(Vector v) = (7 + N) words + N * sizeof(v) + memoryUsage v = CostRose arrayCost [ memoryUsage a | a <- Vector.toList v ] + where + arrayCost :: SatInt + arrayCost = 7 + fromIntegral (Vector.length v) + {-# INLINE arrayCost #-} + {-# INLINE memoryUsage #-} + {- Another naive traversal for size. This accounts for the number of nodes in a Data object, and also the sizes of the contents of the nodes. This is not ideal, but it seems to be the best we can do. At present this only comes diff --git a/plutus-core/plutus-core/src/PlutusCore/Parser/Builtin.hs b/plutus-core/plutus-core/src/PlutusCore/Parser/Builtin.hs index 419008947ef..d8467d0baf2 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Parser/Builtin.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Parser/Builtin.hs @@ -3,7 +3,7 @@ module PlutusCore.Parser.Builtin where -import PlutusPrelude (Word8, reoption) +import PlutusPrelude (Word8, reoption, void) import PlutusCore.Crypto.BLS12_381.G1 qualified as BLS12_381.G1 import PlutusCore.Crypto.BLS12_381.G2 qualified as BLS12_381.G2 @@ -20,6 +20,8 @@ import Data.ByteString (ByteString, pack) import Data.Map.Strict qualified as Map import Data.Text qualified as T import Data.Text.Internal.Read (hexDigitToInt) +import Data.Vector.Strict (Vector) +import Data.Vector.Strict qualified as Vector import Text.Megaparsec (customFailure, getSourcePos, takeWhileP) import Text.Megaparsec.Char (char, hexDigitChar, string) import Text.Megaparsec.Char.Lexer qualified as Lex @@ -65,7 +67,7 @@ conText = lexeme . fmap T.pack $ char '\"' *> manyTill Lex.charLiteral (char '\" -- | Parser for unit. conUnit :: Parser () -conUnit = () <$ (symbol "(" *> symbol ")") +conUnit = void (symbol "(" *> symbol ")") -- | Parser for bool. conBool :: Parser Bool @@ -78,7 +80,11 @@ conBool = -- | Parser for lists. conList :: DefaultUni (Esc a) -> Parser [a] conList uniA = trailingWhitespace . inBrackets $ - constantOf ExpectParensNo uniA `sepBy` symbol "," + constantOf ExpectParensNo uniA `sepBy` symbol "," + +-- | Parser for arrays. +conArray :: DefaultUni (Esc a) -> Parser (Vector a) +conArray uniA = Vector.fromList <$> conList uniA -- | Parser for pairs. conPair :: DefaultUni (Esc a) -> DefaultUni (Esc b) -> Parser (a, b) @@ -123,13 +129,15 @@ conBLS12_381_G2_Element = do -- | Parser for constants of the given type. constantOf :: ExpectParens -> DefaultUni (Esc a) -> Parser a -constantOf expectParens uni = case uni of +constantOf expectParens uni = + case uni of DefaultUniInteger -> conInteger DefaultUniByteString -> conBS DefaultUniString -> conText DefaultUniUnit -> conUnit DefaultUniBool -> conBool DefaultUniProtoList `DefaultUniApply` uniA -> conList uniA + DefaultUniProtoArray `DefaultUniApply` uniA -> conArray uniA DefaultUniProtoPair `DefaultUniApply` uniA `DefaultUniApply` uniB -> conPair uniA uniB f `DefaultUniApply` _ `DefaultUniApply` _ `DefaultUniApply` _ -> noMoreTypeFunctions f DefaultUniData -> conData expectParens diff --git a/plutus-core/plutus-core/src/PlutusCore/Pretty/Extra.hs b/plutus-core/plutus-core/src/PlutusCore/Pretty/Extra.hs index f0798ded51d..cb502923aa9 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Pretty/Extra.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Pretty/Extra.hs @@ -2,7 +2,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} @@ -22,6 +21,7 @@ import Data.Map qualified as Map import Data.Profunctor import Data.Set (Set) import Data.Set qualified as Set +import Data.Vector.Strict (Vector) import Text.PrettyBy.Fixity import Text.PrettyBy.Internal @@ -62,3 +62,8 @@ instance PrettyDefaultBy config [a] => DefaultPrettyBy config (Set a) where defaultPrettyBy config = prettyBy config . Set.toList deriving via PrettyCommon (Set a) instance PrettyDefaultBy config (Set a) => PrettyBy config (Set a) + +instance PrettyDefaultBy config [a] => DefaultPrettyBy config (Vector a) where + defaultPrettyBy config = prettyBy config . toList +deriving via PrettyCommon (Vector a) + instance PrettyDefaultBy config (Vector a) => PrettyBy config (Vector a) diff --git a/plutus-core/plutus-core/src/PlutusCore/Pretty/PrettyConst.hs b/plutus-core/plutus-core/src/PlutusCore/Pretty/PrettyConst.hs index 18270ac2fc2..4b737f59c22 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Pretty/PrettyConst.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Pretty/PrettyConst.hs @@ -19,11 +19,11 @@ import PlutusCore.Pretty.Readable import Control.Lens hiding (List) import Data.ByteString qualified as BS import Data.Coerce -import Data.Foldable (fold) import Data.List.NonEmpty import Data.Proxy import Data.Text qualified as T import Data.Typeable +import Data.Vector.Strict (Vector) import Data.Word (Word8) import Numeric (showHex) import Prettyprinter @@ -122,6 +122,8 @@ instance PrettyConst a => PrettyBy ConstConfig (NoParens a) where instance PrettyConst a => NonDefaultPrettyBy ConstConfig [a] where nonDefaultPrettyBy config = defaultPrettyBy @_ @[NoParens a] config . coerce +instance PrettyConst a => NonDefaultPrettyBy ConstConfig (Vector a) where + nonDefaultPrettyBy config = defaultPrettyBy @_ @(Vector (NoParens a)) config . coerce instance (PrettyConst a, PrettyConst b) => NonDefaultPrettyBy ConstConfig (a, b) where nonDefaultPrettyBy config = defaultPrettyBy @_ @(NoParens a, NoParens b) config . coerce @@ -134,7 +136,7 @@ asBytes x = Text 2 $ T.pack $ addLeadingZero $ showHex x mempty | otherwise = id toBytes :: BS.ByteString -> Doc ann -toBytes b = fold (asBytes <$> BS.unpack b) +toBytes = foldMap asBytes . BS.unpack instance PrettyBy ConstConfig Data where prettyBy = inContextM $ \d0 -> iterAppDocM $ \_ prettyArg -> case d0 of diff --git a/plutus-core/plutus-core/test/CostModelSafety/Spec.hs b/plutus-core/plutus-core/test/CostModelSafety/Spec.hs index d42f2b8482d..229b4ede8f6 100644 --- a/plutus-core/plutus-core/test/CostModelSafety/Spec.hs +++ b/plutus-core/plutus-core/test/CostModelSafety/Spec.hs @@ -48,6 +48,8 @@ import Data.Functor.Identity (Identity (..)) import Data.Kind qualified as GHC (Type) import Data.List.Extra (enumerate) import Data.Text (Text) +import Data.Vector.Strict (Vector) +import Data.Vector.Strict qualified as Vector import Data.Word (Word8) import GHC.Natural import Test.Tasty (TestTree, testGroup) @@ -137,6 +139,10 @@ smallConstant tr , Just HRefl <- eqTypeRep trList' (typeRep @ListCostedByLength) = case smallConstant trElem of SomeConst c -> SomeConst ([] `asTypeOf` [c]) + | trArray `App` trElem <- tr + , Just HRefl <- eqTypeRep trArray (typeRep @Vector) = + case smallConstant trElem of + SomeConst c -> SomeConst (Vector.fromList ([] `asTypeOf` [c])) | trSomeConstant `App` _ `App` trEl <- tr , Just HRefl <- eqTypeRep trSomeConstant (typeRep @SomeConstant) = smallConstant trEl diff --git a/plutus-core/plutus-core/test/TypeSynthesis/Golden/DefaultFun/IndexArray.plc.golden b/plutus-core/plutus-core/test/TypeSynthesis/Golden/DefaultFun/IndexArray.plc.golden new file mode 100644 index 00000000000..1f965336cac --- /dev/null +++ b/plutus-core/plutus-core/test/TypeSynthesis/Golden/DefaultFun/IndexArray.plc.golden @@ -0,0 +1 @@ +all a. array a -> integer -> a \ No newline at end of file diff --git a/plutus-core/plutus-core/test/TypeSynthesis/Golden/DefaultFun/LengthArray.plc.golden b/plutus-core/plutus-core/test/TypeSynthesis/Golden/DefaultFun/LengthArray.plc.golden new file mode 100644 index 00000000000..b23049d3e57 --- /dev/null +++ b/plutus-core/plutus-core/test/TypeSynthesis/Golden/DefaultFun/LengthArray.plc.golden @@ -0,0 +1 @@ +all a. array a -> integer \ No newline at end of file diff --git a/plutus-core/plutus-core/test/TypeSynthesis/Golden/DefaultFun/ListToArray.plc.golden b/plutus-core/plutus-core/test/TypeSynthesis/Golden/DefaultFun/ListToArray.plc.golden new file mode 100644 index 00000000000..c0ad279630b --- /dev/null +++ b/plutus-core/plutus-core/test/TypeSynthesis/Golden/DefaultFun/ListToArray.plc.golden @@ -0,0 +1 @@ +all a. list a -> array a \ No newline at end of file diff --git a/plutus-core/plutus-core/test/TypeSynthesis/Golden/Signatures/DefaultFun/IndexArray.sig.golden b/plutus-core/plutus-core/test/TypeSynthesis/Golden/Signatures/DefaultFun/IndexArray.sig.golden new file mode 100644 index 00000000000..34e0aff0e34 --- /dev/null +++ b/plutus-core/plutus-core/test/TypeSynthesis/Golden/Signatures/DefaultFun/IndexArray.sig.golden @@ -0,0 +1 @@ +forall a. SomeConstant DefaultUni (Vector (TyVarRep * ('TyNameRep * "a" 0))) -> Int -> BuiltinResult (Opaque Val (TyVarRep * ('TyNameRep * "a" 0))) \ No newline at end of file diff --git a/plutus-core/plutus-core/test/TypeSynthesis/Golden/Signatures/DefaultFun/LengthArray.sig.golden b/plutus-core/plutus-core/test/TypeSynthesis/Golden/Signatures/DefaultFun/LengthArray.sig.golden new file mode 100644 index 00000000000..20832479b51 --- /dev/null +++ b/plutus-core/plutus-core/test/TypeSynthesis/Golden/Signatures/DefaultFun/LengthArray.sig.golden @@ -0,0 +1 @@ +forall a. SomeConstant DefaultUni (Vector (TyVarRep * ('TyNameRep * "a" 0))) -> BuiltinResult Int \ No newline at end of file diff --git a/plutus-core/plutus-core/test/TypeSynthesis/Golden/Signatures/DefaultFun/ListToArray.sig.golden b/plutus-core/plutus-core/test/TypeSynthesis/Golden/Signatures/DefaultFun/ListToArray.sig.golden new file mode 100644 index 00000000000..da88809127e --- /dev/null +++ b/plutus-core/plutus-core/test/TypeSynthesis/Golden/Signatures/DefaultFun/ListToArray.sig.golden @@ -0,0 +1 @@ +forall a. SomeConstant DefaultUni [TyVarRep * ('TyNameRep * "a" 0)] -> BuiltinResult (Opaque Val (Vector (TyVarRep * ('TyNameRep * "a" 0)))) \ No newline at end of file diff --git a/plutus-core/plutus-ir/src/PlutusIR/Compiler/Definitions.hs b/plutus-core/plutus-ir/src/PlutusIR/Compiler/Definitions.hs index 865468db7d1..235ccd35756 100644 --- a/plutus-core/plutus-ir/src/PlutusIR/Compiler/Definitions.hs +++ b/plutus-core/plutus-ir/src/PlutusIR/Compiler/Definitions.hs @@ -167,7 +167,7 @@ wrapWithDefs :: wrapWithDefs x tds body = let toValue k = fst <$> Map.lookup k tds wrapDefScc acc scc = - let bs = catMaybes $ toValue <$> Graph.vertexList scc + let bs = mapMaybe toValue (Graph.vertexList scc) in mkLet x (if Graph.isAcyclic scc then NonRec else Rec) bs acc in -- process from the inside out Foldable.foldl' wrapDefScc body (defSccs tds) diff --git a/plutus-core/plutus-ir/src/PlutusIR/Transform/RewriteRules/CommuteFnWithConst.hs b/plutus-core/plutus-ir/src/PlutusIR/Transform/RewriteRules/CommuteFnWithConst.hs index 4ac4920d046..69bea0ed3b7 100644 --- a/plutus-core/plutus-ir/src/PlutusIR/Transform/RewriteRules/CommuteFnWithConst.hs +++ b/plutus-core/plutus-ir/src/PlutusIR/Transform/RewriteRules/CommuteFnWithConst.hs @@ -114,6 +114,9 @@ isCommutative = \case HeadList -> False TailList -> False NullList -> False + LengthArray -> False + ListToArray -> False + IndexArray -> False ChooseData -> False CaseData -> False ConstrData -> False diff --git a/plutus-core/plutus-ir/test/PlutusIR/Parser/Tests.hs b/plutus-core/plutus-ir/test/PlutusIR/Parser/Tests.hs index b8872485029..bba80f3e703 100644 --- a/plutus-core/plutus-ir/test/PlutusIR/Parser/Tests.hs +++ b/plutus-core/plutus-ir/test/PlutusIR/Parser/Tests.hs @@ -89,8 +89,8 @@ isScramblable (PLC.Some (PLC.ValueOf uni0 x0)) = go uni0 x0 where go PLC.DefaultUniString text = T.all (\c -> not (separator c) && c /= '`') text go PLC.DefaultUniUnit _ = True go PLC.DefaultUniBool _ = True - go (PLC.DefaultUniProtoList `PLC.DefaultUniApply` uniA) xs = - all (go uniA) xs + go (PLC.DefaultUniProtoList `PLC.DefaultUniApply` uniA) xs = all (go uniA) xs + go (PLC.DefaultUniProtoArray `PLC.DefaultUniApply` uniA) xs = all (go uniA) xs go (PLC.DefaultUniProtoPair `PLC.DefaultUniApply` uniA `PLC.DefaultUniApply` uniB) (x, y) = go uniA x && go uniB y go (f `PLC.DefaultUniApply` _ `PLC.DefaultUniApply` _ `PLC.DefaultUniApply` _) _ = diff --git a/plutus-core/testlib/PlutusCore/Generators/Hedgehog/Builtin.hs b/plutus-core/testlib/PlutusCore/Generators/Hedgehog/Builtin.hs index 1a2cd3c2143..461487eceb8 100644 --- a/plutus-core/testlib/PlutusCore/Generators/Hedgehog/Builtin.hs +++ b/plutus-core/testlib/PlutusCore/Generators/Hedgehog/Builtin.hs @@ -1,6 +1,5 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE GADTs #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeApplications #-} @@ -27,6 +26,8 @@ import Data.ByteString qualified as BS import Data.Kind qualified as GHC import Data.Text (Text) import Data.Type.Equality +import Data.Vector.Strict (Vector) +import Data.Vector.Strict qualified as Vector import Data.Word (Word8) import GHC.Natural import Hedgehog hiding (Opaque, Var, eval) @@ -119,6 +120,10 @@ genConstant tr , Just HRefl <- eqTypeRep trList' (typeRep @ListCostedByLength) = case genConstant trElem of SomeGen genElem -> SomeGen $ Gen.list (Range.linear 0 10) genElem + | trArray `App` trElem <- tr + , Just HRefl <- eqTypeRep trArray (typeRep @Vector) = + case genConstant trElem of + SomeGen genElem -> SomeGen $ fmap Vector.fromList $ Gen.list (Range.linear 0 10) genElem | trSomeConstant `App` _ `App` trEl <- tr , Just HRefl <- eqTypeRep trSomeConstant (typeRep @SomeConstant) = genConstant trEl diff --git a/plutus-core/testlib/PlutusCore/Generators/QuickCheck/Builtin.hs b/plutus-core/testlib/PlutusCore/Generators/QuickCheck/Builtin.hs index 42f390876b9..112b51429b5 100644 --- a/plutus-core/testlib/PlutusCore/Generators/QuickCheck/Builtin.hs +++ b/plutus-core/testlib/PlutusCore/Generators/QuickCheck/Builtin.hs @@ -7,6 +7,7 @@ {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-orphans #-} +{-# LANGUAGE InstanceSigs #-} module PlutusCore.Generators.QuickCheck.Builtin where @@ -29,8 +30,11 @@ import Data.Proxy import Data.Text (Text) import Data.Text qualified as Text import Data.Text.Encoding qualified as Text +import Data.Vector (Vector) +import Data.Vector.Strict qualified as Strict import Test.QuickCheck import Test.QuickCheck.Instances.ByteString () +import Test.QuickCheck.Instances.Vector () import Universe -- | Same as 'Arbitrary' but specifically for Plutus built-in types, so that we are not tied to @@ -294,6 +298,20 @@ instance ArbitraryBuiltin a => ArbitraryBuiltin [a] where scale (`div` len) . coerce $ arbitrary @(AsArbitraryBuiltin a) shrinkBuiltin = coerce $ shrink @[AsArbitraryBuiltin a] +instance ArbitraryBuiltin a => ArbitraryBuiltin (Strict.Vector a) where + arbitraryBuiltin = do + spine <- Strict.fromLazy <$> arbitrary + let len = length spine + for spine $ \() -> + -- Scale the elements, so that generating a list of vectors of lists doesn't take + -- exponential size (and thus time). + scale (`div` len) . coerce $ arbitrary @(AsArbitraryBuiltin a) + shrinkBuiltin = + map (coerce . Strict.fromLazy) + . shrink @(Vector (AsArbitraryBuiltin a)) + . Strict.toLazy + . coerce @(Strict.Vector a) @(Strict.Vector (AsArbitraryBuiltin a)) + instance (ArbitraryBuiltin a, ArbitraryBuiltin b) => ArbitraryBuiltin (a, b) where arbitraryBuiltin = do (,) diff --git a/plutus-core/testlib/PlutusCore/Test.hs b/plutus-core/testlib/PlutusCore/Test.hs index cf31956bdc1..18b3161c389 100644 --- a/plutus-core/testlib/PlutusCore/Test.hs +++ b/plutus-core/testlib/PlutusCore/Test.hs @@ -153,8 +153,8 @@ isSerialisable (Some (ValueOf uni0 x0)) = go uni0 x0 where go TPLC.DefaultUniString _ = True go TPLC.DefaultUniUnit _ = True go TPLC.DefaultUniBool _ = True - go (TPLC.DefaultUniProtoList `TPLC.DefaultUniApply` uniA) xs = - all (go uniA) xs + go (TPLC.DefaultUniProtoList `TPLC.DefaultUniApply` uniA) xs = all (go uniA) xs + go (TPLC.DefaultUniProtoArray `TPLC.DefaultUniApply` uniA) xs = all (go uniA) xs go (TPLC.DefaultUniProtoPair `TPLC.DefaultUniApply` uniA `TPLC.DefaultUniApply` uniB) (x, y) = go uniA x && go uniB y go (f `TPLC.DefaultUniApply` _ `TPLC.DefaultUniApply` _ `TPLC.DefaultUniApply` _) _ = diff --git a/plutus-core/testlib/PlutusIR/Generators/QuickCheck/ShrinkTerms.hs b/plutus-core/testlib/PlutusIR/Generators/QuickCheck/ShrinkTerms.hs index 7039702c7a8..77eb9e1f2ed 100644 --- a/plutus-core/testlib/PlutusIR/Generators/QuickCheck/ShrinkTerms.hs +++ b/plutus-core/testlib/PlutusIR/Generators/QuickCheck/ShrinkTerms.hs @@ -36,6 +36,7 @@ import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map import Data.Set qualified as Set import Data.Set.Lens (setOf) +import Data.Vector.Strict qualified as Vector import GHC.Stack import Test.QuickCheck (shrink, shrinkList) @@ -119,6 +120,7 @@ minimalBuiltin (SomeTypeIn uni) = case toSingKind uni of go DefaultUniByteString = "" go DefaultUniData = I 0 go (DefaultUniProtoList `DefaultUniApply` _) = [] + go (DefaultUniProtoArray `DefaultUniApply` _) = Vector.empty go (DefaultUniProtoPair `DefaultUniApply` a `DefaultUniApply` b) = (go a, go b) go (f `DefaultUniApply` _ `DefaultUniApply` _ `DefaultUniApply` _) = noMoreTypeFunctions f go DefaultUniBLS12_381_G1_Element = BLS12_381.G1.offchain_zero diff --git a/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Simplify.hs b/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Simplify.hs index f4cdf837503..c07ac4d5c57 100644 --- a/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Simplify.hs +++ b/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Simplify.hs @@ -27,6 +27,7 @@ import UntypedPlutusCore.Transform.Simplifier import Control.Monad import Data.List as List (foldl') import Data.Typeable +import Data.Vector.Orphans () simplifyProgram :: forall name uni fun m a. diff --git a/plutus-core/untyped-plutus-core/test/Evaluation/Builtins/Definition.hs b/plutus-core/untyped-plutus-core/test/Evaluation/Builtins/Definition.hs index 42bd0700b93..adf7ac663f9 100644 --- a/plutus-core/untyped-plutus-core/test/Evaluation/Builtins/Definition.hs +++ b/plutus-core/untyped-plutus-core/test/Evaluation/Builtins/Definition.hs @@ -1,6 +1,7 @@ -- editorconfig-checker-disable-file -- | Tests for all kinds of built-in functions. +{-# LANGUAGE BlockArguments #-} {-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} @@ -54,24 +55,26 @@ import PlutusCore.StdLib.Data.Unit import PlutusCore.Test import UntypedPlutusCore.Evaluation.Machine.Cek -import Control.Exception +import Control.Exception (evaluate, try) import Data.Bifunctor (bimap) import Data.ByteString (ByteString, pack) import Data.ByteString.Base16 qualified as Base16 import Data.DList qualified as DList import Data.List (find) -import Data.Proxy +import Data.Proxy (Proxy (..)) import Data.String (IsString (fromString)) import Data.Text (Text) import Data.Text qualified as Text import Data.Text.Encoding qualified as Text -import Hedgehog hiding (Opaque, Size, Var) +import Data.Vector.Strict (Vector) +import Data.Vector.Strict qualified as Vector +import Hedgehog (forAll, property, withTests, (===)) import Hedgehog.Gen qualified as Gen import Hedgehog.Range qualified as Range import Prettyprinter (vsep) -import Test.Tasty -import Test.Tasty.Hedgehog -import Test.Tasty.HUnit +import Test.Tasty (TestTree, testGroup) +import Test.Tasty.Hedgehog (testPropertyNamed) +import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@=?), (@?=)) import Test.Tasty.QuickCheck qualified as QC type DefaultFunExt = Either DefaultFun ExtensionFun @@ -368,6 +371,30 @@ test_IdBuiltinList = typecheckEvaluateCekNoEmit def defaultBuiltinCostModelExt term @?= Right (EvaluationSuccess xsTerm) +test_BuiltinArray :: TestTree +test_BuiltinArray = + testGroup "BuiltinArray" [ + testCase "listToArray" do + let listOfInts = mkConstant @[Integer] @DefaultUni () [1..10] + let arrayOfInts = mkConstant @(Vector Integer) @DefaultUni () (Vector.fromList [1..10]) + let term = apply () (tyInst () (builtin () ListToArray) integer) listOfInts + typecheckEvaluateCekNoEmit def defaultBuiltinCostModelForTesting term @?= + Right (EvaluationSuccess arrayOfInts) + , testCase "lengthArray" do + let arrayOfInts = mkConstant @(Vector Integer) @DefaultUni () (Vector.fromList [1..10]) + let expectedLength = mkConstant @Integer @DefaultUni () 10 + term = apply () (tyInst () (builtin () LengthArray) integer) arrayOfInts + typecheckEvaluateCekNoEmit def defaultBuiltinCostModelForTesting term @?= + Right (EvaluationSuccess expectedLength) + , testCase "indexArray" do + let arrayOfInts = mkConstant @(Vector Integer) @DefaultUni () (Vector.fromList [1..10]) + let index = mkConstant @Integer @DefaultUni () 5 + expectedValue = mkConstant @Integer @DefaultUni () 6 + term = mkIterAppNoAnn (tyInst () (builtin () IndexArray) integer) [index, arrayOfInts] + typecheckEvaluateCekNoEmit def defaultBuiltinCostModelForTesting term @?= + Right (EvaluationSuccess expectedValue) + ] + test_BuiltinPair :: TestTree test_BuiltinPair = testCase "BuiltinPair" $ do @@ -1178,6 +1205,7 @@ test_definition = , test_ExpensivePlus , test_BuiltinList , test_IdBuiltinList + , test_BuiltinArray , test_BuiltinPair , test_SwapEls , test_IdBuiltinData diff --git a/plutus-tx/plutus-tx.cabal b/plutus-tx/plutus-tx.cabal index 1eff5af1935..089ce2b5082 100644 --- a/plutus-tx/plutus-tx.cabal +++ b/plutus-tx/plutus-tx.cabal @@ -141,6 +141,7 @@ library , text , th-abstraction , th-compat + , vector ^>=0.13.2 default-extensions: Strict diff --git a/plutus-tx/src/PlutusTx/Builtins/HasBuiltin.hs b/plutus-tx/src/PlutusTx/Builtins/HasBuiltin.hs index 30cd0dc7beb..ee9951155b0 100644 --- a/plutus-tx/src/PlutusTx/Builtins/HasBuiltin.hs +++ b/plutus-tx/src/PlutusTx/Builtins/HasBuiltin.hs @@ -18,6 +18,7 @@ import PlutusTx.Builtins.Internal import Data.ByteString (ByteString) import Data.Kind qualified as GHC import Data.Text (Text) +import Data.Vector.Strict (Vector) {- Note [useToOpaque and useFromOpaque] It used to be possible to use 'toBuiltin'/'fromBuiltin' within a smart contract, but this is no @@ -91,6 +92,10 @@ instance HasFromBuiltin a => HasFromBuiltin (BuiltinList a) where type FromBuiltin (BuiltinList a) = [FromBuiltin a] fromBuiltin (BuiltinList xs) = map fromBuiltin xs +instance HasToBuiltin a => HasToBuiltin (Vector a) where + type ToBuiltin (Vector a) = BuiltinArray (ToBuiltin a) + toBuiltin = useToOpaque (BuiltinArray . map toBuiltin) + instance (HasToBuiltin a, HasToBuiltin b) => HasToBuiltin (a, b) where type ToBuiltin (a, b) = BuiltinPair (ToBuiltin a) (ToBuiltin b) toBuiltin (x, y) = BuiltinPair (toBuiltin x, toBuiltin y)