Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make gas units more granular #1245

Merged
merged 16 commits into from
Aug 3, 2023
3 changes: 2 additions & 1 deletion src-ghc/Pact/Bench.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Pact.Bench where

Expand Down Expand Up @@ -173,7 +174,7 @@ loadCompile f = do


prodGasEnv :: GasEnv
prodGasEnv = GasEnv 100000 0.01 $ tableGasModel defaultGasConfig
prodGasEnv = GasEnv (gasLimitToMilliGasLimit 100_000) 0.01 $ tableGasModel defaultGasConfig

parseCode :: Text -> IO ParsedCode
parseCode m = ParsedCode m <$> eitherDie "parseCode" (parseExprs m)
Expand Down
3 changes: 2 additions & 1 deletion src-ghc/Pact/GasModel/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NumericUnderscores #-}


module Pact.GasModel.Types
Expand Down Expand Up @@ -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 (gasLimitToMilliGasLimit 10_000_000) 0.01 $ tableGasModel defaultGasConfig
noPact44EC = mkExecutionConfig [FlagDisablePact44]

-- MockDb
Expand Down
15 changes: 11 additions & 4 deletions src-ghc/Pact/Interpreter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ setupEvalEnv
-> ExecutionConfig
-> IO (EvalEnv e)
setupEvalEnv dbEnv ent mode msgData refStore gasEnv np spv pd ec = do
gasRef <- newIORef 0
gasRef <- newIORef mempty
warnRef <- newIORef mempty
pure EvalEnv {
_eeRefStore = refStore
Expand Down Expand Up @@ -298,16 +298,23 @@ interpret :: Interpreter e -> EvalEnv e -> EvalInput -> IO EvalResult
interpret runner evalEnv terms = do
((rs,logs,txid),state) <-
runEval def evalEnv $ evalTerms runner terms
gas <- readIORef (_eeGas evalEnv)
milliGas <- readIORef (_eeGas evalEnv)
warnings <- readIORef (_eeWarnings evalEnv)
let gasLogs = _evalLogGas state
let pact48Disabled = views (eeExecutionConfig . ecFlags) (S.member FlagDisablePact48) evalEnv
gasLogs = _evalLogGas state
pactExec = _evalPactExec state
modules = _rsLoadedModules $ _evalRefs state
gasUsed = if pact48Disabled then milliGasToGas milliGas else gasRem milliGas
-- output uses lenient conversion
return $! EvalResult
terms
(map (elideModRefInfo . toPactValueLenient) rs)
logs pactExec gas modules txid gasLogs (_evalEvents state) warnings
logs pactExec gasUsed modules txid gasLogs (_evalEvents state) warnings
where
-- Round up by 1 if the `MilliGas` amount is in any way fractional.
gasRem (MilliGas milliGas) =
let (d, r) = milliGas `quotRem` millisPerGas
in Gas (if r == 0 then d else d+1)

evalTerms :: Interpreter e -> EvalInput -> Eval e EvalOutput
evalTerms interp input = withRollback (start (interpreter interp runInput) >>= end)
Expand Down
3 changes: 2 additions & 1 deletion src-ghc/Pact/Server/PactService.hs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ applyCmd logger conf dbv gasModel bh _ pbh spv exConfig exMode _ (ProcSucc cmd)
blocktime <- (((*) 1000000) <$> systemSeconds <$> getSystemTime)

let payload = _cmdPayload cmd
gasEnv = GasEnv (_pmGasLimit pubMeta) (_pmGasPrice pubMeta) gasModel
gasLimit = gasLimitToMilliGasLimit (_pmGasLimit pubMeta)
gasEnv = GasEnv gasLimit (_pmGasPrice pubMeta) gasModel
pd = PublicData pubMeta bh blocktime pbh
pubMeta = _pMeta payload
nid = _pNetworkId payload
Expand Down
94 changes: 45 additions & 49 deletions src/Pact/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ apply :: App (Term Ref) -> [Term Name] -> Eval e (Term Name)
apply app as = reduceApp $ over appArgs (++ map liftTerm as) app

topLevelCall
:: Info -> Text -> GasArgs -> (Gas -> Eval e (Gas, a)) -> Eval e a
:: Info -> Text -> GasArgs -> Eval e a -> Eval e a
topLevelCall i name gasArgs action = call (StackFrame name i Nothing) $
computeGas (Left (i,name)) gasArgs >>= action
computeGas (Left (i,name)) gasArgs *> action

-- | Acquire module admin with enforce.
acquireModuleAdmin :: Info -> ModuleName -> Governance (Def Ref) -> Eval e CapEvalResult
Expand All @@ -233,8 +233,8 @@ enforceModuleAdmin i modGov =
Right d@Def{..} -> case _dDefType of
Defcap -> do
af <- prepareUserAppArgs d [] _dInfo
g <- computeUserAppGas d _dInfo
void $ evalUserAppBody d af _dInfo g reduceBody
computeUserAppGas d _dInfo
void $ evalUserAppBody d af _dInfo reduceBody
_ -> evalError i "enforceModuleAdmin: module governance must be defcap"


Expand Down Expand Up @@ -287,10 +287,10 @@ eval t =

-- | Evaluate top-level term.
eval' :: Term Name -> Eval e (Term Name)
eval' (TUse u@Use{..} i) = topLevelCall i "use" (GUse _uModuleName _uModuleHash) $ \g ->
evalUse u >> return (g,tStr $ renderCompactText' $ "Using " <> pretty _uModuleName)
eval' (TUse u@Use{..} i) = topLevelCall i "use" (GUse _uModuleName _uModuleHash) $
evalUse u >> return (tStr $ renderCompactText' $ "Using " <> pretty _uModuleName)
eval' (TModule _tm@(MDModule m) bod i) =
topLevelCall i "module" (GModuleDecl (_mName m) (_mCode m)) $ \g0 -> do
topLevelCall i "module" (GModuleDecl (_mName m) (_mCode m)) $ do
endAdvice <- eAdvise i (AdviceModule _tm)
checkAllowModule i
mNs <- use $ evalRefs . rsNamespace
Expand Down Expand Up @@ -325,16 +325,16 @@ eval' (TModule _tm@(MDModule m) bod i) =
evalRefs.rsQualifiedDeps %= HM.filterWithKey (\k _ -> _fqModule k /= _mName mangledM)

-- build/install module from defs
(g,govM) <- loadModule mangledM bod i g0
govM <- loadModule mangledM bod i
szVer <- getSizeOfVersion
_ <- computeGas (Left (i,"module")) (GPreWrite (WriteModule (_mName m) (_mCode m)) szVer)
writeRow i Write Modules (_mName mangledM) =<< traverse (traverse toPersistDirect') govM
endAdvice govM
return (g, msg $ "Loaded module " <> pretty (_mName mangledM) <> ", hash " <> pretty (_mHash mangledM))
return (msg $ "Loaded module " <> pretty (_mName mangledM) <> ", hash " <> pretty (_mHash mangledM))


eval' (TModule _tm@(MDInterface m) bod i) =
topLevelCall i "interface" (GInterfaceDecl (_interfaceName m) (_interfaceCode m)) $ \gas -> do
topLevelCall i "interface" (GInterfaceDecl (_interfaceName m) (_interfaceCode m)) $ do
endAdvice <- eAdvise i (AdviceModule _tm)
checkAllowModule i
mNs <- use $ evalRefs . rsNamespace
Expand All @@ -344,12 +344,12 @@ eval' (TModule _tm@(MDInterface m) bod i) =
-- enforce no upgrades
void $ lookupModule i (_interfaceName mangledI) >>= traverse
(const $ evalError i $ "Existing interface found (interfaces cannot be upgraded)")
(g,govI) <- loadInterface mangledI bod i gas
govI <- loadInterface mangledI bod i
szVer <- getSizeOfVersion
_ <- computeGas (Left (i, "interface")) (GPreWrite (WriteInterface (_interfaceName m) (_interfaceCode m)) szVer)
computeGas (Left (i, "interface")) (GPreWrite (WriteInterface (_interfaceName m) (_interfaceCode m)) szVer)
writeRow i Write Modules (_interfaceName mangledI) =<< traverse (traverse toPersistDirect') govI
endAdvice govI
return (g, msg $ "Loaded interface " <> pretty (_interfaceName mangledI))
return (msg $ "Loaded interface " <> pretty (_interfaceName mangledI))
eval' t = enscope t >>= reduceEnscoped

reduceEnscoped :: Term Ref -> Eval e (Term Name)
Expand Down Expand Up @@ -455,17 +455,16 @@ loadModule
:: Module (Term Name)
-> Scope n Term Name
-> Info
-> Gas
-> Eval e (Gas,ModuleData Ref)
loadModule m bod1 mi g0 = do
-> Eval e (ModuleData Ref)
loadModule m bod1 mi = do
mapM_ evalUse $ _mImports m
(g1,mdefs) <- collectNames g0 (GModuleMember $ MDModule m) bod1 $ \t -> case t of
mdefs <- collectNames (GModuleMember $ MDModule m) bod1 $ \case
TDef d _ -> return $ Just $ asString (_dDefName d)
TConst a _ _ _ _ -> return $ Just $ _aName a
TSchema n _ _ _ _ -> return $ Just $ asString n
tt@TTable{} -> return $ Just $ asString (_tTableName tt)
TUse _ _ -> return Nothing
_ -> evalError' t "Invalid module member"
t -> evalError' t "Invalid module member"
let mangled = mangleDefs (_mName m) <$> mdefs
(evaluatedDefs, deps) <-
ifExecutionFlagSet FlagDisablePact43
Expand All @@ -475,49 +474,46 @@ loadModule m bod1 mi g0 = do
mGov <- resolveGovernance solvedDefs m'
let md = ModuleData mGov solvedDefs deps
installModule True md Nothing
return (g1,md)
return md

loadInterface
:: Interface
-> Scope n Term Name
-> Info
-> Gas
-> Eval e (Gas,ModuleData Ref)
loadInterface i body info gas0 = do
-> Eval e (ModuleData Ref)
loadInterface i body info = do
mapM_ evalUse $ _interfaceImports i
(gas1,idefs) <- collectNames gas0 (GModuleMember $ MDInterface i) body $ \t -> case t of
idefs <- collectNames (GModuleMember $ MDInterface i) body $ \case
TDef d _ -> return $ Just $ asString (_dDefName d)
TConst a _ _ _ _ -> return $ Just $ _aName a
TSchema n _ _ _ _ -> return $ Just $ asString n
TUse _ _ -> return Nothing
_ -> evalError' t "Invalid interface member"
t -> evalError' t "Invalid interface member"
evaluatedDefs <- evaluateDefs info (MDInterface i) $
mangleDefs (_interfaceName i) <$> idefs
let md = ModuleData (MDInterface i) evaluatedDefs mempty
installModule True md Nothing
return (gas1,md)
return md

-- | Retrieve map of definition names to their corresponding terms
-- and compute their gas value
--
collectNames
:: Gas
-- ^ initial gas value
-> GasArgs
:: GasArgs
-- ^ gas args (should be GModuleMember)
-> Scope n Term Name
-- ^ module body
-> (Term Name -> Eval e (Maybe Text))
-- ^ function extracting definition names
-> Eval e (Gas, HM.HashMap Text (Term Name))
collectNames g0 args body k = case instantiate' body of
-> Eval e (HM.HashMap Text (Term Name))
collectNames args body k = case instantiate' body of
TList bd _ _ -> do
ns <- view $ eeRefStore . rsNatives
foldM (go ns) (g0, mempty) bd
foldM (go ns) mempty bd
t -> evalError' t $ "malformed declaration"
where
go ns (g,ds) t = k t >>= \dnm -> case dnm of
Nothing -> return (g, ds)
go ns ds t = k t >>= \dnm -> case dnm of
Nothing -> return ds
Just dn -> do
-- disallow native overlap
when (isJust $ HM.lookup dn ns) $
Expand All @@ -526,8 +522,8 @@ collectNames g0 args body k = case instantiate' body of
when (isJust $ HM.lookup dn ds) $
evalError' t $ "definition name conflict: " <> pretty dn

g' <- computeGas (Left (_tInfo t,dn)) args
return (g + g',HM.insert dn t ds)
computeGas (Left (_tInfo t,dn)) args
return (HM.insert dn t ds)


resolveGovernance
Expand Down Expand Up @@ -1131,7 +1127,7 @@ resolveArg ai as i = case as ^? ix i of
Nothing -> appError ai $ "Missing argument value at index " <> pretty i
Just i' -> i'

appCall :: Pretty t => FunApp -> Info -> [Term t] -> Eval e (Gas,a) -> Eval e a
appCall :: Pretty t => FunApp -> Info -> [Term t] -> Eval e a -> Eval e a
appCall fa ai as = call (StackFrame (_faName fa) ai (Just (fa,map abbrev as)))

enforcePactValue :: Pretty n => (Term n) -> Eval e PactValue
Expand All @@ -1153,9 +1149,9 @@ reduceApp (App (TDef d@Def{..} _) as ai) = do
c r
pure r
Defpact -> do
g <- computeUserAppGas d ai
computeUserAppGas d ai
af <- prepareUserAppArgs d as ai
evalUserAppBody d af ai g $ \bod' -> do
evalUserAppBody d af ai $ \bod' -> do
continuation <-
PactContinuation (QName (QualifiedName _dModule (asString _dDefName) def))
. map elideModRefInfo
Expand All @@ -1166,7 +1162,7 @@ reduceApp (App (TLam (Lam lamName funTy body _) _) as ai) =
functionApp (DefName lamName) funTy Nothing as body Nothing ai
reduceApp (App (TLitString errMsg) _ i) = evalError i $ pretty errMsg
reduceApp (App (TDynamic tref tmem ti) as ai) =
reduceDynamic tref tmem ti >>= \rd -> case rd of
reduceDynamic tref tmem ti >>= \case
Left v -> evalError ti $ "reduceApp: expected module member for dynamic: " <> pretty v
Right d -> reduceApp $ App (TDef d (getInfo d)) as ai
reduceApp (App r _ ai) = evalError' ai $ "Expected def: " <> pretty r
Expand All @@ -1183,7 +1179,7 @@ functionApp
-> Info
-> Eval e (Term Name)
functionApp fnName funTy mod_ as fnBody docs ai = do
gas <- computeGas (Left (ai, asString fnName)) (GUserApp Defun)
computeGas (Left (ai, asString fnName)) (GUserApp Defun)
args <- traverse reduce as
fty <- traverse reduce funTy
typecheckArgs ai fnName fty args
Expand All @@ -1192,7 +1188,7 @@ functionApp fnName funTy mod_ as fnBody docs ai = do
fname = asString fnName
fa = FunApp ai fname mod_ Defun (funTypes fty) docs

returnVal <- guardRecursion fname mod_ $ appCall fa ai args' $ fmap (gas,) $ reduceBody body
returnVal <- guardRecursion fname mod_ $ appCall fa ai args' $ reduceBody body

unlessExecutionFlagSet FlagDisableRuntimeReturnTypeChecking $
typecheckTerm ai (_ftReturn fty) returnVal
Expand Down Expand Up @@ -1231,7 +1227,7 @@ reduceDynamic tref tmem i = do


-- | precompute "UserApp" cost
computeUserAppGas :: Def Ref -> Info -> Eval e Gas
computeUserAppGas :: Def Ref -> Info -> Eval e ()
computeUserAppGas Def{..} ai = computeGas (Left (ai, asString _dDefName)) (GUserApp _dDefType)

-- | prepare reduced args and funtype, and typecheck
Expand All @@ -1253,11 +1249,11 @@ guardRecursion fname m act =
sfn == fname && (_faModule . fst =<< app) == m

-- | Instantiate args in body and evaluate using supplied action.
evalUserAppBody :: Def Ref -> ([Term Name], FunType (Term Name)) -> Info -> Gas
evalUserAppBody :: Def Ref -> ([Term Name], FunType (Term Name)) -> Info
-> (Term Ref -> Eval e (Term Name)) -> Eval e (Term Name)
evalUserAppBody _d@Def{..} (as',ft') ai g run = guardRecursion fname (Just _dModule) $ do
evalUserAppBody _d@Def{..} (as',ft') ai run = guardRecursion fname (Just _dModule) $ do
c <- eAdvise ai (AdviceUser _d)
!r <- appCall fa ai as' $ fmap (g,) $ run bod'
!r <- appCall fa ai as' $ run bod'
c r
pure r
where
Expand Down Expand Up @@ -1563,7 +1559,7 @@ resumeNestedPactExec i def' req ctx = do

let args = map (liftTerm . fromPactValue) (_pcArgs (_npeContinuation ctx))

g <- computeUserAppGas def' i
computeUserAppGas def' i
af <- prepareUserAppArgs def' args i

-- if resume is in step, use that, otherwise get from exec state
Expand All @@ -1573,7 +1569,7 @@ resumeNestedPactExec i def' req ctx = do

-- run local environment with yield from pact exec
local (set eePactStep (Just $ set psResume resume req)) $
evalUserAppBody def' af i g $ \bod ->
evalUserAppBody def' af i $ \bod ->
applyNestedPact i (_npeContinuation ctx) bod req


Expand Down Expand Up @@ -1607,7 +1603,7 @@ resumePactExec i req ctx = do

let args = map (liftTerm . fromPactValue) (_pcArgs (_peContinuation ctx))

g <- computeUserAppGas def' i
computeUserAppGas def' i
af <- prepareUserAppArgs def' args i

-- if resume is in step, use that, otherwise get from exec state
Expand All @@ -1617,7 +1613,7 @@ resumePactExec i req ctx = do

-- run local environment with yield from pact exec
local (set eePactStep (Just $ set psResume resume req)) $
evalUserAppBody def' af i g $ \bod ->
evalUserAppBody def' af i $ \bod ->
applyPact i (_peContinuation ctx) bod req (_peNested ctx)


Expand Down
Loading