diff --git a/executables/GasModel2.hs b/executables/GasModel2.hs
new file mode 100644
index 000000000..5e470a56a
--- /dev/null
+++ b/executables/GasModel2.hs
@@ -0,0 +1,1724 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ImportQualifiedPost #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NumericUnderscores #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE Strict #-}
+{-# LANGUAGE StrictData #-}
+{-# LANGUAGE TupleSections #-}
+
+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
+{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Main where
+
+import Control.DeepSeq
+import Control.Exception (bracket)
+import Control.Lens
+import Control.Monad
+import Control.Monad.Trans.Class
+import Control.Monad.Trans.Reader
+import Criterion qualified as C
+import qualified Data.Aeson as A
+import Criterion.Types qualified as C
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.ByteString.Base64 as B64
+import Data.Csv qualified as Csv
+import Data.Decimal
+import Data.Default
+import Data.HashMap.Strict (HashMap)
+import Data.HashMap.Strict qualified as M
+import Data.IORef
+import Data.List
+import Data.Maybe (isJust, isNothing)
+import Data.Text qualified as T
+import Data.Text qualified as Text
+import Data.Text.Encoding qualified as Text
+import Data.Time
+import Data.Time.Format.ISO8601
+import Data.Vector qualified as V
+import GHC.Generics
+import Hedgehog
+import Hedgehog.Gen qualified as Gen
+import Hedgehog.Range qualified as Range
+import Pact.GasModel.GasModel hiding (bench, benchesOnce, main)
+import Pact.GasModel.Types
+import Pact.GasModel.Utils
+import Pact.Types.Lang qualified as Pact
+import Pact.Types.Runtime (EvalEnv (_eeGas), eeMsgBody,
+ evalCallStack, evalPactExec)
+import Pact.Types.Term (Gas(..))
+import Statistics.Types (Estimate (..))
+
+instance Csv.FromField Gas where
+ parseField s = Gas <$> Csv.parseField s
+instance Csv.ToField Gas where
+ toField (Gas s) = Csv.toField s
+
+deriving instance Csv.FromRecord Gas
+deriving instance Csv.ToRecord Gas
+
+data GasResult = GasResult {
+ testName :: String,
+ gasCost :: Gas,
+ timeSpent :: NanoSeconds,
+ gasRate :: NanoSeconds,
+ pactExpr :: T.Text
+ }
+ deriving (Show, Generic,
+ Csv.FromRecord, Csv.ToRecord,
+ Csv.FromNamedRecord, Csv.ToNamedRecord)
+
+main :: IO ()
+main = do
+ putStrLn "Checking that generation works:"
+ xs1 <-
+ Gen.sample $
+ replicateM 10 $
+ flip runReaderT defaultEnv $
+ genExpr TInt
+ forM_ xs1 $ \x1 -> do
+ -- print x1
+ putStrLn $ toLisp x1
+
+ -- Enforces that unit tests succeed
+ putStrLn "Doing dry run of benchmark tests"
+ tests <- forM ([1..5_000] :: [Int]) $ \i -> do
+ t <- mkGasTest (show i) genBuiltin genType
+ mockRuns t
+ pure $! (Pact.NativeDefName (T.pack (show i)), t)
+ putStrLn "Doing dry run of benchmark tests...done"
+
+ putStrLn "Running benchmark(s)"
+ if True -- _oBenchOnly opt
+ then do
+ putStrLn "Establishing gas baseline"
+ baseline <- establishBaseline
+
+ results <- mapM (uncurry (displayGasPrice baseline)) tests
+ BL.putStr $ Csv.encodeByName
+ (V.fromList ["testName","gasCost","timeSpent","gasRate","pactExpr"])
+ (baseline : results)
+ else do
+ let testsSorted = sortOn fst tests
+ allBenches <- mapM benchesMultiple testsSorted
+
+ putStrLn "Exporting raw benchmarks data"
+ writeRawCSV (concatMap snd allBenches)
+
+ putStrLn "Exporting data-driven gas prices"
+ writeGasPriceCSV allBenches
+
+ putStrLn "Reporting coverage"
+ coverageReport
+
+singleGasTestExpr :: GasUnitTests -> String
+singleGasTestExpr (GasUnitTests [t]) =
+ T.unpack (_pactExpressionFull (_gasTestExpression t))
+singleGasTestExpr _ = error "Unexpected"
+
+mkGasTest :: String -> PactGen -> Gen ExprType -> IO GasUnitTests
+mkGasTest !name g t = do
+ expr <- Gen.sample $ runReaderT (g =<< lift t) defaultEnv
+ return $! expr `deepseq` gasTest name expr
+
+single :: PactGen -> Gen ExprType -> IO ()
+single g t = putStrLn =<< singleGasTestExpr <$> mkGasTest "single" g t
+
+establishBaseline :: IO GasResult
+establishBaseline = do
+ baselineReport <- benchesOnce $ gasTest "baseline" (ESym "true")
+ let [(baselineGas, baselineTime)] =
+ map _gasTestResultSqliteDb baselineReport
+ pure GasResult {
+ testName = "baseline",
+ gasCost = baselineGas,
+ timeSpent = baselineTime,
+ gasRate = 0.0,
+ pactExpr = "true"
+ }
+
+displayGasPrice
+ :: Pact.AsString a
+ => GasResult -> a -> GasUnitTests -> IO GasResult
+displayGasPrice baseline funName gt@(GasUnitTests [t]) = do
+ res <- benchesOnce gt
+ let [(gas, time)] = map _gasTestResultSqliteDb res
+ let Gas gas' = gas - gasCost baseline
+ let time' = time - timeSpent baseline
+ pure GasResult {
+ testName = T.unpack (Pact.asString funName),
+ gasCost = Gas gas',
+ timeSpent = time',
+ gasRate = if gas' > 0
+ then time' / fromIntegral gas'
+ else time',
+ pactExpr = _pactExpressionFull (_gasTestExpression t)
+ }
+
+runSingleWithBaseline :: GasResult -> PactGen -> Gen ExprType -> IO ()
+runSingleWithBaseline baseline g t =
+ print
+ =<< displayGasPrice baseline ("single" :: String)
+ =<< mkGasTest "single" g t
+
+runSingle :: PactGen -> Gen ExprType -> IO ()
+runSingle g t = do
+ baseline <- establishBaseline
+ runSingleWithBaseline baseline g t
+
+bench ::
+ PactExpression ->
+ GasSetup e ->
+ IO (Gas, NanoSeconds)
+bench expr dbSetup = do
+ terms <- compileCode (_pactExpressionFull expr)
+ putStrLn $ T.unpack (getDescription expr dbSetup)
+ (gas, rep) <- bracket setup teardown $ \s@(NoopNFData (env, state)) -> do
+ _ <- terms `deepseq` exec state env terms
+ gas <- readIORef (_eeGas env)
+ rep <- C.benchmark' (run terms s)
+ pure (gas, rep)
+ return
+ ( gas,
+ secToNs $
+ estPoint $
+ C.anMean $
+ C.reportAnalysis rep
+ )
+ where
+ setup = do
+ s <- setupEnv dbSetup
+ return $ NoopNFData s
+ teardown (NoopNFData env) = do
+ (gasSetupCleanup dbSetup) env
+ run terms ~(NoopNFData (env, state)) =
+ C.nfIO (exec state env terms)
+
+benchesOnce ::
+ GasUnitTests ->
+ IO [GasTestResult (Gas, NanoSeconds)]
+benchesOnce tests = runGasUnitTests tests bench mockFun
+ where
+ mockFun :: PactExpression -> GasSetup () -> IO (Gas, NanoSeconds)
+ mockFun _ _ = pure (0, 0)
+
+gasTest :: String -> LispExpr -> GasUnitTests
+gasTest name expr =
+ createGasUnitTests
+ (updateWithPactExec . updateStackFrame . updateEnv)
+ (updateWithPactExec . updateStackFrame . updateEnv)
+ [PactExpression (T.pack (toLisp expr)) Nothing]
+ (Pact.NativeDefName (T.pack name))
+ where
+ updateStackFrame = setState (set evalCallStack [someStackFrame])
+
+ updateWithPactExec = setState $ set evalPactExec $ Just $
+ Pact.PactExec 2 Nothing Nothing 0 (Pact.PactId "somePactId")
+ (Pact.PactContinuation (Pact.Name $ Pact.BareName "some-defpact-func" def) [])
+ False
+ mempty
+
+ updateEnv = setEnv $ set eeMsgBody $ A.object
+ [ "ks1" A..= A.object
+ [ "keys" A..= ["76d458b3aa1b0d11a5be8385be2646d799ab898d863dc74e6b78c4726e7f4e8d" :: T.Text]
+ , "pred" A..= ("keys-all" :: T.Text)
+ ]
+ , "ks2" A..= A.object
+ [ "keys" A..=
+ [ "76d458b3aa1b0d11a5be8385be2646d799ab898d863dc74e6b78c4726e7f4e8d" :: T.Text
+ , "011b1bb033d77f0ef7fe0c09f7b10ed91c7f432f6fdc1ba68acdc776fa53d99c" :: T.Text
+ ]
+ , "pred" A..= ("keys-all" :: T.Text)
+ ]
+ , "msg" A..= ("hello" :: T.Text)
+ , "int" A..= (123 :: Int)
+ , "dec" A..= (456.0 :: Float)
+ ]
+
+type PactGen = ExprType -> ReaderT Env Gen LispExpr
+
+type Scope = HashMap String LispExpr
+
+data Env = Env
+ { scopes :: [Scope],
+ depth :: Int -- how deeply can expressions be nested?
+ }
+
+defaultEnv :: Env
+defaultEnv = Env [] 6
+
+-- Although "any" is technically a valid type, we only generate values in this
+-- module whose type we know at time of generation.
+data ExprType
+ = TStr
+ | TInt
+ | TDec
+ | TBool
+ | TTime
+ | TKeyset
+ | TList !ExprType
+ | TObj !Schema
+ | TTable !Schema
+ | TArrow ![ExprType] !ExprType
+ deriving (Eq, Show, Generic, NFData)
+
+type Schema = [(String, ExprType)]
+
+isTStr :: ExprType -> Bool
+isTStr TStr = True
+isTStr _ = False
+
+isTInt :: ExprType -> Bool
+isTInt TInt = True
+isTInt _ = False
+
+isTDec :: ExprType -> Bool
+isTDec TDec = True
+isTDec _ = False
+
+isTBool :: ExprType -> Bool
+isTBool TBool = True
+isTBool _ = False
+
+isTTime :: ExprType -> Bool
+isTTime TTime = True
+isTTime _ = False
+
+isTKeyset :: ExprType -> Bool
+isTKeyset TKeyset = True
+isTKeyset _ = False
+
+isTList :: ExprType -> Bool
+isTList (TList _) = True
+isTList _ = False
+
+isTObj :: ExprType -> Bool
+isTObj (TObj _) = True
+isTObj _ = False
+
+isTTable :: ExprType -> Bool
+isTTable (TTable _) = True
+isTTable _ = False
+
+data LispExpr
+ = EStr !String
+ | EInt !Integer
+ | EDec !Decimal
+ | EBool !Bool
+ | ETime !UTCTime
+ | EKeyset
+ | EList ![LispExpr]
+ | EObj ![(String, LispExpr)]
+ | ETable !Schema
+ | EModule
+ | ESym !String
+ | EParens ![LispExpr]
+ deriving (Eq, Show, Generic, NFData)
+
+toLisp :: LispExpr -> String
+toLisp = \case
+ EStr s -> show s
+ EInt i -> show i
+ EDec d -> show d
+ EBool True -> "true"
+ EBool False -> "false"
+ ETime t ->
+ -- jww (2022-12-15): Stuart says that extra precision can affect storage,
+ -- so we need to generate those as well.
+ let t' = t { utctDayTime = fromIntegral (round (utctDayTime t) :: Integer) }
+ in "(time \"" ++ iso8601Show t' ++ "\")"
+ EKeyset -> "!keyset!" -- jww (2022-09-26): TODO
+ EList xs -> "[" ++ intercalate ", " (map toLisp xs) ++ "]"
+ EObj sch ->
+ "{" ++ emitFields sch ++ " }"
+ where
+ emitFields [] = ""
+ emitFields ((f,x):[]) =
+ " \"" ++ f ++ "\": " ++ toLisp x
+ emitFields ((f,x):fs) =
+ " \"" ++ f ++ "\": " ++ toLisp x ++ "," ++ emitFields fs
+ ETable _ -> "!table!" -- jww (2022-09-26): TODO
+ EModule -> "!module!" -- jww (2022-09-26): TODO
+ ESym s -> s
+ EParens xs -> "(" ++ intercalate " " (map toLisp xs) ++ ")"
+
+pickField :: MonadGen m => Schema -> m String
+pickField = Gen.element . map fst
+
+genIdent :: MonadGen m => m String
+genIdent =
+ (:)
+ <$> Gen.alpha
+ <*> Gen.string (Range.constant 0 16) Gen.alphaNum
+
+genStr :: MonadGen m => m LispExpr
+genStr = EStr <$> Gen.string (Range.linear 0 32) Gen.alpha
+
+genInt :: MonadGen m => m LispExpr
+genInt = do
+ b <- Gen.bool
+ if b
+ then
+ EInt
+ <$> Gen.integral_
+ ( Range.linear
+ 0
+ 1_000_000
+ )
+ else
+ EInt
+ <$> Gen.integral_
+ ( Range.linear
+ (-1_000_000)
+ 1_000_000
+ )
+
+genDec :: MonadGen m => m LispExpr
+genDec = do
+ b <- Gen.bool
+ if b
+ then
+ EDec
+ <$> Gen.realFrac_
+ ( Range.linearFrac
+ 0
+ 1_000_000
+ )
+ else
+ EDec
+ <$> Gen.realFrac_
+ ( Range.linearFrac
+ (-1_000_000)
+ 1_000_000
+ )
+
+genBool :: MonadGen m => m LispExpr
+genBool = EBool <$> Gen.bool
+
+genUTCTime :: MonadGen m => m UTCTime
+genUTCTime = do
+ day <- Gen.integral_ $ Range.linear 0 (10000 :: Integer)
+ sec <- Gen.integral_ $ Range.linear 0 (10_000_000 :: Integer)
+ pure $ UTCTime (ModifiedJulianDay day) (fromIntegral sec)
+
+genTime :: MonadGen m => m LispExpr
+genTime = ETime <$> genUTCTime
+
+genSchema :: MonadGen m => m Schema
+genSchema =
+ Gen.list (Range.exponential 1 5)
+ ((,) <$> Gen.string (Range.linear 2 5) Gen.alpha <*> genType)
+
+genObjBy :: PactGen -> Schema -> ReaderT Env Gen LispExpr
+genObjBy gen sch =
+ local (\e -> e { depth = depth e - 1 }) $ do
+ env <- ask
+ if depth env <= 0
+ then pure $ EObj []
+ else ReaderT $ \_ ->
+ EObj <$> traverse (\(fld, ty) -> (fld,) <$> runReaderT (gen ty) env) sch
+
+genObj :: Schema -> ReaderT Env Gen LispExpr
+genObj = genObjBy genExpr
+
+genLitType :: MonadGen m => m ExprType
+genLitType = Gen.element [TStr, TInt, TDec, TBool]
+
+genType :: MonadGen m => m ExprType
+genType = go (2 :: Int)
+ where
+ go n =
+ Gen.frequency
+ [ (1, genLitType),
+ -- Do not generate lists of lists greater than depth 2
+ (if n > 0 then 1 else 0, TList <$> go (pred n)),
+ -- (1, pure TKeyset), -- jww (2022-09-26): TODO
+ (1, TObj <$> genSchema)
+ -- (1, TTable <$> genSchema) -- jww (2022-09-26): TODO
+ ]
+
+genAtom :: PactGen
+genAtom = \case
+ TStr -> genStr
+ TInt -> genInt
+ TDec -> genDec
+ TBool -> genBool
+ TTime -> genTime
+ TKeyset -> pure EKeyset -- jww (2022-09-26): TODO
+ TList t -> genListBy genAtom t
+ TObj sch -> genObjBy genAtom sch
+ -- elp (2022-10-26): should this even exist?
+ TTable sch -> pure $ ETable sch -- jww (2022-09-26): TODO
+ TArrow doms cod -> genArrow doms cod
+
+genExpr :: PactGen
+genExpr t = do
+ env <- ask
+ if depth env <= 0
+ then genAtom t
+ else do
+ EBool b <- genBool
+ if b
+ then genAtom t
+ else local (\e -> e { depth = depth e - 1 }) $
+ genBuiltin t
+
+listRange :: Int -> Range Int
+listRange = Range.constant 0
+
+genListBy :: PactGen -> PactGen
+genListBy gen t =
+ local (\e -> e { depth = depth e - 1 }) $ do
+ env <- ask
+ if depth env <= 0
+ then pure $ EList []
+ else EList <$> Gen.list (listRange (len (depth env))) (gen t)
+ where
+ -- These numbers determine how long lists can be at various recursion
+ -- depths.
+ len n | n < 1 = 8
+ | n < 2 = 6
+ | n < 3 = 4
+ | otherwise = 2
+
+genList :: PactGen
+genList = genListBy genExpr
+
+genArrow :: [ExprType] -> PactGen
+genArrow _doms _cod = mzero -- jww (2022-12-06): genArrow TODO
+
+genBuiltinByName :: String -> PactGen
+genBuiltinByName name t = case M.lookup name builtins of
+ Just gen -> gen t
+ Nothing -> fail $ "Unknown builtin: " ++ name
+
+genBuiltin :: PactGen
+genBuiltin t = case t of
+ TStr ->
+ Gen.choice
+ [ gen_at t
+ , gen_base64_decode t
+ , gen_base64_encode t
+ , gen_concat t
+ , gen_constantly t
+ , gen_drop t
+ , gen_fold t
+ , gen_format t
+ , gen_hash t
+ , gen_identity t
+ , gen_if t
+ , gen_int_to_str t
+ , tl_gen_namespace t
+ , gen_pact_id t
+ , tl_gen_pact_version t
+ , gen_read_msg t
+ , gen_read_string t
+ , gen_take t
+ , gen_try t
+ , gen_tx_hash t
+ , gen_typeof t
+ --
+ , gen_plus t
+ ]
+ TInt ->
+ Gen.choice
+ [ gen_at t
+ , gen_constantly t
+ , gen_fold t
+ , gen_identity t
+ , gen_if t
+ , gen_length t
+ , gen_str_to_int t
+ , gen_read_msg t
+ , gen_try t
+ --
+ , gen_bitwise_and t
+ , gen_mult t
+ , gen_plus t
+ , gen_minus t
+ , gen_divide t
+ , gen_pow t
+ , gen_abs t
+ , gen_ceiling t
+ , gen_exp t
+ , gen_floor t
+ , gen_ln t
+ , gen_log t
+ , gen_mod t
+ , gen_round t
+ , gen_shift t
+ , gen_sqrt t
+ , gen_xor t
+ , gen_bitwise_or t
+ , gen_bitwise_complement t
+ , gen_days t
+ ]
+ TDec ->
+ Gen.choice
+ [ gen_at t
+ , gen_constantly t
+ , gen_fold t
+ , gen_identity t
+ , gen_if t
+ , gen_read_decimal t
+ , gen_read_msg t
+ , gen_try t
+ --
+ , gen_mult t
+ , gen_plus t
+ , gen_minus t
+ , gen_divide t
+ , gen_pow t
+ , gen_abs t
+ , gen_exp t
+ , gen_ln t
+ , gen_log t
+ , gen_sqrt t
+ , gen_days t
+ ]
+ TBool ->
+ Gen.choice
+ [ gen_at t
+ , gen_constantly t
+ , gen_contains t
+ , gen_enforce t
+ , gen_enforce_one t
+ , tl_gen_enforce_pact_version t
+ , gen_fold t
+ , gen_identity t
+ , gen_if t
+ , gen_is_charset t
+ , gen_read_msg t
+ , gen_try t
+ , gen_not t
+ --
+ , gen_neq t
+ , gen_lt t
+ , gen_lte t
+ , gen_eq t
+ , gen_gt t
+ , gen_gte t
+ , gen_and t
+ , gen_or t
+ ]
+ TTime ->
+ Gen.choice
+ [ gen_at t
+ , gen_constantly t
+ , gen_fold t
+ , gen_identity t
+ , gen_if t
+ , gen_read_msg t
+ , gen_try t
+ --
+ , gen_add_time t
+ ]
+ TKeyset ->
+ Gen.choice
+ [ gen_at t
+ , gen_constantly t
+ , gen_fold t
+ , gen_identity t
+ , gen_if t
+ , gen_read_msg t
+ , gen_try t
+ ]
+ TList _ ->
+ Gen.choice $
+ [ gen_at t
+ , gen_constantly t
+ , gen_drop t
+ , gen_filter t
+ , gen_fold t
+ , gen_identity t
+ , gen_if t
+ , gen_make_list t
+ , gen_map t
+ , gen_zip t
+ , gen_read_msg t
+ , gen_reverse t
+ , gen_sort t
+ , gen_take t
+ , gen_try t
+ , gen_distinct t
+ --
+ , gen_plus t
+ ]
+ ++ [ gen_enumerate t | t == TInt ]
+ ++ [ tl_gen_list_modules t | t == TStr ]
+ ++ [ gen_str_to_list t | t == TStr ]
+ TObj _ ->
+ Gen.choice
+ [ gen_at t
+ , gen_constantly t
+ , gen_bind t
+ , gen_chain_data t
+ , tl_gen_define_namespace t
+ , gen_drop t
+ , gen_fold t
+ , gen_identity t
+ , gen_if t
+ , gen_read_msg t
+ , gen_remove t
+ , gen_sort t
+ , gen_take t
+ , gen_try t
+ ]
+ TTable _ ->
+ Gen.choice
+ [ gen_at t
+ , gen_constantly t
+ , gen_fold t
+ , gen_identity t
+ , gen_if t
+ , gen_read_msg t
+ , gen_try t
+ ]
+ TArrow _ _ ->
+ Gen.choice
+ [ gen_compose t
+ , gen_constantly t
+ , gen_fold t
+ , gen_identity t
+ , gen_if t
+ , gen_read_msg t
+ , gen_try t
+ ]
+
+------------------------------------------------------------------------
+-- Builtins
+
+-- The builtins map is a mapping from function names to generators and a
+-- function that report what the return type will be for a given set of
+-- arguments.
+builtins :: HashMap String PactGen
+builtins =
+ M.fromList
+ [ -- Language constructs
+ ("let", gen_let),
+
+ -- General native functions
+ ("at", gen_at),
+ ("base64-decode", gen_base64_decode),
+ ("base64-encode", gen_base64_encode),
+ ("bind", gen_bind),
+ ("chain-data", gen_chain_data),
+ ("compose", gen_compose),
+ ("concat", gen_concat),
+ ("constantly", gen_constantly),
+ ("contains", gen_contains),
+ ("define-namespace", tl_gen_define_namespace),
+ ("drop", gen_drop),
+ ("enforce", gen_enforce),
+ ("enforce-one", gen_enforce_one),
+ ("enforce-pact-version", tl_gen_enforce_pact_version),
+ ("enumerate", gen_enumerate),
+ ("filter", gen_filter),
+ ("fold", gen_fold),
+ ("format", gen_format),
+ ("hash", gen_hash),
+ ("identity", gen_identity),
+ ("if", gen_if),
+ ("int-to-str", gen_int_to_str),
+ ("is-charset", gen_is_charset),
+ ("length", gen_length),
+ ("list-modules", tl_gen_list_modules),
+ ("make-list", gen_make_list),
+ ("map", gen_map),
+ ("zip", gen_zip),
+ ("namespace", tl_gen_namespace),
+ ("pact-id", gen_pact_id),
+ ("pact-version", tl_gen_pact_version),
+ ("read-decimal", gen_read_decimal),
+ ("read-integer", gen_read_integer),
+ ("read-msg", gen_read_msg),
+ ("read-string", gen_read_string),
+ ("remove", gen_remove),
+ ("resume", gen_resume),
+ ("reverse", gen_reverse),
+ ("sort", gen_sort),
+ ("str-to-int", gen_str_to_int),
+ ("str-to-list", gen_str_to_list),
+ ("take", gen_take),
+ ("try", gen_try),
+ ("tx-hash", gen_tx_hash),
+ ("typeof", gen_typeof),
+ ("distinct", gen_distinct),
+ ("where", gen_where),
+ ("yield", gen_yield),
+
+ -- Operators native functions
+ ("!=", gen_neq),
+ ("&", gen_bitwise_and),
+ ("*", gen_mult),
+ ("+", gen_plus),
+ ("-", gen_minus),
+ ("/", gen_divide),
+ ("<", gen_lt),
+ ("<=", gen_lte),
+ ("=", gen_eq),
+ (">", gen_gt),
+ (">=", gen_gte),
+ ("^", gen_pow),
+ ("abs", gen_abs),
+ ("and", gen_and),
+ ("and?", gen_and_question),
+ ("ceiling", gen_ceiling),
+ ("exp", gen_exp),
+ ("floor", gen_floor),
+ ("ln", gen_ln),
+ ("log", gen_log),
+ ("mod", gen_mod),
+ ("not", gen_not),
+ ("not?", gen_not_question),
+ ("or", gen_or),
+ ("or?", gen_or_question),
+ ("round", gen_round),
+ ("shift", gen_shift),
+ ("sqrt", gen_sqrt),
+ ("xor", gen_xor),
+ ("|", gen_bitwise_or),
+ ("~", gen_bitwise_complement),
+
+ -- Time native functions
+ ("add-time", gen_add_time),
+ ("days", gen_days),
+ ("diff-time", gen_diff_time),
+ ("format-time", gen_format_time),
+ ("hours", gen_hours),
+ ("minutes", gen_minutes),
+ ("parse-time", gen_parse_time),
+ ("time", gen_time),
+
+ -- Commitments native functions
+ ("decrypt-cc20p1305", gen_decrypt_cc20p1305),
+ ("validate-keypair", gen_validate_keypair),
+
+ -- Keyset native functions
+ ("define-keyset", tl_gen_define_keyset),
+ ("enforce-keyset", gen_enforce_keyset),
+ ("keys-2", gen_keys_2),
+ ("keys-all", gen_keys_all),
+ ("keys-any", gen_keys_any),
+ ("read-keyset", gen_read_keyset),
+
+ -- Database native functions
+ ("create-table", tl_gen_create_table),
+ ("describe-keyset", tl_gen_describe_keyset),
+ ("describe-module", tl_gen_describe_module),
+ ("describe-table", tl_gen_describe_table),
+ ("describe-namespace", tl_gen_describe_namespace),
+ ("insert", gen_insert),
+ ("keylog", gen_keylog),
+ ("keys", gen_keys),
+ ("read", gen_read),
+ ("select", gen_select),
+ ("txids", gen_txids),
+ ("txlog", gen_txlog),
+ ("update", gen_update),
+ ("with-default-read", gen_with_default_read),
+ ("with-read", gen_with_read),
+ ("write", gen_write),
+ ("fold-db", gen_fold_db),
+
+ -- Capabilities native functions
+ ("compose-capability", gen_compose_capability),
+ ("create-module-guard", gen_create_module_guard),
+ ("create-pact-guard", gen_create_pact_guard),
+ ("create-user-guard", gen_create_user_guard),
+ ("enforce-guard", gen_enforce_guard),
+ ("install-capability", gen_install_capability),
+ ("keyset-ref-guard", gen_keyset_ref_guard),
+ ("require-capability", gen_require_capability),
+ ("with-capability", gen_with_capability),
+ ("emit-event", gen_emit_event),
+
+ -- Principal creation and validation
+ ("create-principal", gen_create_principal),
+ ("validate-principal", gen_validate_principal),
+ ("is-principal", gen_is_principal),
+ ("typeof-principal", gen_typeof_principal),
+
+ -- Non-native concepts to benchmark
+ ("use", gen_use),
+ ("module", gen_module),
+ ("interface", gen_interface)
+ ]
+
+gen_let :: PactGen
+gen_let t = do
+ -- Note that the list here cannot be a call to a builtin, because then we
+ -- wouldn't know the length in order to construct a valid index.
+ x <- genExpr t
+ -- jww (2022-12-16): We need to synthesize some names and values, add them
+ -- to the Reader environment, and then allow `genExpr` to pick symbols from
+ -- this environment.
+ pure $ EParens [ESym "let", EParens [EParens [ESym "x", EInt 0]], x]
+
+gen_at :: PactGen
+gen_at t = do
+ -- Note that the list here cannot be a call to a builtin, because then we
+ -- wouldn't know the length in order to construct a valid index.
+ l@(EList xs) <- genList t
+ guard $ length xs > 0
+ i <- Gen.integral $ Range.constant 0 (pred (length xs))
+ pure $ EParens [ESym "at", EInt (fromIntegral i), l]
+
+gen_base64_decode :: PactGen
+gen_base64_decode TStr = do
+ -- jww (2022-12-09): In order to properly stress this, we have to generate 2
+ -- strings: one (the first part) encoded as valid base64, and then the
+ -- second being (potential) garbage so that we can stress the error path
+ -- more effectively.
+ EStr x <- genStr
+ let z =
+ Text.unpack . Text.decodeUtf8
+ . B64.encode
+ . Text.encodeUtf8 . Text.pack $ x
+ pure $ EParens [ESym "base64-decode", EStr z]
+gen_base64_decode _ = mzero
+
+gen_base64_encode :: PactGen
+gen_base64_encode t@TStr = do
+ x <- genExpr t
+ pure $ EParens [ESym "base64-encode", x]
+gen_base64_encode _ = mzero
+
+gen_bind :: PactGen
+-- gen_bind t@TObj {} = do
+-- x <- genExpr t
+-- y <- genExpr t
+-- z <- genExpr =<< genType
+-- pure $ EParens [ESym "bind", x, y, z]
+gen_bind _ = mzero -- jww (2022-12-15): TODO
+
+public_chain_data :: Schema
+public_chain_data =
+ [ ("chain-id", TStr)
+ , ("block-height", TInt)
+ , ("block-time", TTime)
+ , ("prev-block-hash", TStr)
+ , ("sender", TStr)
+ , ("gas-limit", TInt)
+ , ("gas-price", TDec)
+ ]
+
+gen_chain_data :: PactGen
+gen_chain_data (TObj sch) | sch == public_chain_data =
+ pure $ EParens [ESym "chain-data"]
+gen_chain_data _ = mzero
+
+gen_compose :: PactGen
+gen_compose (TArrow [a] c) = do
+ b :: ExprType <- genType
+ g <- genExpr (TArrow [a] b)
+ f <- genExpr (TArrow [b] c)
+ pure $ EParens [ESym "compose", f, g]
+gen_compose _ = mzero
+
+gen_concat :: PactGen
+gen_concat t@TStr = do
+ x <- genExpr (TList t)
+ pure $ EParens [ESym "concat", x]
+gen_concat _ = mzero
+
+gen_constantly :: PactGen
+gen_constantly t = do
+ x <- genExpr t
+ y <- genType >>= genExpr
+ pure $ EParens [ESym "constantly", x, y]
+
+gen_contains :: PactGen
+gen_contains TBool = do
+ EBool b1 <- genBool
+ if b1
+ then do -- value list [] → bool
+ t <- genType
+ x <- genExpr t
+ l <- genExpr (TList t)
+ pure $ EParens [ESym "contains", x, l]
+ else do
+ EBool b2 <- genBool
+ if b2
+ then do -- key object object:<{o}> → bool
+ EBool b3 <- genBool
+ if b3
+ then do -- key object object:<{o}> → bool
+ k <- genExpr TStr
+ sch <- genSchema
+ o <- genExpr (TObj sch)
+ pure $ EParens [ESym "contains", k, o]
+ else do -- key object object:<{o}> → bool
+ EStr k <- genStr
+ sch <- genSchema
+ o <- genObj sch
+ guard $ isJust (lookup k sch)
+ pure $ EParens [ESym "contains", EStr k, o]
+ else do -- value string string string → bool
+ EBool b3 <- genBool
+ if b3
+ then do -- value string string string → bool
+ x <- genExpr TStr
+ y <- genExpr TStr
+ pure $ EParens [ESym "contains", x, y]
+ else do -- value string string string → bool
+ EStr x <- genStr
+ EStr y <- genStr
+ guard $ x `isInfixOf` y
+ pure $ EParens [ESym "contains", EStr x, EStr y]
+gen_contains _ = mzero
+
+tl_gen_define_namespace :: PactGen
+-- tl_gen_define_namespace (TObj _sch) = do
+-- n <- genExpr TStr
+-- g1 <- genGuard
+-- g2 <- genGuard
+-- pure $ EParens [ESym "define-namespace", n, g1, g2]
+tl_gen_define_namespace _ = mzero -- jww (2022-12-15): TODO
+
+gen_drop :: PactGen
+gen_drop TStr = do
+ -- Note that the string here cannot be a call to a builtin, because then we
+ -- wouldn't know the length in order to construct a valid index.
+ x@(EStr s) <- genStr
+ EBool b <- genBool
+ n <- if b
+ then Gen.integral $ Range.constant 0 (length s)
+ else Gen.integral $ Range.constant (- length s) (length s)
+ pure $ EParens [ESym "drop", EInt (fromIntegral n), x]
+gen_drop (TList t) = do
+ -- Note that the list here cannot be a call to a builtin, because then we
+ -- wouldn't know the length in order to construct a valid index.
+ x@(EList l) <- genList t
+ EBool b <- genBool
+ n <- if b
+ then Gen.integral $ Range.constant 0 (length l)
+ else Gen.integral $ Range.constant (- length l) (length l)
+ pure $ EParens [ESym "drop", EInt (fromIntegral n), x]
+gen_drop (TObj sch) = do
+ sch2 <- genSchema
+ forM_ sch2 $ \(k, _) ->
+ guard $ isNothing $ lookup k sch
+ EBool b <- genBool
+ if b
+ then do
+ o <- genExpr (TObj (sch ++ sch2))
+ pure $ EParens [ESym "drop", EList (map (EStr . fst) sch2), o]
+ else do
+ o <- genExpr (TObj sch)
+ pure $ EParens [ESym "drop", EList (map (EStr . fst) sch2), o]
+gen_drop _ = mzero
+
+gen_enforce :: PactGen
+gen_enforce t@TBool = do
+ x <- genExpr t
+ msg <- genExpr TStr
+ pure $ EParens [ESym "enforce", EParens [ESym "or", x, ESym "true"], msg]
+gen_enforce _ = mzero
+
+gen_enforce_one :: PactGen
+gen_enforce_one TBool = do
+ s <- genExpr TStr
+ -- jww (2022-12-14): TODO
+ -- y <- genExpr (TList t)
+ EList y <- genList TBool
+ guard $ length y > 0
+ pure $ EParens [ESym "enforce-one", s,
+ EList (map (\b -> EParens [ESym "or", b, ESym "true"]) y)]
+gen_enforce_one _ = mzero
+
+tl_gen_enforce_pact_version :: PactGen
+-- tl_gen_enforce_pact_version TBool = do
+-- ver <- genExpr TStr
+-- pure $ EParens [ESym "enforce-pact-version", ver]
+tl_gen_enforce_pact_version _ = mzero -- jww (2022-12-15): TODO
+
+gen_enumerate :: PactGen
+gen_enumerate (TList TInt) = do
+ x <- genExpr TInt
+ y <- genExpr TInt
+ EBool b <- genBool
+ if b
+ then do
+ z <- genExpr TInt
+ pure $ EParens [ESym "enumerate", x, y, z]
+ else do
+ pure $ EParens [ESym "enumerate", x, y]
+gen_enumerate _ = mzero
+
+gen_filter :: PactGen
+gen_filter (TList t) = do
+ f <- genExpr (TArrow [t] TBool)
+ l <- genExpr (TList t)
+ pure $ EParens [ESym "filter", f, l]
+gen_filter _ = mzero
+
+gen_fold :: PactGen
+gen_fold a = do
+ b <- genType
+ f <- genExpr (TArrow [a, b] a)
+ z <- genExpr a
+ l <- genExpr (TList b)
+ pure $ EParens [ESym "fold", f, z, l]
+
+gen_format :: PactGen
+gen_format TStr = do
+ -- Note that the list here cannot be a call to a builtin, because then we
+ -- wouldn't know the length in order to construct a valid index.
+ x@(EList l) <- genList TStr
+ let i = length l
+ -- creates a string of the shape "{}-{}-{}-{}-{}-{}"
+ -- etc for the length of the list.
+ s = intercalate "-" $ replicate i "{}"
+ pure $ EParens [ESym "format", EStr s, x]
+gen_format _ = mzero
+
+gen_hash :: PactGen
+gen_hash TStr = do
+ x <- genType >>= genExpr
+ pure $ EParens [ESym "hash", x]
+gen_hash _ = mzero
+
+gen_identity :: PactGen
+gen_identity t = do
+ x <- genExpr t
+ pure $ EParens [ESym "identity", x]
+
+gen_if :: PactGen
+gen_if t = do
+ b <- genExpr TBool
+ x <- genExpr t
+ y <- genExpr t
+ pure $ EParens [ESym "if", b, x, y]
+
+gen_int_to_str :: PactGen
+gen_int_to_str TStr = do
+ x <- genExpr TInt
+ -- defaulting to 16 for the base conversion since
+ -- it should be the upper bound in terms of
+ -- computational cost
+ pure $ EParens [ESym "int-to-str", EInt 16, EParens [ESym "abs", x]]
+gen_int_to_str _ = mzero
+
+gen_is_charset :: PactGen
+gen_is_charset TBool = do
+ c <- Gen.element ["CHARSET_ASCII", "CHARSET_LATIN1"]
+ x <- genExpr TStr
+ -- latin1 is the upperbound in terms of complexity for
+ -- this native.
+ pure $ EParens [ESym "is-charset", ESym c, x]
+gen_is_charset _ = mzero
+
+gen_length :: PactGen
+gen_length TInt = do
+ x <- genType >>= genExpr . TList
+ pure $ EParens [ESym "length", x]
+gen_length _ = mzero
+
+tl_gen_list_modules :: PactGen
+tl_gen_list_modules (TList TStr) = do
+ pure $ EParens [ESym "list-modules"]
+tl_gen_list_modules _ = mzero
+
+gen_make_list :: PactGen
+gen_make_list (TList t) = do
+ n <- Gen.integral (listRange 10)
+ x <- genExpr t
+ pure $ EParens [ESym "make-list", EInt (fromIntegral n), x]
+gen_make_list _ = mzero
+
+gen_map :: PactGen
+gen_map (TList a) = do
+ b <- genType
+ f <- genExpr (TArrow [b] a)
+ l <- genExpr (TList b)
+ pure $ EParens [ESym "map", f, l]
+gen_map _ = mzero
+
+gen_zip :: PactGen
+gen_zip (TList c) = do
+ a <- genType
+ b <- genType
+ f <- genExpr (TArrow [a, b] c)
+ la <- genExpr (TList a)
+ lb <- genExpr (TList b)
+ pure $ EParens [ESym "zip", f, la, lb]
+gen_zip _ = mzero
+
+tl_gen_namespace :: PactGen
+-- tl_gen_namespace TStr = do
+-- x <- genExpr TStr
+-- pure $ EParens [ESym "namespace", x]
+tl_gen_namespace _ = mzero -- jww (2022-12-09): TODO
+
+gen_pact_id :: PactGen
+gen_pact_id TStr = pure $ EParens [ESym "pact-id"]
+gen_pact_id _ = mzero
+
+tl_gen_pact_version :: PactGen
+-- tl_gen_pact_version TStr = pure $ EParens [ESym "pact-version"]
+tl_gen_pact_version _ = mzero -- jww (2022-12-15): TODO
+
+gen_read_decimal :: PactGen
+gen_read_decimal TDec = do
+ -- jww (2022-12-14): This should really be a key chosen from a map
+ -- provided in the environment.
+ -- key <- genExpr TStr
+ let key = EStr "dec"
+ pure $ EParens [ESym "read-decimal", key]
+gen_read_decimal _ = mzero
+
+gen_read_integer :: PactGen
+gen_read_integer TInt = do
+ -- jww (2022-12-14): This should really be a key chosen from a map
+ -- provided in the environment.
+ -- key <- genExpr TStr
+ let key = EStr "int"
+ pure $ EParens [ESym "read-integer", key]
+gen_read_integer _ = mzero
+
+-- jww (2022-12-09): This needs to read from an environment map that gets
+-- setup as part of the Pact exe environment. For now, we only provide a
+-- single string value under the key "msg".
+gen_read_msg :: PactGen
+gen_read_msg TStr = do
+ -- jww (2022-12-14): `read-msg` with no arguments is the "type inference"
+ -- version of read-msg.
+ -- EBool b <- genBool
+ let b = True
+ if b
+ then do
+ -- jww (2022-12-14): This should really be a key chosen from a map
+ -- provided in the environment.
+ -- key <- genExpr TStr
+ let key = EStr "msg"
+ pure $ EParens [ESym "read-msg", key]
+ else do
+ pure $ EParens [ESym "read-msg"]
+gen_read_msg _ = mzero
+
+gen_read_string :: PactGen
+gen_read_string TStr = do
+ -- jww (2022-12-14): This should really be a key chosen from a map
+ -- provided in the environment.
+ -- key <- genExpr TStr
+ let key = EStr "msg"
+ pure $ EParens [ESym "read-string", key]
+gen_read_string _ = mzero
+
+gen_remove :: PactGen
+gen_remove (TObj sch) = do
+ t <- genType
+ EStr e <- genStr
+ guard $ length e > 0
+ guard $ isNothing $ lookup e sch
+ x <- genExpr (TObj ((e,t):sch))
+ pure $ EParens [ESym "remove", EStr e, x]
+gen_remove _ = mzero
+
+gen_resume :: PactGen
+gen_resume _ = mzero -- jww (2022-12-07): TODO
+
+gen_reverse :: PactGen
+gen_reverse (TList t) = do
+ x <- genExpr (TList t)
+ pure $ EParens [ESym "reverse", x]
+gen_reverse _ = mzero
+
+gen_sort :: PactGen
+gen_sort (TList (TObj sch)) = do
+ EList l <- genList (TObj sch)
+ guard $ length l > 1
+ let fields = map fst sch
+ s <- Gen.subsequence fields
+ guard $ length s > 0
+ pure $ EParens [ESym "sort", EList (map EStr s), EList l]
+gen_sort (TList t) = do
+ x <- genExpr (TList t)
+ pure $ EParens [ESym "sort", x]
+gen_sort _ = mzero
+
+gen_str_to_int :: PactGen
+gen_str_to_int TInt = do
+ EBool b <- genBool
+ if b
+ then do
+ EStr s <- genStr
+ guard $ all (`elem` ("0123456789" :: String)) s
+ guard $ length s > 0
+ pure $ EParens [ESym "str-to-int", EStr s]
+ else do
+ n <- Gen.element [2, 8, 10, 16]
+ EStr s <- genStr
+ case n of
+ 2 -> guard $ all (`elem` ("01" :: String)) s
+ 8 -> guard $ all (`elem` ("01234567" :: String)) s
+ 10 -> guard $ all (`elem` ("0123456789" :: String)) s
+ 16 -> guard $ all (`elem` ("0123456789abcdef" :: String)) s
+ _ -> error "Impossible"
+ guard $ length s > 0
+ pure $ EParens [ESym "str-to-int", EInt n, EStr s]
+gen_str_to_int _ = mzero
+
+gen_str_to_list :: PactGen
+gen_str_to_list (TList TStr) = do
+ x <- genExpr TStr
+ pure $ EParens [ESym "str-to-list", x]
+gen_str_to_list _ = mzero
+
+gen_take :: PactGen
+gen_take TStr = do
+ -- Note that the string here cannot be a call to a builtin, because then we
+ -- wouldn't know the length in order to construct a valid index.
+ x@(EStr s) <- genStr
+ EBool b <- genBool
+ n <- if b
+ then Gen.integral $ Range.constant 0 (length s)
+ else Gen.integral $ Range.constant (- length s) (length s)
+ pure $ EParens [ESym "take", EInt (fromIntegral n), x]
+gen_take (TList t) = do
+ -- Note that the list here cannot be a call to a builtin, because then we
+ -- wouldn't know the length in order to construct a valid index.
+ x@(EList l) <- genList t
+ EBool b <- genBool
+ n <- if b
+ then Gen.integral $ Range.constant 0 (length l)
+ else Gen.integral $ Range.constant (- length l) (length l)
+ pure $ EParens [ESym "take", EInt (fromIntegral n), x]
+gen_take (TObj sch) = do
+ sch2 <- genSchema
+ forM_ sch2 $ \(k, _) ->
+ guard $ isNothing $ lookup k sch
+ o <- genExpr (TObj (sch ++ sch2))
+ pure $ EParens [ESym "take", EList (map (EStr . fst) sch), o]
+gen_take _ = mzero
+
+gen_try :: PactGen
+gen_try t = do
+ x <- genExpr t
+ y <- genExpr t
+ pure $ EParens [ESym "try", x, y]
+
+gen_tx_hash :: PactGen
+gen_tx_hash TStr = pure $ EParens [ESym "tx-hash"]
+gen_tx_hash _ = mzero
+
+gen_typeof :: PactGen
+gen_typeof TStr = do
+ x <- genType >>= genExpr
+ pure $ EParens [ESym "typeof", x]
+gen_typeof _ = mzero
+
+gen_distinct :: PactGen
+gen_distinct (TList t) = do
+ x <- genExpr (TList t)
+ pure $ EParens [ESym "distinct", x]
+gen_distinct _ = mzero
+
+gen_where :: PactGen
+gen_where _ = mzero -- jww (2022-09-26): TODO
+
+gen_yield :: PactGen
+gen_yield _ = mzero -- jww (2022-09-26): TODO
+
+arity1 :: String -> PactGen
+arity1 name t = do
+ n <- genExpr t
+ pure $ EParens [ESym name, n]
+
+arity1_int_or_dec :: String -> PactGen
+arity1_int_or_dec name _t =
+ arity1 name =<< Gen.element [TInt, TDec]
+
+arity2 :: String -> PactGen
+arity2 name t = do
+ n <- genExpr t
+ m <- genExpr t
+ pure $ EParens [ESym name, n, m]
+
+arity2_int_or_dec :: String -> PactGen
+arity2_int_or_dec name _t = do
+ (n, m) <-
+ Gen.choice
+ [ (,) <$> genExpr TDec <*> genExpr TDec,
+ (,) <$> genExpr TInt <*> genExpr TDec,
+ (,) <$> genExpr TDec <*> genExpr TInt
+ ]
+ pure $ EParens [ESym name, n, m]
+
+canEq :: ExprType -> Bool
+canEq TList {} = True
+canEq TObj {} = True
+canEq TStr = True
+canEq TInt = True
+canEq TDec = True
+canEq TBool = True
+canEq TTime = True
+canEq TTable {} = True
+-- canEq TSchema {} TSchema {} = True
+-- canEq TGuard {} TGuard {} = True
+-- canEq TModRef {} TModRef {} = True
+canEq _ = False
+
+canCmp :: ExprType -> Bool
+canCmp TStr = True
+canCmp TInt = True
+canCmp TDec = True
+canCmp TTime = True
+canCmp _ = False
+
+gen_neq :: PactGen
+gen_neq TBool = do
+ t <- genType
+ guard (canEq t)
+ arity2 "!=" t
+gen_neq _ = mzero
+
+gen_bitwise_and :: PactGen
+gen_bitwise_and t@TInt = arity2 "&" t
+gen_bitwise_and _ = mzero
+
+gen_mult :: PactGen
+gen_mult t@TDec = arity2_int_or_dec "*" t
+gen_mult t@TInt = arity2 "*" t
+gen_mult _ = mzero
+
+gen_plus :: PactGen
+gen_plus t@TDec = arity2_int_or_dec "+" t
+gen_plus t@TInt = arity2 "+" t
+gen_plus t@TStr = arity2 "+" t
+gen_plus t@(TList _) = arity2 "+" t
+gen_plus t@(TObj _) = arity2 "+" t
+gen_plus _ = mzero
+
+gen_minus :: PactGen
+gen_minus t@TDec = arity2_int_or_dec "-" t
+gen_minus t@TInt = arity2 "-" t
+gen_minus _ = mzero
+
+gen_divide :: PactGen
+gen_divide TDec = do
+ x <- genExpr TDec
+ y <- Gen.choice [ do EInt y <- genInt
+ guard $ y /= 0
+ pure $ EInt y
+ , do EDec y <- genDec
+ guard $ y /= 0
+ pure $ EDec y
+ ]
+ pure $ EParens [ESym "/", x, y]
+gen_divide TInt = do
+ x <- genExpr TInt
+ EInt y <- genInt
+ guard $ y /= 0
+ pure $ EParens [ESym "/", x, EInt y]
+gen_divide _ = mzero
+
+gen_lt :: PactGen
+gen_lt TBool = do
+ t <- genType
+ guard (canCmp t)
+ arity2 "<" t
+gen_lt _ = mzero
+
+gen_lte :: PactGen
+gen_lte TBool = do
+ t <- genType
+ guard (canCmp t)
+ arity2 "<=" t
+gen_lte _ = mzero
+
+gen_eq :: PactGen
+gen_eq TBool = do
+ t <- genType
+ guard (canEq t)
+ arity2 "=" t
+gen_eq _ = mzero
+
+gen_gt :: PactGen
+gen_gt TBool = do
+ t <- genType
+ guard (canCmp t)
+ arity2 ">" t
+gen_gt _ = mzero
+
+gen_gte :: PactGen
+gen_gte TBool = do
+ t <- genType
+ guard (canCmp t)
+ arity2 ">=" t
+gen_gte _ = mzero
+
+gen_pow :: PactGen
+gen_pow t@TInt = do
+ EParens [sym, n, _] <- arity2 "^" t
+ EInt m <- genInt
+ guard $ m > 0 && m < 100
+ pure $ EParens [sym, n, EInt m]
+gen_pow t@TDec = do
+ EParens [sym, n, _] <- arity2_int_or_dec "^" t
+ m <- Gen.choice [genInt, genDec]
+ case m of
+ EInt m' -> guard $ m' > 0 && m' < 100
+ EDec m' -> guard $ m' > 0 && m' < 100
+ _ -> error "Impossible"
+ pure $ EParens [sym, n, m]
+gen_pow _ = mzero
+
+gen_abs :: PactGen
+gen_abs t@TInt = arity1 "abs" t
+gen_abs t@TDec = arity1 "abs" t
+gen_abs _ = mzero
+
+gen_and :: PactGen
+gen_and t@TBool = arity2 "and" t
+gen_and _ = mzero
+
+gen_and_question :: PactGen
+gen_and_question _ = mzero -- jww (2022-09-26): TODO
+
+gen_ceiling :: PactGen
+gen_ceiling TInt = arity1 "ceiling" TDec
+gen_ceiling _ = mzero
+
+gen_exp :: PactGen
+gen_exp t@TDec = arity1_int_or_dec "exp" t
+gen_exp _ = mzero
+
+gen_floor :: PactGen
+gen_floor TInt = arity1 "floor" TDec
+gen_floor _ = mzero
+
+gen_ln :: PactGen
+gen_ln TDec = do
+ n <- Gen.choice [genInt, genDec]
+ case n of
+ EInt n' -> guard $ n' > 0
+ EDec n' -> guard $ n' > 0
+ _ -> error "Impossible"
+ pure $ EParens [ESym "ln", n]
+gen_ln _ = mzero
+
+gen_log :: PactGen
+gen_log TInt = do
+ n <- EInt <$> Gen.element [2, 8, 10, 16, 64]
+ EInt x <- genInt
+ guard $ x > 0
+ pure $ EParens [ESym "log", n, EInt x]
+gen_log TDec = do
+ n <- EInt <$> Gen.element [2, 8, 10, 16, 64]
+ EDec x <- genDec
+ guard $ x > 0
+ pure $ EParens [ESym "log", n, EDec x]
+gen_log _ = mzero
+
+gen_mod :: PactGen
+gen_mod TInt = do
+ x <- genExpr TInt
+ EInt y <- genInt
+ guard $ y /= 0
+ pure $ EParens [ESym "mod", x, EInt y]
+gen_mod _ = mzero
+
+gen_not :: PactGen
+gen_not t@TBool = arity1 "not" t
+gen_not _ = mzero
+
+gen_not_question :: PactGen
+gen_not_question _ = mzero -- jww (2022-09-26): TODO
+
+gen_or :: PactGen
+gen_or t@TBool = arity2 "or" t
+gen_or _ = mzero
+
+gen_or_question :: PactGen
+gen_or_question _ = mzero -- jww (2022-09-26): TODO
+
+gen_round :: PactGen
+gen_round TInt = arity1 "round" TDec
+gen_round _ = mzero
+
+gen_shift :: PactGen
+gen_shift t@TInt = arity2 "shift" t
+gen_shift _ = mzero
+
+gen_sqrt :: PactGen
+gen_sqrt t@TDec = do
+ EParens [sym, n] <- arity1 "sqrt" t
+ pure $ EParens [sym, EParens [ESym "abs", n]]
+gen_sqrt _ = mzero
+
+gen_xor :: PactGen
+gen_xor t@TInt = arity2 "xor" t
+gen_xor _ = mzero
+
+gen_bitwise_or :: PactGen
+gen_bitwise_or t@TInt = arity2 "|" t
+gen_bitwise_or _ = mzero
+
+gen_bitwise_complement :: PactGen
+gen_bitwise_complement t@TInt = arity1 "~" t
+gen_bitwise_complement _ = mzero
+
+gen_add_time :: PactGen
+gen_add_time TTime = do
+ t <- genExpr TTime
+ s <- genExpr TInt
+ return $ EParens [ESym "add-time", t, s]
+gen_add_time _ = mzero
+
+gen_days :: PactGen
+gen_days t@TDec = arity1_int_or_dec "days" t
+gen_days _ = mzero
+
+gen_diff_time :: PactGen
+gen_diff_time _ = mzero -- jww (2022-12-15): TODO
+
+gen_format_time :: PactGen
+gen_format_time _ = mzero -- jww (2022-12-15): TODO
+
+gen_hours :: PactGen
+gen_hours _ = mzero -- jww (2022-12-15): TODO
+
+gen_minutes :: PactGen
+gen_minutes _ = mzero -- jww (2022-12-15): TODO
+
+gen_parse_time :: PactGen
+gen_parse_time _ = mzero -- jww (2022-12-15): TODO
+
+gen_time :: PactGen
+gen_time _ = mzero -- jww (2022-12-15): TODO
+
+gen_decrypt_cc20p1305 :: PactGen
+gen_decrypt_cc20p1305 _ = mzero -- jww (2022-12-15): TODO
+
+gen_validate_keypair :: PactGen
+gen_validate_keypair _ = mzero -- jww (2022-12-15): TODO
+
+tl_gen_define_keyset :: PactGen
+tl_gen_define_keyset _ = mzero -- jww (2022-12-15): TODO
+
+gen_enforce_keyset :: PactGen
+gen_enforce_keyset _ = mzero -- jww (2022-12-15): TODO
+
+gen_keys_2 :: PactGen
+gen_keys_2 _ = mzero -- jww (2022-12-15): TODO
+
+gen_keys_all :: PactGen
+gen_keys_all _ = mzero -- jww (2022-12-15): TODO
+
+gen_keys_any :: PactGen
+gen_keys_any _ = mzero -- jww (2022-12-15): TODO
+
+gen_read_keyset :: PactGen
+gen_read_keyset _ = mzero -- jww (2022-12-15): TODO
+
+tl_gen_create_table :: PactGen
+tl_gen_create_table _ = mzero -- jww (2022-12-15): TODO
+
+tl_gen_describe_keyset :: PactGen
+tl_gen_describe_keyset _ = mzero -- jww (2022-12-15): TODO
+
+tl_gen_describe_module :: PactGen
+tl_gen_describe_module _ = mzero -- jww (2022-12-15): TODO
+
+tl_gen_describe_table :: PactGen
+tl_gen_describe_table _ = mzero -- jww (2022-12-15): TODO
+
+tl_gen_describe_namespace :: PactGen
+tl_gen_describe_namespace _ = mzero -- jww (2022-12-15): TODO
+
+gen_insert :: PactGen
+gen_insert _ = mzero -- jww (2022-12-15): TODO
+
+gen_keylog :: PactGen
+gen_keylog _ = mzero -- jww (2022-12-15): TODO
+
+gen_keys :: PactGen
+gen_keys _ = mzero -- jww (2022-12-15): TODO
+
+gen_read :: PactGen
+gen_read _ = mzero -- jww (2022-12-15): TODO
+
+gen_select :: PactGen
+gen_select _ = mzero -- jww (2022-12-15): TODO
+
+gen_txids :: PactGen
+gen_txids _ = mzero -- jww (2022-12-15): TODO
+
+gen_txlog :: PactGen
+gen_txlog _ = mzero -- jww (2022-12-15): TODO
+
+gen_update :: PactGen
+gen_update _ = mzero -- jww (2022-12-15): TODO
+
+gen_with_default_read :: PactGen
+gen_with_default_read _ = mzero -- jww (2022-12-15): TODO
+
+gen_with_read :: PactGen
+gen_with_read _ = mzero -- jww (2022-12-15): TODO
+
+gen_write :: PactGen
+gen_write _ = mzero -- jww (2022-12-15): TODO
+
+gen_fold_db :: PactGen
+gen_fold_db _ = mzero -- jww (2022-12-15): TODO
+
+gen_compose_capability :: PactGen
+gen_compose_capability _ = mzero -- jww (2022-12-15): TODO
+
+gen_create_module_guard :: PactGen
+gen_create_module_guard _ = mzero -- jww (2022-12-15): TODO
+
+gen_create_pact_guard :: PactGen
+gen_create_pact_guard _ = mzero -- jww (2022-12-15): TODO
+
+gen_create_user_guard :: PactGen
+gen_create_user_guard _ = mzero -- jww (2022-12-15): TODO
+
+gen_enforce_guard :: PactGen
+gen_enforce_guard _ = mzero -- jww (2022-12-15): TODO
+
+gen_install_capability :: PactGen
+gen_install_capability _ = mzero -- jww (2022-12-15): TODO
+
+gen_keyset_ref_guard :: PactGen
+gen_keyset_ref_guard _ = mzero -- jww (2022-12-15): TODO
+
+gen_require_capability :: PactGen
+gen_require_capability _ = mzero -- jww (2022-12-15): TODO
+
+gen_with_capability :: PactGen
+gen_with_capability _ = mzero -- jww (2022-12-15): TODO
+
+gen_emit_event :: PactGen
+gen_emit_event _ = mzero -- jww (2022-12-15): TODO
+
+gen_create_principal :: PactGen
+gen_create_principal _ = mzero -- jww (2022-12-15): TODO
+
+gen_validate_principal :: PactGen
+gen_validate_principal _ = mzero -- jww (2022-12-15): TODO
+
+gen_is_principal :: PactGen
+gen_is_principal _ = mzero -- jww (2022-12-15): TODO
+
+gen_typeof_principal :: PactGen
+gen_typeof_principal _ = mzero -- jww (2022-12-15): TODO
+
+gen_use :: PactGen
+gen_use _ = mzero -- jww (2022-12-15): TODO
+
+gen_module :: PactGen
+gen_module _ = mzero -- jww (2022-12-15): TODO
+
+gen_interface :: PactGen
+gen_interface _ = mzero -- jww (2022-12-15): TODO
diff --git a/pact.cabal b/pact.cabal
index 26c3cdade..9a0708c98 100644
--- a/pact.cabal
+++ b/pact.cabal
@@ -356,6 +356,35 @@ executable gasmodel
ghc-prof-options: -fprof-auto -fprof-auto-calls
default-language: Haskell2010
+executable gasmodel2
+ if impl(ghcjs) || !flag(build-tool)
+ buildable: False
+ main-is: GasModel2.hs
+ build-depends: base
+ , Decimal
+ , aeson >= 0.11.3.0 && < 1.6
+ , base64-bytestring >= 1.0.0.1 && < 1.2.0.0
+ , bytestring >=0.10.8.1 && < 0.12
+ , cassava >= 0.5
+ , criterion
+ , data-default
+ , deepseq >= 1.4.2.0 && < 1.5
+ , hedgehog >= 1.0.1 && < 1.3
+ , lens
+ , pact
+ , statistics >= 0.13.3 && < 0.17
+ , tasty >=0.11 && <1.5
+ , tasty-hedgehog >= 1.3.0.0 && < 1.4
+ , text
+ , time
+ , transformers
+ , unordered-containers
+ , vector >= 0.11.0.0 && < 0.13
+ hs-source-dirs: executables
+ ghc-options: -Wall -threaded -rtsopts -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
+ ghc-prof-options: -fprof-auto -fprof-auto-calls
+ default-language: Haskell2010
+
test-suite hspec
main-is: PactTests.hs
type: exitcode-stdio-1.0
diff --git a/src-ghc/Pact/GasModel/Types.hs b/src-ghc/Pact/GasModel/Types.hs
index 804e1e625..d2a2e71d2 100644
--- a/src-ghc/Pact/GasModel/Types.hs
+++ b/src-ghc/Pact/GasModel/Types.hs
@@ -1,4 +1,5 @@
{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
@@ -240,7 +241,7 @@ defEvalEnv db = do
setupEvalEnv db entity Transactional (initMsgData pactInitialHash) (versionedNativesRefStore noPact44EC)
prodGasModel permissiveNamespacePolicy noSPVSupport def noPact44EC
where entity = Just $ EntityName "entity"
- prodGasModel = GasEnv 10000000 0.01 $ tableGasModel defaultGasConfig
+ prodGasModel = GasEnv 1_000_000_000 0.01 (tableGasModel defaultGasConfig)
noPact44EC = mkExecutionConfig [FlagDisablePact44]
-- MockDb
diff --git a/src/Pact/Types/Exp.hs b/src/Pact/Types/Exp.hs
index 88bbc3f43..b17bc58a8 100644
--- a/src/Pact/Types/Exp.hs
+++ b/src/Pact/Types/Exp.hs
@@ -31,6 +31,7 @@ module Pact.Types.Exp
genLiteralDecimal,
genLiteralBool,
genLiteralTime,
+ genArbitraryUTCTime,
simpleISO8601,formatLTime,
litToPrim,
LiteralExp(..),AtomExp(..),ListExp(..),SeparatorExp(..),