@@ -9,7 +9,7 @@ module EVM.SMT where
99import Prelude hiding (LT , GT )
1010
1111import Control.Monad
12- import Data.Containers.ListUtils (nubOrd )
12+ import Data.Containers.ListUtils (nubOrd , nubInt )
1313import Data.ByteString (ByteString )
1414import Data.ByteString qualified as BS
1515import Data.List qualified as List
@@ -28,6 +28,7 @@ import Data.Text.Lazy (Text)
2828import Data.Text qualified as TS
2929import Data.Text.Lazy qualified as T
3030import Data.Text.Lazy.Builder
31+ import Data.Text.Read (decimal )
3132import Language.SMT2.Parser (getValueRes , parseCommentFreeFileMsg )
3233import Language.SMT2.Syntax (Symbol , SpecConstant (.. ), GeneralRes (.. ), Term (.. ), QualIdentifier (.. ), Identifier (.. ), Sort (.. ), Index (.. ), VarBinding (.. ))
3334import 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
327330referencedBlockContext :: 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+
441460declareFrameContext :: [(Builder , [Prop ])] -> Err SMT2
442461declareFrameContext 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+
10251050parseBlockCtx :: TS. Text -> Expr EWord
10261051parseBlockCtx " origin" = Origin
10271052parseBlockCtx " coinbase" = Coinbase
@@ -1031,12 +1056,14 @@ parseBlockCtx "prevrandao" = PrevRandao
10311056parseBlockCtx " gaslimit" = GasLimit
10321057parseBlockCtx " chainid" = ChainId
10331058parseBlockCtx " 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
10361062parseTxCtx :: TS. Text -> Expr EWord
10371063parseTxCtx 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
10421069getAddrs :: (TS. Text -> Expr EAddr ) -> (Text -> IO Text ) -> [TS. Text ] -> IO (Map (Expr EAddr ) Addr )
0 commit comments