Skip to content

Commit 51ebe37

Browse files
authored
Merge pull request #639 from ethereum/gas-fixes
Gas fixes
2 parents a373de3 + 9c8d851 commit 51ebe37

File tree

3 files changed

+46
-6
lines changed

3 files changed

+46
-6
lines changed

src/EVM/SMT.hs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module EVM.SMT where
99
import Prelude hiding (LT, GT)
1010

1111
import Control.Monad
12-
import Data.Containers.ListUtils (nubOrd)
12+
import Data.Containers.ListUtils (nubOrd, nubInt)
1313
import Data.ByteString (ByteString)
1414
import Data.ByteString qualified as BS
1515
import Data.List qualified as List
@@ -28,6 +28,7 @@ import Data.Text.Lazy (Text)
2828
import Data.Text qualified as TS
2929
import Data.Text.Lazy qualified as T
3030
import Data.Text.Lazy.Builder
31+
import Data.Text.Read (decimal)
3132
import Language.SMT2.Parser (getValueRes, parseCommentFreeFileMsg)
3233
import Language.SMT2.Syntax (Symbol, SpecConstant(..), GeneralRes(..), Term(..), QualIdentifier(..), Identifier(..), Sort(..), Index(..), VarBinding(..))
3334
import Numeric (readHex, readBin)
@@ -232,6 +233,7 @@ assertPropsNoSimp psPreConc = do
232233
<> smt2Line ""
233234
<> keccakAssertions'
234235
<> readAssumes'
236+
<> gasOrder
235237
<> smt2Line ""
236238
<> SMT2 (fmap (\p -> "(assert " <> p <> ")") encs) mempty mempty
237239
<> SMT2 smt mempty mempty
@@ -256,6 +258,7 @@ assertPropsNoSimp psPreConc = do
256258
allVars = fmap referencedVars toDeclarePsElim <> fmap referencedVars bufVals <> fmap referencedVars storeVals <> [abstrVars abst]
257259
frameCtx = fmap referencedFrameContext toDeclarePsElim <> fmap referencedFrameContext bufVals <> fmap referencedFrameContext storeVals
258260
blockCtx = fmap referencedBlockContext toDeclarePsElim <> fmap referencedBlockContext bufVals <> fmap referencedBlockContext storeVals
261+
gasOrder = enforceGasOrder psPreConc
259262

260263
abstrVars :: AbstState -> [Builder]
261264
abstrVars (AbstState b _) = map ((\v->fromString ("abst_" ++ show v)) . snd) (Map.toList b)
@@ -321,7 +324,7 @@ referencedFrameContext expr = nubOrd $ foldTerm go [] expr
321324
go = \case
322325
TxValue -> [(fromString "txvalue", [])]
323326
v@(Balance a) -> [(fromString "balance_" <> formatEAddr a, [PLT v (Lit $ 2 ^ (96 :: Int))])]
324-
Gas {} -> internalError "TODO: GAS"
327+
Gas freshVar -> [(fromString ("gas_" <> show freshVar), [])]
325328
_ -> []
326329

327330
referencedBlockContext :: TraversableTerm a => a -> [(Builder, [Prop])]
@@ -438,6 +441,22 @@ declareAddrs names = SMT2 (["; symbolic addresseses"] <> fmap declare names) cex
438441
declare n = "(declare-fun " <> n <> " () Addr)"
439442
cexvars = (mempty :: CexVars){ addrs = fmap toLazyText names }
440443

444+
enforceGasOrder :: [Prop] -> SMT2
445+
enforceGasOrder ps = SMT2 (["; gas ordering"] <> order indices) mempty mempty
446+
where
447+
order :: [Int] -> [Builder]
448+
order n = consecutivePairs n >>= \(x, y)->
449+
-- The GAS instruction itself costs gas, so it's strictly decreasing
450+
["(assert (bvugt gas_" <> (fromString . show $ x) <> " gas_" <> (fromString . show $ y) <> "))"]
451+
consecutivePairs :: [Int] -> [(Int, Int)]
452+
consecutivePairs [] = []
453+
consecutivePairs l = zip l (tail l)
454+
indices :: [Int] = nubInt $ concatMap (foldProp go mempty) ps
455+
go :: Expr a -> [Int]
456+
go e = case e of
457+
Gas freshVar -> [freshVar]
458+
_ -> []
459+
441460
declareFrameContext :: [(Builder, [Prop])] -> Err SMT2
442461
declareFrameContext names = do
443462
decls <- concatMapM declare names
@@ -850,6 +869,7 @@ exprToSMT = \case
850869
encPrev <- exprToSMT prev
851870
pure $ "(store" `sp` encPrev `sp` encIdx `sp` encVal <> ")"
852871
SLoad idx store -> op2 "select" store idx
872+
Gas freshVar -> pure $ fromLazyText $ "gas_" <> (T.pack $ show freshVar)
853873

854874
a -> internalError $ "TODO: implement: " <> show a
855875
where
@@ -1022,6 +1042,11 @@ parseEAddr name
10221042
| Just a <- TS.stripPrefix "symaddr_" name = SymAddr a
10231043
| otherwise = internalError $ "cannot parse: " <> show name
10241044

1045+
textToInt :: TS.Text -> Int
1046+
textToInt text = case decimal text of
1047+
Right (value, _) -> value
1048+
Left _ -> internalError $ "cannot parse '" <> (TS.unpack text) <> "' into an Int"
1049+
10251050
parseBlockCtx :: TS.Text -> Expr EWord
10261051
parseBlockCtx "origin" = Origin
10271052
parseBlockCtx "coinbase" = Coinbase
@@ -1031,12 +1056,14 @@ parseBlockCtx "prevrandao" = PrevRandao
10311056
parseBlockCtx "gaslimit" = GasLimit
10321057
parseBlockCtx "chainid" = ChainId
10331058
parseBlockCtx "basefee" = BaseFee
1034-
parseBlockCtx t = internalError $ "cannot parse " <> (TS.unpack t) <> " into an Expr"
1059+
parseBlockCtx gas | TS.isPrefixOf (TS.pack "gas_") gas = Gas (textToInt $ TS.drop 4 gas)
1060+
parseBlockCtx val = internalError $ "cannot parse '" <> (TS.unpack val) <> "' into an Expr"
10351061

10361062
parseTxCtx :: TS.Text -> Expr EWord
10371063
parseTxCtx name
10381064
| name == "txvalue" = TxValue
10391065
| Just a <- TS.stripPrefix "balance_" name = Balance (parseEAddr a)
1066+
| Just a <- TS.stripPrefix "gas_" name = Gas (textToInt a)
10401067
| otherwise = internalError $ "cannot parse " <> (TS.unpack name) <> " into an Expr"
10411068

10421069
getAddrs :: (TS.Text -> Expr EAddr) -> (Text -> IO Text) -> [TS.Text] -> IO (Map (Expr EAddr) Addr)

src/EVM/Types.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ data Expr (a :: EType) where
286286

287287
Balance :: Expr EAddr -> Expr EWord
288288

289-
Gas :: Int -- frame idx
289+
Gas :: Int -- fresh gas variable
290290
-> Expr EWord
291291

292292
-- code

test/test.hs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,8 +1168,21 @@ tests = testGroup "hevm"
11681168
|]
11691169
(_, [Cex _]) <- withSolvers Bitwuzla 1 1 Nothing $ \s -> checkAssert s [0x1] c Nothing [] defaultVeriOpts
11701170
putStrLnM "expected counterexample found"
1171-
,
1172-
test "enum-conversion-fail" $ do
1171+
, test "gas-decrease-monotone" $ do
1172+
Just c <- solcRuntime "MyContract"
1173+
[i|
1174+
contract MyContract {
1175+
function fun(uint8 a) external {
1176+
uint a = gasleft();
1177+
uint b = gasleft();
1178+
assert(a > b);
1179+
}
1180+
}
1181+
|]
1182+
let sig = (Just (Sig "fun(uint8)" [AbiUIntType 8]))
1183+
(_, [Qed _]) <- withDefaultSolver $ \s -> checkAssert s defaultPanicCodes c sig [] defaultVeriOpts
1184+
putStrLnM "expected Qed found"
1185+
, test "enum-conversion-fail" $ do
11731186
Just c <- solcRuntime "MyContract"
11741187
[i|
11751188
contract MyContract {

0 commit comments

Comments
 (0)