diff --git a/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs b/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs index 7601a1b2097..b2df1aaab12 100644 --- a/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs +++ b/plutus-core/cost-model/create-cost-model/BuiltinMemoryModels.hs @@ -110,6 +110,8 @@ builtinMemoryModels = BuiltinCostModelBase , paramHeadList = Id $ ModelOneArgumentConstantCost 32 , paramTailList = Id $ ModelOneArgumentConstantCost 32 , paramNullList = Id $ ModelOneArgumentConstantCost 32 + , paramLengthArray = Id $ ModelOneArgumentConstantCost 99 + , paramListToArray = Id $ ModelOneArgumentConstantCost 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..63193d7cc28 100644 --- a/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs +++ b/plutus-core/cost-model/create-cost-model/CreateBuiltinCostModel.hs @@ -75,6 +75,8 @@ builtinCostModelNames = BuiltinCostModelBase , paramHeadList = "headListModel" , paramTailList = "tailListModel" , paramNullList = "nullListModel" + , paramLengthArray = "lengthArrayModel" + , paramListToArray = "listToArrayModel" , paramChooseData = "chooseDataModel" , paramConstrData = "constrDataModel" , paramMapData = "mapDataModel" @@ -209,6 +211,9 @@ 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 -- 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..9edea7b1579 100644 --- a/plutus-core/cost-model/data/builtinCostModelA.json +++ b/plutus-core/cost-model/data/builtinCostModelA.json @@ -698,6 +698,26 @@ "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" + } + }, "quotientInteger": { "cpu": { "arguments": { diff --git a/plutus-core/cost-model/data/builtinCostModelB.json b/plutus-core/cost-model/data/builtinCostModelB.json index d52c258c175..cb920eb0497 100644 --- a/plutus-core/cost-model/data/builtinCostModelB.json +++ b/plutus-core/cost-model/data/builtinCostModelB.json @@ -698,6 +698,26 @@ "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" + } + }, "quotientInteger": { "cpu": { "arguments": { diff --git a/plutus-core/cost-model/data/builtinCostModelC.json b/plutus-core/cost-model/data/builtinCostModelC.json index 4664f40d9b2..62829e7bdbf 100644 --- a/plutus-core/cost-model/data/builtinCostModelC.json +++ b/plutus-core/cost-model/data/builtinCostModelC.json @@ -707,6 +707,26 @@ "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" + } + }, "quotientInteger": { "cpu": { "arguments": { diff --git a/plutus-core/plutus-core.cabal b/plutus-core/plutus-core.cabal index 26c00a6f2a7..2b58a80f4a5 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..a810867adb5 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,9 @@ data DefaultFun | HeadList | TailList | NullList + -- Arrays + | LengthArray + | ListToArray -- 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 @@ -1554,6 +1559,24 @@ instance uni ~ DefaultUni => ToBuiltinMeaning uni DefaultFun where nullListDenotation (runCostingFunOneArgument . paramNullList) + toBuiltinMeaning _semvar LengthArray = + let lengthArrayDenotation :: SomeConstant uni (Vector a) -> BuiltinResult Int + lengthArrayDenotation (SomeConstant (Some (ValueOf uni vec))) = + case uni of + DefaultUniArray _argUni -> 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 arg -> pure $ fromValueOf (DefaultUniArray arg) $ Vector.fromList xs + _ -> throwing _StructuralUnliftingError "Expected an array but got something else" + {-# INLINE listToArrayDenotation #-} + in makeBuiltinMeaning listToArrayDenotation (runCostingFunOneArgument . paramListToArray) + -- Data toBuiltinMeaning _semvar ChooseData = let chooseDataDenotation :: Data -> a -> a -> a -> a -> a -> a @@ -2183,6 +2206,9 @@ instance Flat DefaultFun where CaseList -> 88 CaseData -> 89 + LengthArray -> 90 + ListToArray -> 91 + decode = go =<< decodeBuiltin where go 0 = pure AddInteger go 1 = pure SubtractInteger @@ -2274,6 +2300,8 @@ 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 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..9c1991b2ba6 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,9 @@ data BuiltinCostModelBase f = , paramHeadList :: f ModelOneArgument , paramTailList :: f ModelOneArgument , paramNullList :: f ModelOneArgument + -- Arrays + , paramLengthArray :: f ModelOneArgument + , paramListToArray :: f ModelOneArgument -- 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..3e232833ba4 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,9 @@ unitCostBuiltinCostModel = BuiltinCostModelBase , paramHeadList = unitCostOneArgument , paramTailList = unitCostOneArgument , paramNullList = unitCostOneArgument + -- Arrays + , paramLengthArray = unitCostOneArgument + , paramListToArray = unitCostOneArgument -- 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..aef9960a573 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) @@ -301,6 +313,10 @@ instance ExMemoryUsage a => ExMemoryUsage [a] where {-# INLINE consRose #-} {-# INLINE memoryUsage #-} +instance ExMemoryUsage a => ExMemoryUsage (Vector a) where + memoryUsage = error "memoryUsage @(Vector a) is not implemented" + {-# 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..2d2d1e90c10 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 = fmap Vector.fromList <$> conList -- | 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/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/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/Transform/RewriteRules/CommuteFnWithConst.hs b/plutus-core/plutus-ir/src/PlutusIR/Transform/RewriteRules/CommuteFnWithConst.hs index 4ac4920d046..aa8b9f07fe5 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,8 @@ isCommutative = \case HeadList -> False TailList -> False NullList -> False + LengthArray -> False + ListToArray -> False ChooseData -> False CaseData -> False ConstrData -> False 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-tx/plutus-tx.cabal b/plutus-tx/plutus-tx.cabal index 2337d8112e0..217ffaae7b4 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)